diff --git a/assets b/assets index 1e7adc74..10a61165 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 1e7adc749e02144615a20a82c847d3c8df46ee3d +Subproject commit 10a611659ddaadfea6a560e707d395834855a979 diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 546d03f5..9e1a81db 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -78,13 +78,21 @@ bool AABBvsTriangles(const AABB& box, bool& isOnGround, glm::vec3& outResolutionVector); +//Detects collision, but does not resolve. +bool AABBvsTriangles(const AABB& box, + const RawModel::Vertex* modelVertices, + const std::vector& modelIndices, + const glm::mat4& modelMatrix); + //Return true if the boxes are intersecting. bool AABBVsAABB(const AABB& a, const AABB& b); //Return true if the boxes are intersecting. //Also outputs the minimum translation that box [a] would need in order to resolve collision. bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation); -// Calculates an absolute AABB from an entity AABB component +// Calculates an absolute AABB from an entity AABB component or Model component. +// if takeModelBox is true, the AABB component will be ignored and box is calculated from Model. +// if takeModelBox is false, the AABB component will be prefered, if it exists. boost::optional EntityAbsoluteAABB(EntityWrapper& entity, bool takeModelBox = false); boost::optional AbsoluteAABBExplosionEffect(EntityWrapper& entity); //Returns the first entity hit by the input ray. entitiesPotentiallyHitSorted needs to be sorted diff --git a/include/Engine/Collision/CollisionSystem.h b/include/Engine/Collision/CollisionSystem.h index c963e6b8..19adea35 100644 --- a/include/Engine/Collision/CollisionSystem.h +++ b/include/Engine/Collision/CollisionSystem.h @@ -15,15 +15,16 @@ class CollisionSystem : public PureSystem public: CollisionSystem(SystemParams params, Octree* octree) : System(params) - , PureSystem("Collidable") + , PureSystem("Physics") , m_Octree(octree) { } - virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) override; + virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cPhysics, double dt) override; private: Octree* m_Octree; std::vector m_OctreeResult; + std::unordered_map m_PrevPositions; }; #endif \ No newline at end of file diff --git a/include/Engine/Core/ComponentInfo.h b/include/Engine/Core/ComponentInfo.h index d9799059..137a44d8 100644 --- a/include/Engine/Core/ComponentInfo.h +++ b/include/Engine/Core/ComponentInfo.h @@ -2,6 +2,7 @@ #define ComponentInfo_h__ #include "../Common.h" +#include struct ComponentInfo { @@ -27,8 +28,9 @@ struct ComponentInfo std::string Name; std::unordered_map Fields; std::vector FieldsInOrder; + std::vector StringFields; unsigned int Stride = 0; - std::shared_ptr Defaults = nullptr; + boost::shared_array Defaults = nullptr; std::shared_ptr Meta = nullptr; }; diff --git a/include/Engine/Core/ComponentPool.h b/include/Engine/Core/ComponentPool.h index 957b8756..aedfd06b 100644 --- a/include/Engine/Core/ComponentPool.h +++ b/include/Engine/Core/ComponentPool.h @@ -1,6 +1,7 @@ #ifndef ComponentPool_h__ #define ComponentPool_h__ +#include #include "MemoryPool.h" #include "ComponentInfo.h" #include "ComponentWrapper.h" @@ -45,7 +46,8 @@ public: : m_ComponentInfo(ci) , m_Pool(ci.Meta->Allocation, sizeof(EntityID) + ci.Stride) { } - ComponentPool(const ComponentPool& other) = delete; + ~ComponentPool(); + ComponentPool(const ComponentPool& other); ComponentPool(const ComponentPool&& other) = delete; const ::ComponentInfo& ComponentInfo() const { return m_ComponentInfo; } diff --git a/include/Engine/Core/ComponentWrapper.h b/include/Engine/Core/ComponentWrapper.h index 0b9f7357..7897e874 100644 --- a/include/Engine/Core/ComponentWrapper.h +++ b/include/Engine/Core/ComponentWrapper.h @@ -2,11 +2,29 @@ #define ComponentWrapper_h__ #include +#include #include "../Common.h" #include "Entity.h" #include "ComponentInfo.h" #include "Util/Any.h" +template +struct ComponentField { }; + +template +struct ComponentField::value>::type> +{ + static T& Get(const ComponentInfo::Field_t& info, char* data) { return *reinterpret_cast(data); } + static void Set(const ComponentInfo::Field_t& info, char* data, const T& value) { Get(data) = value; } +}; + +template <> +struct ComponentField +{ + static std::string& Get(const ComponentInfo::Field_t& info, char* data) { return **reinterpret_cast(data); } + static void Set(const ComponentInfo::Field_t& info, char* data, const std::string& value) { Get(info, data) = value; } +}; + struct ComponentWrapper { ComponentWrapper(const ComponentInfo& componentInfo, char* data) @@ -47,7 +65,31 @@ struct ComponentWrapper void Copy(ComponentWrapper& destination) { - memcpy(destination.Data, this->Data, Info.Stride); + // Copy trivial data + memcpy(destination.Data, Data, Info.Stride); + // Duplicate strings + SolidifyStrings(destination); + } + + // When component data has been copied, strings need to be reconstructed or they'll refer to the same data! + static void SolidifyStrings(ComponentWrapper& component) + { + for (auto& name : component.Info.StringFields) { + std::size_t offset = component.Info.Fields.at(name).Offset; + std::string value = *reinterpret_cast(component.Data + offset); + new (component.Data + offset) std::string(value); + } + } + + // This needs to be called to properly free component data, because strings. + static void Destroy(ComponentInfo info, char* data) + { + // Call std::string destructors + for (auto& name : info.StringFields) { + std::size_t offset = info.Fields.at(name).Offset; + auto field = reinterpret_cast(data + offset); + field->~basic_string(); + } } struct SubscriptProxy @@ -102,26 +144,38 @@ public: ComponentWrapperFactory(std::string componentTypeName, unsigned int allocation = 0) { m_ComponentInfo.Name = componentTypeName; + m_ComponentInfo.Meta = std::make_shared(); m_ComponentInfo.Meta->Allocation = allocation; } template void AddProperty(std::string fieldName, T defaultValue) { - m_DefaultValues.push_back(defaultValue); - m_ComponentInfo.Fields[fieldName].Type = typeid(T).name(); - m_ComponentInfo.Fields[fieldName].Offset = m_ComponentInfo.Stride; - m_ComponentInfo.Fields[fieldName].Stride = sizeof(T); + auto& field = m_ComponentInfo.Fields[fieldName]; + field.Name = fieldName; + field.Type = typeid(T).name(); + field.Offset = m_ComponentInfo.Stride; + field.Stride = sizeof(T); + m_ComponentInfo.FieldsInOrder.push_back(field.Name); + if (field.Type == typeid(std::string).name()) { + field.Type = "string"; + m_ComponentInfo.StringFields.push_back(field.Name); + } m_ComponentInfo.Stride += sizeof(T); + m_DefaultValues.push_back(std::make_pair(field, defaultValue)); } - + ComponentInfo& Finalize() { - m_ComponentInfo.Defaults = std::shared_ptr(new char[m_ComponentInfo.Stride]); + m_ComponentInfo.Defaults = boost::shared_array(new char[m_ComponentInfo.Stride]); std::size_t offset = 0; - for (auto& val : m_DefaultValues) { - memcpy(m_ComponentInfo.Defaults.get() + offset, val.Data.get(), val.Size); - offset += val.Size; + for (auto& pair : m_DefaultValues) { + if (pair.first.Type == "string") { + new (m_ComponentInfo.Defaults.get() + offset) std::string(*reinterpret_cast(pair.second.Data.get())); + } else { + memcpy(m_ComponentInfo.Defaults.get() + offset, pair.second.Data.get(), pair.second.Size); + } + offset += pair.second.Size; } return m_ComponentInfo; @@ -131,7 +185,7 @@ public: private: ComponentInfo m_ComponentInfo; - std::vector m_DefaultValues; + std::vector> m_DefaultValues; }; #endif diff --git a/include/Engine/Core/EAmmoPickup.h b/include/Engine/Core/EAmmoPickup.h new file mode 100644 index 00000000..6d854d48 --- /dev/null +++ b/include/Engine/Core/EAmmoPickup.h @@ -0,0 +1,18 @@ +#ifndef EAmmoPickup_h__ +#define EAmmoPickup_h__ + +#include "EventBroker.h" +#include "../Core/EntityWrapper.h" + +namespace Events +{ + + struct AmmoPickup : Event + { + EntityWrapper Player; + int AmmoGain; + }; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Core/MemoryPool.h b/include/Engine/Core/MemoryPool.h index f4073294..053e75aa 100644 --- a/include/Engine/Core/MemoryPool.h +++ b/include/Engine/Core/MemoryPool.h @@ -66,9 +66,25 @@ public: , m_LowestAllocatedSlot(m_NumSlots) { } - //We may get problems with memory being released - //prematurely, etc. if we allow copies. - MemoryPool(const MemoryPool& other) = delete; + MemoryPool(const MemoryPool& other) + : m_StartAddress(new char[other.m_NumSlots*other.m_Stride]) + , m_SlotIsAllocated(other.m_SlotIsAllocated) + , m_ExtraMemory() + , m_NumSlots(other.m_NumSlots) + , m_LowestAllocatedSlot(other.m_LowestAllocatedSlot) + , m_NumAllocatedSlots(other.m_NumAllocatedSlots) + , m_Stride(other.m_Stride) + , m_CurrentAllocSlot(other.m_CurrentAllocSlot) + { + // Copy statically allocated pool + memcpy(m_StartAddress, other.m_StartAddress, m_NumSlots*m_Stride); + // Copy dynamically allocated memory + for (char* otherAddr : other.m_ExtraMemory) { + char* addr = (char*)malloc(m_Stride); + memcpy(addr, otherAddr, m_Stride); + m_ExtraMemory.push_back(addr); + } + } MemoryPool(const MemoryPool&& other) = delete; //Free all memory that has been allocated. @@ -78,8 +94,9 @@ public: delete[] m_StartAddress; m_StartAddress = nullptr; } - for (char* addr : m_ExtraMemory) - free(addr); + for (char* addr : m_ExtraMemory) { + free(addr); + } m_ExtraMemory.clear(); } diff --git a/include/Engine/Core/PerformanceTimer.h b/include/Engine/Core/PerformanceTimer.h new file mode 100644 index 00000000..a3cdfa92 --- /dev/null +++ b/include/Engine/Core/PerformanceTimer.h @@ -0,0 +1,25 @@ +#ifndef PerformanceTimer_h__ +#define PerformanceTimer_h__ + +#include "../Common.h" +#include +using boost::timer::cpu_timer; + +class PerformanceTimer +{ +public: + static void StartTimer(std::string nameOfTimer); + static void StartTimerAndStopPrevious(std::string nameOfTimer); + static void StopTimer(std::string nameOfTimer); + static void SetFrameNumber(int frameNumber); + + static void ResetAllTimers(); + static void CreateExcelData(); + +private: + static std::map timers; + static cpu_timer m_Timer; + static std::string currentTimerRunning; +}; + +#endif diff --git a/include/Engine/Core/SystemPipeline.h b/include/Engine/Core/SystemPipeline.h index 5f7aee0b..c4801fde 100644 --- a/include/Engine/Core/SystemPipeline.h +++ b/include/Engine/Core/SystemPipeline.h @@ -6,6 +6,7 @@ #include "System.h" #include "World.h" #include "EPause.h" +#include "PerformanceTimer.h" class SystemPipeline { @@ -72,7 +73,10 @@ public: // Update for (auto& system : group.ImpureSystems) { + auto className = (std::string)typeid(*system).name(); + PerformanceTimer::StartTimer(className); system->Update(dt); + PerformanceTimer::StopTimer(className); } for (auto& pair : group.PureSystems) { const std::string& componentName = pair.first; @@ -83,7 +87,10 @@ public: } for (auto& component : *pool) { for (auto& system : systems) { + auto className = (std::string)typeid(*system).name(); + PerformanceTimer::StartTimer(className); system->UpdateComponent(EntityWrapper(m_World, component.EntityID), component, dt); + PerformanceTimer::StopTimer(className); } } } @@ -106,9 +113,9 @@ private: std::vector m_OrderedSystemGroups; EventRelay m_EPause; - bool OnPause(const Events::Pause& e) { - if (e.World == m_World) { - m_Paused = true; + bool OnPause(const Events::Pause& e) { + if (e.World == m_World) { + m_Paused = true; } return true; } diff --git a/include/Engine/Core/Util/Any.h b/include/Engine/Core/Util/Any.h index f0f90737..e7b93dfb 100644 --- a/include/Engine/Core/Util/Any.h +++ b/include/Engine/Core/Util/Any.h @@ -2,6 +2,7 @@ #define Util_Any_h__ #include +#include struct Any { @@ -10,7 +11,7 @@ struct Any template Any(const T& value) { - Data = std::shared_ptr(new char[sizeof(T)]); + Data = boost::shared_array(new char[sizeof(T)]); Size = sizeof(T); memcpy(Data.get(), &value, Size); } @@ -18,7 +19,7 @@ struct Any template Any(T&& value) { - Data = std::shared_ptr(new char[sizeof(T)]); + Data = boost::shared_array(new char[sizeof(T)]); Size = sizeof(T); memcpy(Data.get(), &value, Size); } @@ -35,7 +36,7 @@ struct Any return Any(value); } - std::shared_ptr Data = nullptr; + boost::shared_array Data = nullptr; std::size_t Size = 0; }; diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index 35394b9f..1604df37 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -15,6 +15,7 @@ public: : m_EventBroker(eventBroker) { } ~World(); + World(const World& other); // Create empty entity EntityID CreateEntity(EntityID parent = 0); diff --git a/include/Engine/GUI/Button.h b/include/Engine/GUI/Button.h deleted file mode 100644 index 80845cb7..00000000 --- a/include/Engine/GUI/Button.h +++ /dev/null @@ -1,165 +0,0 @@ -#ifndef GUI_BUTTON_H__ -#define GUI_BUTTON_H__ - -#include "GUI/TextureFrame.h" -#include "GUI/EButtonEnter.h" -#include "GUI/EButtonLeave.h" -#include "GUI/EButtonPress.h" -#include "GUI/EButtonRelease.h" -#include "Core/EMouseMove.h" -#include "Core/EMousePress.h" -#include "Core/EMouseRelease.h" - -namespace dd -{ -namespace GUI -{ - -class Button : public TextureFrame -{ -public: - Button(Frame* parent, std::string name) - : TextureFrame(parent, name) - { - EVENT_SUBSCRIBE_MEMBER(m_EMouseMove, &Button::OnMouseMove); - EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &Button::OnMousePress); - EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &Button::OnMouseRelease); - } - - void SetTextureHover(std::string resourceName) - { - m_TextureHover = resourceName; - } - void SetTextureReleased(std::string resourceName) - { - m_TextureReleased = resourceName; - SetTexture(resourceName); - } - void SetTexturePressed(std::string resourceName) - { - m_TexturePressed = resourceName; - } - - void Draw(RenderScene& rq) override - { - if (m_Texture == nullptr && !m_TextureReleased.empty()) { - SetTexture(m_TextureReleased); - } - - TextureFrame::Draw(rq); - } - - virtual void OnEnter() { } - virtual void OnLeave() { } - virtual void OnPress() { } - virtual void OnRelease() { } - -protected: - bool m_MouseIsOver = false; - bool m_IsDown = false; - - virtual bool OnMouseMove(const Events::MouseMove& event) - { - if (Hidden()) { - return false; - } - - bool isOver = Rectangle::Intersects(AbsoluteRectangle(), Rectangle(event.X, event.Y, 1, 1)); - if (isOver && !m_MouseIsOver) { // Enter - if (!m_IsDown) { - if (!m_TextureHover.empty()) { - SetTexture(m_TextureHover); - } - } - OnEnter(); - Events::ButtonEnter e; - e.FrameName = m_Name; - EventBroker->Publish(e); - Events::PlaySound soundEvent; - soundEvent.FilePath = "Sounds/GUI/hover-n.wav"; - EventBroker->Publish(soundEvent); - - } else if (!isOver && m_MouseIsOver) { // Leave - if (!m_IsDown) { - if (!m_TextureReleased.empty()) { - SetTexture(m_TextureReleased); - } - } - OnLeave(); - Events::ButtonLeave e; - e.FrameName = m_Name; - EventBroker->Publish(e); - } - m_MouseIsOver = isOver; - - return true; - } - virtual bool OnMousePress(const Events::MousePress& event) - { - if (Hidden()) { - //LOG_DEBUG("Pressed hidden button"); - return false; - } - - if (!Rectangle::Intersects(AbsoluteRectangle(), Rectangle(event.X, event.Y, 1, 1))) { - return false; - } - - if (!m_TexturePressed.empty()) { - SetTexture(m_TexturePressed); - } - - m_IsDown = true; - OnPress(); - Events::ButtonPress e; - e.FrameName = m_Name; - e.Button = this; - EventBroker->Publish(e); - - return true; - } - virtual bool OnMouseRelease(const Events::MouseRelease& event) - { - if (Hidden()) { - //LOG_DEBUG("Released hidden button"); - return false; - } - - bool isOver = Rectangle::Intersects(AbsoluteRectangle(), Rectangle(event.X, event.Y, 1, 1)); - if (!isOver && !m_IsDown) { - return false; - } - - if (m_MouseIsOver) { - if (!m_TextureHover.empty()) { - SetTexture(m_TextureHover); - } - } else { - if (!m_TextureReleased.empty()) { - SetTexture(m_TextureReleased); - } - } - - m_IsDown = false; - OnRelease(); - Events::ButtonRelease e; - e.FrameName = m_Name; - e.Button = this; - EventBroker->Publish(e); - - return true; - } - -private: - EventRelay m_EMouseMove; - EventRelay m_EMousePress; - EventRelay m_EMouseRelease; - - std::string m_TextureHover; - std::string m_TexturePressed; - std::string m_TextureReleased; -}; - -} -} -#endif diff --git a/include/Engine/GUI/ButtonSystem.h b/include/Engine/GUI/ButtonSystem.h new file mode 100644 index 00000000..7dfe6b48 --- /dev/null +++ b/include/Engine/GUI/ButtonSystem.h @@ -0,0 +1,44 @@ +#ifndef ButtonSystem_h__ +#define ButtonSystem_h__ + +#include "../Rendering/IRenderer.h" +#include "../Core/ConfigFile.h" +#include "../Rendering/PickingPass.h" +#include "../Core/ResourceManager.h" +#include "../Core/System.h" +#include "../Core/Event.h" +#include "../Core/EMousePress.h" +#include "../Core/EMouseRelease.h" +#include "../Core/ELockMouse.h" + +#include "EButtonPressed.h" +#include "EButtonReleased.h" +#include "EButtonClicked.h" + + +class ButtonSystem : public PureSystem +{ +public: + ButtonSystem(SystemParams params, IRenderer* renderer); + virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) override; +private: + IRenderer* m_Renderer; + bool m_MouseIsLocked = false; + + EntityWrapper m_PickEntity = EntityWrapper::Invalid; + PickData m_PickData; + + EventRelay m_EMouseLock; + bool OnMouseLock(const Events::LockMouse& e); + EventRelay m_EMouseUnlock; + bool OnMouseUnlock(const Events::UnlockMouse& e); + + EventRelay m_EMousePress; + bool OnMousePress(const Events::MousePress& e); + EventRelay m_EMouseRelease; + bool OnMouseRelease(const Events::MouseRelease& e); +}; + + + +#endif \ No newline at end of file diff --git a/include/Engine/GUI/EButtonClicked.h b/include/Engine/GUI/EButtonClicked.h new file mode 100644 index 00000000..e4fe7abe --- /dev/null +++ b/include/Engine/GUI/EButtonClicked.h @@ -0,0 +1,16 @@ +#ifndef Events_ButtonClicked_h__ +#define Events_ButtonClicked_h__ + +#include "Core/Event.h" + +namespace Events +{ + +struct ButtonClicked : public Event { + std::string EntityName; + EntityWrapper Entity; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/GUI/EButtonEnter.h b/include/Engine/GUI/EButtonEnter.h deleted file mode 100644 index 13a78383..00000000 --- a/include/Engine/GUI/EButtonEnter.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef Events_ButtonEnter_h__ -#define Events_ButtonEnter_h__ - -#include "../Core/EventBroker.h" - -namespace Events -{ - -/** Thrown on GUI button hover. */ -struct ButtonEnter : Event -{ - std::string FrameName; -}; - -} - -#endif \ No newline at end of file diff --git a/include/Engine/GUI/EButtonLeave.h b/include/Engine/GUI/EButtonLeave.h deleted file mode 100644 index 789e5aec..00000000 --- a/include/Engine/GUI/EButtonLeave.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef Events_ButtonLeave_h__ -#define Events_ButtonLeave_h__ - -#include "../Core/EventBroker.h" - -namespace Events -{ - -/** Thrown on GUI button hover. */ -struct ButtonLeave : Event -{ - std::string FrameName; -}; - -} - -#endif \ No newline at end of file diff --git a/include/Engine/GUI/EButtonPress.h b/include/Engine/GUI/EButtonPress.h deleted file mode 100644 index 6f925b2b..00000000 --- a/include/Engine/GUI/EButtonPress.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef Events_ButtonPress_h__ -#define Events_ButtonPress_h__ - -#include "../Core/EventBroker.h" - -namespace GUI { class Button; } - -namespace Events -{ - -/** Thrown on GUI button press. */ -struct ButtonPress : Event -{ - std::string FrameName; - GUI::Button* Button; -}; - -} - -#endif \ No newline at end of file diff --git a/include/Engine/GUI/EButtonPressed.h b/include/Engine/GUI/EButtonPressed.h new file mode 100644 index 00000000..3615ac8a --- /dev/null +++ b/include/Engine/GUI/EButtonPressed.h @@ -0,0 +1,16 @@ +#ifndef Events_ButtonPressed_h__ +#define Events_ButtonPressed_h__ + +#include "Core/Event.h" + +namespace Events +{ + +struct ButtonPressed : public Event { + std::string EntityName; + EntityWrapper Entity; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/GUI/EButtonRelease.h b/include/Engine/GUI/EButtonRelease.h deleted file mode 100644 index d13a8ad6..00000000 --- a/include/Engine/GUI/EButtonRelease.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef Events_ButtonRelease_h__ -#define Events_ButtonRelease_h__ - -#include "../Core/EventBroker.h" - -namespace GUI { class Button; } - -namespace Events -{ - -/** Thrown on GUI button release. */ -struct ButtonRelease : Event -{ - std::string FrameName; - GUI::Button* Button; -}; - -} - -#endif \ No newline at end of file diff --git a/include/Engine/GUI/EButtonReleased.h b/include/Engine/GUI/EButtonReleased.h new file mode 100644 index 00000000..ecd8dde2 --- /dev/null +++ b/include/Engine/GUI/EButtonReleased.h @@ -0,0 +1,16 @@ +#ifndef Events_ButtonReleased_h__ +#define Events_ButtonReleased_h__ + +#include "Core/Event.h" + +namespace Events +{ + +struct ButtonReleased : public Event { + std::string EntityName; + EntityWrapper Entity; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/GUI/Frame.h b/include/Engine/GUI/Frame.h deleted file mode 100644 index 4c5f2eb9..00000000 --- a/include/Engine/GUI/Frame.h +++ /dev/null @@ -1,261 +0,0 @@ -#ifndef GUI_Frame_h__ -#define GUI_Frame_h__ - -#include "../Common.h" -#include "../Core/Util/Rectangle.h" -#include "../Core/EventBroker.h" -#include "../Core/EKeyDown.h" -#include "../Core/EKeyUp.h" -#include "../Core/ResourceManager.h" -#include "../Rendering/RenderQueue.h" -#include "../Rendering/Texture.h" -#include "../Input/EInputCommand.h" - -namespace GUI -{ - -class Frame : public Rectangle -{ -public: - enum class Anchor - { - Left, - Right, - Top, - Bottom - }; - - static const int BaseWidth = 1280; - static const int BaseHeight = 720; - - // Set up a base frame with an event broker - Frame(EventBroker* eventBroker) - : m_EventBroker(eventBroker) - , BaseFrame(this) - , m_Name("UIParent") - , Rectangle() { } - - // Create a frame as a child - Frame(Frame* parent, std::string name) - : m_Name(name) - { - SetParent(parent); - Width = parent->Width; - Height = parent->Height; - EVENT_SUBSCRIBE_MEMBER(m_EKeyDown, &Frame::OnKeyDown); - EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &Frame::OnKeyUp); - EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Frame::OnCommand); - } - - ~Frame() - { - /*for (auto layer : m_Children) - { - for (auto child : layer.second) - { - delete child.second; - } - } - - if (m_Parent) - { - m_Parent->RemoveChild(this); - }*/ - } - - Frame* Parent() const { return m_Parent; } - - void SetParent(Frame* parent) - { - if (parent == nullptr) { - LOG_ERROR("Failed to parent frame \"%s\": Invalid parent", m_Name.c_str()); - return; - } - - m_Layer = parent->Layer() + 1; - parent->AddChild(this); - m_Parent = parent; - m_EventBroker = parent->m_EventBroker; - BaseFrame = parent->BaseFrame; - } - - void AddChild(Frame* child) - { - m_Children[child->m_Layer].insert(std::make_pair(child->Name(), child)); - if (m_Parent) { - m_Parent->AddChild(child); - } - } - - void RemoveChild(Frame* child) - { - auto it = m_Children.find(child->m_Layer); - if (it != m_Children.end()) { - m_Children.erase(it); - } - - if (m_Parent) { - m_Parent->RemoveChild(child); - } - } - - std::string Name() const { return m_Name; } - void SetName(std::string val) { m_Name = val; } - - int Layer() const { return m_Layer; } - - bool Hidden() const - { - if (m_Parent) - return m_Parent->Hidden() || m_Hidden; - else - return m_Hidden; - } - bool Visible() const - { - return !Hidden(); - } - - virtual void Hide() { m_Hidden = true; } - virtual void Show() { m_Hidden = false; } - - int Left() const override - { - if (m_Parent) - return m_Parent->Left() + X; - else - return X; - } - void SetLeft(int absLeft) override - { - if (m_Parent) { - X = absLeft - m_Parent->Left(); - } else { - X = absLeft; - } - } - int Right() const override - { - return Left() + Width; - } - void SetRight(int absRight) override - { - if (m_Parent) { - X = absRight - Width - m_Parent->Left(); - } else { - X = absRight - Width; - } - } - int Top() const override - { - if (m_Parent) - return m_Parent->Top() + Y; - else - return Y; - } - void SetTop(int absTop) override - { - if (m_Parent) { - Y = absTop - m_Parent->Top(); - } else { - Y = absTop; - } - } - int Bottom() const override - { - return Top() + Height; - } - void SetBottom(int absBottom) override - { - if (m_Parent) { - Y = absBottom - Height - m_Parent->Top(); - } else { - Y = absBottom - Height; - } - } - - glm::vec2 Scale() - { - if (m_Parent) - return m_Parent->Scale(); - else - return glm::vec2(Width, Height) / glm::vec2(BaseWidth, BaseHeight); - } - - Rectangle AbsoluteRectangle() - { - int left = Left(); - if (m_Parent) - left = std::max(left, m_Parent->Left()); - int top = Top(); - if (m_Parent) - top = std::max(top, m_Parent->Top()); - int width = Right() - left; - int height = Bottom() - top; - return Rectangle(left, top, width, height); - } - - void UpdateLayered(double dt) - { - // Update ourselves - this->Update(dt); - - // Update children - for (auto& pairLayer : m_Children) { - auto children = pairLayer.second; - for (auto& pairChild : children) { - auto child = pairChild.second; - child->Update(dt); - } - } - } - - virtual void Update(double dt) { } - - void DrawLayered(RenderScene& rq) - { - if (this->Hidden()) - return; - - // Draw ourselves - this->Draw(rq); - - // Draw children - for (auto& pairLayer : m_Children) { - auto children = pairLayer.second; - for (auto& pairChild : children) { - auto child = pairChild.second; - if (child->Hidden()) - continue; - child->Draw(rq); - } - } - } - - virtual void Draw(RenderScene& rq) { } - -protected: - ::EventBroker* m_EventBroker; - Frame* BaseFrame = nullptr; - - std::string m_Name = "Unnamed"; - int m_Layer = 0; - bool m_Hidden = false; - - Frame* m_Parent = nullptr; - typedef std::multimap Children_t; // name -> frame - std::map m_Children; // layer -> Children_t - - virtual bool OnKeyDown(const Events::KeyDown& event) { return false; } - virtual bool OnKeyUp(const Events::KeyUp& event) { return false; } - virtual bool OnCommand(const Events::InputCommand& event) { return false; } - -private: - EventRelay m_EKeyDown; - EventRelay m_EKeyUp; - EventRelay m_EInputCommand; -}; - -} - -#endif diff --git a/include/Engine/GUI/MainMenuSystem.h b/include/Engine/GUI/MainMenuSystem.h new file mode 100644 index 00000000..ba69ff0d --- /dev/null +++ b/include/Engine/GUI/MainMenuSystem.h @@ -0,0 +1,33 @@ +#ifndef MainMenuSystem_h__ +#define MainMenuSystem_h__ + +#include "../Core/System.h" +#include "../Rendering/IRenderer.h" +#include "../Core/ResourceManager.h" +#include "../Core/Event.h" + + +#include "EButtonClicked.h" +#include "EButtonPressed.h" +#include "EButtonReleased.h" + + +class MainMenuSystem : public ImpureSystem +{ +public: + MainMenuSystem(SystemParams params, IRenderer* renderer); + virtual void Update(double dt) override; + +private: + IRenderer* m_Renderer; + + EventRelay m_EClicked; + bool OnButtonClick(const Events::ButtonClicked& e); + EventRelay m_EReleased; + bool OnButtonRelease(const Events::ButtonReleased& e); + EventRelay m_EPressed; + bool OnButtonPress(const Events::ButtonPressed& e); + +}; + +#endif \ No newline at end of file diff --git a/include/Engine/GUI/TextureFrame.h b/include/Engine/GUI/TextureFrame.h deleted file mode 100644 index 77967b4d..00000000 --- a/include/Engine/GUI/TextureFrame.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef GUI_TextureFrame_h__ -#define GUI_TextureFrame_h__ - -#include "Frame.h" -#include "../Rendering/Texture.h" -#include "../Rendering/Util/CommonFunctions.h" - -namespace GUI -{ - -class TextureFrame : public Frame -{ -public: - TextureFrame(Frame* parent, std::string name) - : Frame(parent, name) { } - - void EnableScissor() { m_ScissorEnabled = true; } - void DisableScissor() { m_ScissorEnabled = false; } - - void Draw(RenderScene& rq) override - { - if (m_Texture == nullptr) - return; - - // Texture while fading - if (m_FadeTexture && m_CurrentFade < 1) { - FrameJob job; - job.Scissor = (m_ScissorEnabled) ? m_Parent->AbsoluteRectangle() : Rectangle(); - job.Viewport = Rectangle(Left(), Top(), Width, Height); - job.TextureID = m_FadeTexture->ResourceID; - job.DiffuseTexture = m_FadeTexture; - job.Color = glm::vec4(m_Color.r, m_Color.g, m_Color.b, m_Color.a); - job.Name = Name(); - rq.GUI.Add(job); - } - - // Main texture - { - FrameJob job; - job.Scissor = (m_ScissorEnabled) ? m_Parent->AbsoluteRectangle() : Rectangle(); - job.Viewport = Rectangle(Left(), Top(), Width, Height); - job.TextureID = m_Texture->ResourceID; - job.DiffuseTexture = m_Texture; - job.Color = glm::vec4(m_Color.r, m_Color.g, m_Color.b, m_Color.a * m_CurrentFade); - job.Name = Name(); - rq.GUI.Add(job); - } - } - - std::string Texture() const { return m_TextureName; } - - void SetTexture(std::string resourceName) - { - if (resourceName.empty()) { - m_Texture = nullptr; - return; - } - - m_Texture = CommonFunctions::LoadTexture(resourceName, false); - m_TextureName = resourceName; - if (m_Texture == nullptr) { - m_Texture = CommonFunctions::LoadTexture("Textures/Core/ErrorTexture.png", false); - } - - SizeToTexture(); - } - - void SizeToTexture() - { - if (m_Texture != nullptr) { - this->Width = m_Texture->Width; - this->Height = m_Texture->Height; - } - } - - void FadeToTexture(std::string resourceName, double duration) - { - m_FadeTexture = m_Texture; - SetTexture(resourceName); - m_FadeDuration = duration; - m_CurrentFade = 0.f; - } - - void Update(double dt) override - { - if (m_CurrentFade < 1) { - m_CurrentFade += dt / m_FadeDuration; - if (m_CurrentFade > 1) { - m_FadeTexture = nullptr; - m_CurrentFade = 1; - m_FadeDuration = 0; - } - } - } - - glm::vec4 Color() const { return m_Color; } - void SetColor(glm::vec4 val) { m_Color = val; } - -protected: - bool m_ScissorEnabled = true; - Texture* m_Texture = nullptr; - std::string m_TextureName; - Texture* m_FadeTexture = nullptr; - glm::vec4 m_Color = glm::vec4(1.f, 1.f, 1.f, 1.f); - float m_FadeDuration = 0.f; - float m_CurrentFade = 1.f; - -}; - -} - -#endif diff --git a/include/Engine/Input/FirstPersonInputController.h b/include/Engine/Input/FirstPersonInputController.h index 5529185a..7dc24a2c 100644 --- a/include/Engine/Input/FirstPersonInputController.h +++ b/include/Engine/Input/FirstPersonInputController.h @@ -202,7 +202,7 @@ void FirstPersonInputController::AssaultDashCheck(double dt, bool } //dashing with shift - if (m_ShiftDashing && m_AssaultDashCoolDownTimer <= 0.0f && !isJumping) { + if (m_ShiftDashing && m_AssaultDashCoolDownTimer <= 0.0f) { //player is dashing with shift //the wanted-direction is set in playermovement already so we dont need to check what direction we want to dash in! m_AssaultDashCoolDownTimer = assaultDashCoolDownMaxTimer; @@ -227,7 +227,7 @@ void FirstPersonInputController::AssaultDashCheck(double dt, bool } m_ValidDoubleTap = false; - if (!(m_AssaultDashCoolDownTimer <= 0.0f && !isJumping)) { + if (!(m_AssaultDashCoolDownTimer <= 0.0f)) { //if we cant dash at the moment, then just reset the tap-sensitivity-timer m_AssaultDashDoubleTapDeltaTime = 0.f; return; diff --git a/include/Engine/Network/Client.h b/include/Engine/Network/Client.h index fb367874..968c4bba 100644 --- a/include/Engine/Network/Client.h +++ b/include/Engine/Network/Client.h @@ -13,15 +13,31 @@ #include "Network/Network.h" #include "Network/MessageType.h" #include "Network/PlayerDefinition.h" +#include "Network/UDPClient.h" +#include "Network/TCPClient.h" #include "Network/SnapshotDefinitions.h" #include "Core/World.h" #include "Core/EventBroker.h" #include "Core/ConfigFile.h" #include "Input/EInputCommand.h" #include "Core/EPlayerDamage.h" +#include "../Game/Events/EDoubleJump.h" #include "Network/EInterpolate.h" #include "Network/SnapshotFilter.h" #include "Core/EPlayerSpawned.h" +#include "Network/ESearchForServers.h" + +struct ServerInfo +{ + ServerInfo(std::string a, int b, std::string c, int d) + { + Address = a; Port = b; Name = c; PlayersConnected = d; + } + std::string Address = ""; + int Port = 0; + std::string Name = ""; + int PlayersConnected = 0; +}; class Client : public Network { @@ -32,18 +48,18 @@ public: void Connect(std::string address, int port); void Update() override; - private: + UDPClient m_Unreliable; + TCPClient m_Reliable; + std::vector m_PlayerSpawnEvents; + void parseSpawnEvents(); + // Save for children std::unique_ptr m_SnapshotFilter = nullptr; - // Assio UDP logic - boost::asio::ip::udp::endpoint m_ReceiverEndpoint; - boost::asio::io_service m_IOService; - boost::asio::ip::udp::socket m_Socket; - + std::string m_Address; + int m_Port = 0; // Sending message to server logic size_t bytesRead = 0; - char readBuf[INPUTSIZE] = { 0 }; // Packet loss logic PacketID m_PacketID = 0; @@ -72,30 +88,32 @@ private: std::vector m_InputCommandBuffer; // Private member functions - void readFromServer(); size_t receive(char* data); - void send(Packet& packet); - void connect(); void disconnect(); void parseMessageType(Packet& packet); void updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID); SharedComponentWrapper createSharedComponent(Packet& packet, EntityID entityID, const ComponentInfo& componentInfo); void ignoreFields(Packet& packet, const ComponentInfo& componentInfo); - void parseConnect(Packet& packet); + void parseUDPConnect(Packet& packet); + void parseTCPConnect(Packet& packet); void parsePlayerConnected(Packet& packet); void parsePing(); + void parseServerlist(Packet& packet); void parseKick(); void parsePlayersSpawned(Packet& packet); void parseEntityDeletion(Packet& packet); + void parsePlayerDamage(Packet& packet); void parseComponentDeletion(Packet& packet); + void parseDoubleJump(Packet& packet); void InterpolateFields(Packet & packet, const ComponentInfo & componentInfo, const EntityID & entityID, const std::string & componentType); void parseSnapshot(Packet& packet); void identifyPacketLoss(); - bool hasServerTimedOut(); + void hasServerTimedOut(); EntityID createPlayer(); void sendInputCommands(); void sendLocalPlayerTransform(); void becomePlayer(); + void displayServerlist(); // Mapping Logic // Returns if local EntityID exist in map bool clientServerMapsHasEntity(EntityID clientEntityID); @@ -111,6 +129,15 @@ private: bool OnPlayerDamage(const Events::PlayerDamage& e); EventRelay m_EPlayerSpawned; bool OnPlayerSpawned(const Events::PlayerSpawned& e); + EventRelay< Client, Events::SearchForServers> m_ESearchForServers; + EventRelay m_EDoubleJump; + bool OnDoubleJump(Events::DoubleJump & e); + bool OnSearchForServers(const Events::SearchForServers& e); + UDPClient m_ServerlistRequest; + std::vector m_Serverlist; + bool m_SearchingForServers = false; + std::clock_t m_StartSearchTime; + double m_SearchingTime = 2000; // Config I guess }; #endif diff --git a/include/Engine/Network/ESearchForServers.h b/include/Engine/Network/ESearchForServers.h new file mode 100644 index 00000000..1b08a8f3 --- /dev/null +++ b/include/Engine/Network/ESearchForServers.h @@ -0,0 +1,12 @@ +#ifndef Events_SearchForServers_h__ +#define Events_SearchForServers_h__ + +#include "Core/Event.h" + +namespace Events +{ + +struct SearchForServers : public Event { }; + +} +#endif diff --git a/include/Engine/Network/MessageType.h b/include/Engine/Network/MessageType.h index 85f22649..00a2a91f 100644 --- a/include/Engine/Network/MessageType.h +++ b/include/Engine/Network/MessageType.h @@ -18,7 +18,10 @@ enum class MessageType OnPlayerSpawned, EntityDeleted, ComponentDeleted, - PlayerTransform + PlayerTransform, + OnDoubleJump, + ServerlistRequest, + Invalid }; #endif diff --git a/include/Engine/Network/Network.h b/include/Engine/Network/Network.h index 0dbc4915..b4de053e 100644 --- a/include/Engine/Network/Network.h +++ b/include/Engine/Network/Network.h @@ -12,7 +12,7 @@ #include #include -#define INPUTSIZE 32000 +#define BUFFERSIZE 32000 typedef unsigned int PlayerID; typedef unsigned int PacketID; @@ -35,6 +35,8 @@ protected: std::clock_t m_SaveDataTimer; unsigned int m_MaxConnections; double m_TimeoutMs; + void logSentData(int bytesSent); + void logReceivedData(int bytesReceived); void saveToFile(); void updateNetworkData(); }; diff --git a/include/Engine/Network/NetworkClient.h b/include/Engine/Network/NetworkClient.h new file mode 100644 index 00000000..4adc68f5 --- /dev/null +++ b/include/Engine/Network/NetworkClient.h @@ -0,0 +1,24 @@ +#ifndef NetworkClient_h__ +#define NetworkClient_h__ + +#include "Network/Packet.h" +#define BUFFERSIZE 64000 +typedef unsigned int PlayerID; +typedef unsigned int PacketID; + +class NetworkClient +{ +public: + NetworkClient(); + virtual ~NetworkClient(); + virtual void Connect(std::string playerName, std::string address, int port) = 0; + virtual void Disconnect() = 0; + virtual void Receive(Packet& packet) = 0; + virtual void Send(Packet & packet) = 0; + virtual bool IsSocketAvailable() = 0; +protected: + char* m_ReadBuffer; + unsigned int m_BufferSize = BUFFERSIZE; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/NetworkServer.h b/include/Engine/Network/NetworkServer.h new file mode 100644 index 00000000..296c8762 --- /dev/null +++ b/include/Engine/Network/NetworkServer.h @@ -0,0 +1,24 @@ +#ifndef NetworkServer_h__ +#define NetworkServer_h__ +#include +#include "Network/Packet.h" +#include "Network/PlayerDefinition.h" +#define BUFFERSIZE 64000 +typedef unsigned int PlayerID; +typedef unsigned int PacketID; + +class NetworkServer +{ +public: + NetworkServer(); + virtual ~NetworkServer(); + virtual void AcceptNewConnections(int& nextPlayerID, std::map& connectedPlayers) = 0; + virtual void Receive(Packet & packet, PlayerDefinition & playerDefinition) = 0; + virtual void Send(Packet & packet, PlayerDefinition & playerDefinition) = 0; + virtual void Send(Packet & packet) = 0; +protected: + char* m_ReadBuffer; + unsigned int m_BufferSize = BUFFERSIZE; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/Packet.h b/include/Engine/Network/Packet.h index 009d8563..b688b8c6 100644 --- a/include/Engine/Network/Packet.h +++ b/include/Engine/Network/Packet.h @@ -49,10 +49,15 @@ public: void WriteData(char* data, int sizeOfData); // Pops the first element as if it was a string. std::string ReadString(); + // Construct a packet + void ReconstructFromData(char* data, size_t SizeOfData); + // Update size of packet variable in header + void UpdateSize(); char* ReadData(int SizeOfData); void ChangePacketID(unsigned int& packetID); size_t Size() { return m_Offset; }; char* Data() { return m_Data; }; + MessageType GetMessageType(); size_t DataReadSize() { return m_ReturnDataOffset; } size_t MaxSize() { return m_MaxPacketSize; } size_t HeaderSize() { return m_HeaderSize; } @@ -64,6 +69,7 @@ private: size_t m_MaxPacketSize = 512; size_t m_HeaderSize = 0; void resizeData(); + void resizeData(int size); }; #endif \ No newline at end of file diff --git a/include/Engine/Network/PlayerDefinition.h b/include/Engine/Network/PlayerDefinition.h index 863948b3..afd5d889 100644 --- a/include/Engine/Network/PlayerDefinition.h +++ b/include/Engine/Network/PlayerDefinition.h @@ -2,6 +2,7 @@ #define PlayerDefinition_h__ #include #include "../Core/Entity.h" +#include struct PlayerDefinition { ::EntityID EntityID = EntityID_Invalid; @@ -9,6 +10,10 @@ struct PlayerDefinition { boost::asio::ip::udp::endpoint Endpoint; unsigned int PacketID; std::clock_t StopTime; + boost::asio::ip::address TCPAddress; + unsigned short TCPPort; + // use for tcp connections + boost::shared_ptr TCPSocket; }; #endif diff --git a/include/Engine/Network/Server.h b/include/Engine/Network/Server.h index f16c6c16..aac4e4c8 100644 --- a/include/Engine/Network/Server.h +++ b/include/Engine/Network/Server.h @@ -5,8 +5,9 @@ #include #include -#include +#include "Network/TCPServer.h" +#include "Network/UDPServer.h" #include "Network/MessageType.h" #include "Network/PlayerDefinition.h" #include "Core/World.h" @@ -16,6 +17,7 @@ #include "Core/EPlayerDamage.h" #include "Network/EPlayerDisconnected.h" #include "Core/EPlayerSpawned.h" +#include "../Game/Events/EDoubleJump.h" #include "Core/EEntityDeleted.h" #include "Core/EComponentDeleted.h" @@ -28,57 +30,64 @@ public: void Update() override; private: + // Network channels + TCPServer m_Reliable; + UDPServer m_Unreliable; + UDPServer m_ServerlistRequest; + // dont forget to set these in the childrens receive logic + boost::asio::ip::address m_Address; int m_Port = 27666; - // UDP logic - boost::asio::ip::udp::endpoint m_ReceiverEndpoint; - boost::asio::io_service m_IOService; - std::unique_ptr m_Socket; - // Sending messages to client logic std::map m_ConnectedPlayers; + std::vector m_PlayersToDisconnect; // HACK: Fix INPUTSIZE - char readBuffer[INPUTSIZE] = { 0 }; + char readBuffer[BUFFERSIZE] = { 0 }; size_t bytesRead = 0; // time for previouse message std::clock_t previousePingMessage = std::clock(); std::clock_t previousSnapshotMessage = std::clock(); std::clock_t timOutTimer = std::clock(); + // How often we send messages (milliseconds) float pingIntervalMs; float snapshotInterval; int checkTimeOutInterval = 100; int m_NextPlayerID = 0; std::vector m_InputCommandsToBroadcast; - //Timers std::clock_t m_StartPingTime; - + // Packet loss logic PacketID m_PacketID = 0; PacketID m_PreviousPacketID = 0; // Private member functions - size_t receive(char* data); - void readFromClients(); - void send(PlayerID player, Packet& packet); - void send(Packet& packet); - void broadcast(Packet& packet); + //int receive(char* data); + void reliableBroadcast(Packet& packet); + void unreliableBroadcast(Packet& packet); void sendSnapshot(); + void addPlayersToPacket(Packet& packet, EntityID entityID); void addChildrenToPacket(Packet& packet, EntityID entityID); void addInputCommandsToPacket(Packet& packet); void sendPing(); void checkForTimeOuts(); void disconnect(PlayerID playerID); void parseMessageType(Packet& packet); - void parseOnInputCommand(Packet& packet); void parseOnPlayerDamage(Packet& packet); - void parseConnect(Packet& packet); - void parseDisconnect(); - void parseClientPing(); - void parsePing(); void identifyPacketLoss(); void kick(PlayerID player); - PlayerID GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint); + PlayerID GetPlayerIDFromEndpoint(); + void parsePlayerTransform(Packet& packet); + void parseOnInputCommand(Packet& packet); + void parseClientPing(); + void parsePing(); + bool parseDoubleJump(Packet& packet); + void parseUDPConnect(Packet& packet); + void parseTCPConnect(Packet& packet); + void parseDisconnect(); + void parseServerlistRequest(boost::asio::ip::udp::endpoint endpoint); + bool shouldSendToClient(EntityWrapper childEntity); + // Debug event EventRelay m_EInputCommand; bool OnInputCommand(const Events::InputCommand& e); @@ -88,7 +97,8 @@ private: bool OnEntityDeleted(const Events::EntityDeleted& e); EventRelay m_EComponentDeleted; bool OnComponentDeleted(const Events::ComponentDeleted& e); - void parsePlayerTransform(Packet& packet); + EventRelay m_EPlayerDamage; + bool OnPlayerDamage(const Events::PlayerDamage& e); }; #endif diff --git a/include/Engine/Network/TCPClient.h b/include/Engine/Network/TCPClient.h new file mode 100644 index 00000000..2108fa3d --- /dev/null +++ b/include/Engine/Network/TCPClient.h @@ -0,0 +1,28 @@ +#ifndef TCPClient_h__ +#define TCPClient_h__ + +#include +#include "NetworkClient.h" + +class TCPClient : public NetworkClient +{ +public: + TCPClient(); + ~TCPClient(); + + void Connect(std::string playerName, std::string address, int port); + void Disconnect(); + void Receive(Packet& packet); + void Send(Packet & packet); + bool IsSocketAvailable(); +private: + // Assio TCP logic + boost::asio::ip::tcp::endpoint m_Endpoint; + boost::asio::io_service m_IOService; + std::unique_ptr m_Socket; + size_t readBuffer(); + PacketID m_SendPacketID = 0; + bool m_IsConnected = false; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/TCPServer.h b/include/Engine/Network/TCPServer.h new file mode 100644 index 00000000..55631a22 --- /dev/null +++ b/include/Engine/Network/TCPServer.h @@ -0,0 +1,37 @@ +#ifndef TCPServer_h__ +#define TCPServer_h__ + +#include +#include +#include +#include "NetworkServer.h" + +class TCPServer : public NetworkServer +{ +public: + TCPServer(); + ~TCPServer(); + void AcceptNewConnections(int& nextPlayerID, std::map& connectedPlayers); + void Receive(Packet & packet, PlayerDefinition & playerDefinition); + void Send(Packet & packet, PlayerDefinition & playerDefinition); + void Send(Packet & packet); + void Disconnect(); + int Port() { return m_Port; } + std::string Address() { return m_Address; } + +private: + // TCP logic + boost::asio::io_service m_IOService; + std::unique_ptr acceptor; + boost::shared_ptr lastReceivedSocket; + + int readBuffer(PlayerDefinition& playerDefinition); + PlayerID getPlayerIDFromEndpoint(const std::map& connectedPlayers, + boost::asio::ip::address address, unsigned short port); + int GetPort(); + std::string GetAddress(); + int m_Port = 0; + std::string m_Address = ""; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/UDPClient.h b/include/Engine/Network/UDPClient.h new file mode 100644 index 00000000..42c27783 --- /dev/null +++ b/include/Engine/Network/UDPClient.h @@ -0,0 +1,28 @@ +#ifndef UDPClient_h__ +#define UDPClient_h__ + +#include +#include "Network/NetworkClient.h" + +class UDPClient : public NetworkClient +{ +public: + UDPClient(); + ~UDPClient(); + + void Connect(std::string playerName, std::string address, int port); + void Disconnect(); + void Receive(Packet& packet); + void Send(Packet & packet); + void Broadcast(Packet& packet, int port); + bool IsSocketAvailable(); +private: + // Assio UDP logic + boost::asio::io_service m_IOService; + boost::asio::ip::udp::endpoint m_ReceiverEndpoint; + boost::shared_ptr m_Socket; + int readBuffer(); + PacketID m_SendPacketID = 0; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Network/UDPServer.h b/include/Engine/Network/UDPServer.h new file mode 100644 index 00000000..54f4e327 --- /dev/null +++ b/include/Engine/Network/UDPServer.h @@ -0,0 +1,28 @@ +#ifndef UDPServer_h__ +#define UDPServer_h__ + +#include "NetworkServer.h" +#include + +class UDPServer : public NetworkServer +{ +public: + UDPServer(); + UDPServer(int port); + ~UDPServer(); + void AcceptNewConnections(int& nextPlayerID, std::map& connectedPlayers); + void Receive(Packet & packet, PlayerDefinition & playerDefinition); + void Send(Packet & packet, PlayerDefinition & playerDefinition); + void Send(Packet & packet); + void Send(Packet & packet, boost::asio::ip::udp::endpoint endpoint); + void Broadcast(Packet & packet, int port); + bool IsSocketAvailable(); +private: + // UDP logic + boost::asio::io_service m_IOService; + boost::asio::ip::udp::endpoint m_ReceiverEndpoint; + std::unique_ptr m_Socket; + int readBuffer(); +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/CubeMapPass.h b/include/Engine/Rendering/CubeMapPass.h new file mode 100644 index 00000000..3cda8cad --- /dev/null +++ b/include/Engine/Rendering/CubeMapPass.h @@ -0,0 +1,27 @@ +#ifndef CubeMapPass_h__ +#define CubeMapPass_h__ + +#include "IRenderer.h" +#include "ShaderProgram.h" + +class CubeMapPass +{ +public: + CubeMapPass(IRenderer* renderer); + ~CubeMapPass() { } + + void LoadTextures(std::string input); + void FillCubeMap(glm::vec3 originPosition); + void GenerateCubeMapTexture(); + + //GLuint CubeMapTexture() const { return m_CubeMapTexture; } + GLuint m_CubeMapTexture = -1; + +private: + IRenderer* m_Renderer; + std::string m_PreviusCubeMapTexture; + + std::vector m_CubeMapTextures; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/DrawBloomPass.h b/include/Engine/Rendering/DrawBloomPass.h index 539d6957..07c90e23 100644 --- a/include/Engine/Rendering/DrawBloomPass.h +++ b/include/Engine/Rendering/DrawBloomPass.h @@ -24,6 +24,8 @@ public: void Draw(GLuint texture); + void OnWindowResize(); + //Getters //Return the blurred result of the texture that was sent into draw GLuint GaussianTexture() const { return m_GaussianTexture_vert; } diff --git a/include/Engine/Rendering/DrawFinalPass.h b/include/Engine/Rendering/DrawFinalPass.h index bf8d4d76..1800c90e 100644 --- a/include/Engine/Rendering/DrawFinalPass.h +++ b/include/Engine/Rendering/DrawFinalPass.h @@ -4,6 +4,7 @@ #include "IRenderer.h" #include "DrawFinalPassState.h" #include "LightCullingPass.h" +#include "CubeMapPass.h" #include "FrameBuffer.h" #include "ShaderProgram.h" #include "Util/UnorderedMapVec2.h" @@ -13,13 +14,14 @@ class DrawFinalPass { public: - DrawFinalPass(IRenderer* renderer, LightCullingPass* lightCullingPass); + DrawFinalPass(IRenderer* renderer, LightCullingPass* lightCullingPass, CubeMapPass* cubeMapPass); ~DrawFinalPass() { } void InitializeTextures(); void InitializeFrameBuffers(); void InitializeShaderPrograms(); - void Draw(RenderScene& scene); + void Draw(RenderScene& scene, GLuint SSAOTexture); void ClearBuffer(); + void OnWindowResize(); //Return the texture that is used in later stages to apply the bloom effect GLuint BloomTexture() const { return m_BloomTexture; } @@ -31,13 +33,12 @@ public: FrameBuffer* FinalPassFrameBuffer() { return &m_FinalPassFrameBuffer; } FrameBuffer* FinalPassFrameBufferLowRes() { return &m_FinalPassFrameBufferLowRes; } - private: void GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const; void GenerateMipMapTexture(GLuint* texture, GLenum wrapping, glm::vec2 dimensions, GLint format, GLenum type, GLint numMipMaps) const; void DrawSprites(std::list>&jobs, RenderScene& scene); - void DrawModelRenderQueues(std::list>& jobs, RenderScene& scene); + void DrawModelRenderQueues(std::list>& jobs, RenderScene& scene, GLuint SSAOTexture); void DrawShieldToStencilBuffer(std::list>& jobs, RenderScene& scene); void DrawShieldedModelRenderQueue(std::list>& jobs, RenderScene& scene); void DrawToDepthBuffer(std::list>& jobs, RenderScene& scene); @@ -62,12 +63,14 @@ private: GLuint m_SceneTextureLowRes; GLuint m_DepthBuffer; GLuint m_DepthBufferLowRes; + GLuint m_CubeMapTexture; //maqke this component based i guess? GLuint m_ShieldPixelRate = 16; const IRenderer* m_Renderer; const LightCullingPass* m_LightCullingPass; + const CubeMapPass* m_CubeMapPass; ShaderProgram* m_ForwardPlusProgram; ShaderProgram* m_ExplosionEffectProgram; diff --git a/include/Engine/Rendering/LightCullingPass.h b/include/Engine/Rendering/LightCullingPass.h index d8852df0..914fb8bb 100644 --- a/include/Engine/Rendering/LightCullingPass.h +++ b/include/Engine/Rendering/LightCullingPass.h @@ -21,6 +21,7 @@ public: void SetSSBOSizes(); void CullLights(RenderScene& scene); void FillLightList(RenderScene& scene); + void OnWindowResize(); GLuint FrustumSSBO() const { return m_FrustumSSBO; } GLuint LightSSBO() const { return m_LightSSBO; } diff --git a/include/Engine/Rendering/ModelJob.h b/include/Engine/Rendering/ModelJob.h index adc21c6f..e905da07 100644 --- a/include/Engine/Rendering/ModelJob.h +++ b/include/Engine/Rendering/ModelJob.h @@ -109,6 +109,7 @@ struct ModelJob : RenderJob EndIndex = matGroup->EndIndex; Matrix = matrix; Color = modelComponent["Color"]; + GlowIntensity = ((double)modelComponent["GlowIntensity"]); Entity = modelComponent.EntityID; glm::vec3 abspos = Transform::AbsolutePosition(world, modelComponent.EntityID); glm::vec3 worldpos = glm::vec3(camera->ViewMatrix() * glm::vec4(abspos, 1)); @@ -150,7 +151,7 @@ struct ModelJob : RenderJob const ::Model* Model = nullptr; ::Skeleton* Skeleton = nullptr; std::shared_ptr<::BlendTree> BlendTree = nullptr; - + float GlowIntensity = 8.0; glm::vec4 DiffuseColor; glm::vec4 SpecularColor; glm::vec4 IncandescenceColor; @@ -163,7 +164,7 @@ struct ModelJob : RenderJob void CalculateHash() override { - Hash = TextureID + ModelID << 10 + ShaderID << 20; + Hash = ShaderID << 20 + ModelID << 10 + TextureID; } }; diff --git a/include/Engine/Rendering/PickingPass.h b/include/Engine/Rendering/PickingPass.h index 2ce2e78d..f6434781 100644 --- a/include/Engine/Rendering/PickingPass.h +++ b/include/Engine/Rendering/PickingPass.h @@ -22,6 +22,8 @@ public: void Draw(RenderScene& scene); void ClearPicking(); + void OnWindowResize(); + //Getters const ShaderProgram& PickingProgram() const { return *m_PickingProgram; } //const std::unordered_map& PickingColorsToEntity() const { return m_PickingColorsToEntity; } @@ -40,7 +42,7 @@ private: const IRenderer* m_Renderer; ShaderProgram* m_PickingProgram; - ShaderProgram* m_PickingSkinnedProgram; + ShaderProgram* m_PickingSkinnedProgram; Camera* m_Camera; struct PickingInfo diff --git a/include/Engine/Rendering/Renderer.h b/include/Engine/Rendering/Renderer.h index 04754514..f3a6bf31 100644 --- a/include/Engine/Rendering/Renderer.h +++ b/include/Engine/Rendering/Renderer.h @@ -16,6 +16,8 @@ #include "DrawScreenQuadPass.h" #include "DrawBloomPass.h" #include "DrawColorCorrectionPass.h" +#include "SSAOPass.h" +#include "CubeMapPass.h" #include "../Core/EventBroker.h" #include "ImGuiRenderPass.h" #include "Camera.h" @@ -23,9 +25,12 @@ #include "imgui/imgui.h" #include "TextPass.h" #include "Util/CommonFunctions.h" +#include "Core/PerformanceTimer.h" class Renderer : public IRenderer { + static void glfwFrameBufferCallback(GLFWwindow* window, int width, int height); + public: Renderer(EventBroker* eventBroker) : m_EventBroker(eventBroker) @@ -37,8 +42,12 @@ public: virtual PickData Pick(glm::vec2 screenCoord) override; + private: //----------------------Variables----------------------// + + static std::unordered_map m_WindowToRenderer; + EventBroker* m_EventBroker; TextPass* m_TextPass; @@ -50,6 +59,14 @@ private: Model* m_UnitSphere; int m_DebugTextureToDraw = 0; + int m_CubeMapTexture = 0; + bool m_ResizeWindow = false; + float m_SSAO_Radius = 1.0f; + float m_SSAO_Bias = 0.05f; + float m_SSAO_Contrast = 1.5f; + float m_SSAO_IntensityScale = 1.0f; + int m_SSAO_NumOfSamples = 24; + int m_SSAO_NumOfTurns = 7; PickingPass* m_PickingPass; LightCullingPass* m_LightCullingPass; @@ -58,6 +75,8 @@ private: DrawScreenQuadPass* m_DrawScreenQuadPass; DrawBloomPass* m_DrawBloomPass; DrawColorCorrectionPass* m_DrawColorCorrectionPass; + SSAOPass* m_SSAOPass; + CubeMapPass* m_CubeMapPass; //----------------------Functions----------------------// void InitializeWindow(); diff --git a/include/Engine/Rendering/SSAOPass.h b/include/Engine/Rendering/SSAOPass.h new file mode 100644 index 00000000..792d1d82 --- /dev/null +++ b/include/Engine/Rendering/SSAOPass.h @@ -0,0 +1,63 @@ +#ifndef SSAOPass_h__ +#define SSAOPass_h__ + +#include "IRenderer.h" +#include "SSAOPassState.h" +//#include "LightCullingPass.h" Finalpass om den skall skickas in +#include "FrameBuffer.h" +#include "ShaderProgram.h" +#include "DrawBloomPass.h" +//#include "Util/UnorderedMapVec2.h" +#include "Texture.h" + +class SSAOPass +{ +public: + SSAOPass(IRenderer* rendere); + ~SSAOPass() { + delete m_DrawBloomPass; + }; + + void Draw(GLuint depthBuffer, Camera* camera); + void Setting(float radius, float bias, float contrast, float intensityScale, int numOfSamples, int NumOfTurns); + void ClearBuffer(); + void OnWindowResize(); + + //Return the SSAO of the texture sent to Draw + GLuint SSAOTexture() const { return m_DrawBloomPass->GaussianTexture(); } + +private: + void InitializeTexture(); + void InitializeFrameBuffer(); + void InitializeShaderProgram(); + void InitializeBuffer(); + + void GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const; + + //void blurHorizontal(GLuint depthBuffer); + //void blurVertical(GLuint depthBuffer); + + Model* m_ScreenQuad; + + const IRenderer* m_Renderer; + + float m_Radius; + float m_Bias; + float m_Contrast; + float m_IntensityScale; + int m_NumOfSamples; + int m_NumOfTurns; + + GLuint m_SSAOTexture; + FrameBuffer m_SSAOFramBuffer; + + GLuint m_SSAOViewSpaceZTexture; + FrameBuffer m_SSAOViewSpaceZFramBuffer; + + ShaderProgram* m_SSAOProgram; + ShaderProgram* m_SSAOViewSpaceZProgram; + + DrawBloomPass* m_DrawBloomPass; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/SSAOPassState.h b/include/Engine/Rendering/SSAOPassState.h new file mode 100644 index 00000000..115fdcf7 --- /dev/null +++ b/include/Engine/Rendering/SSAOPassState.h @@ -0,0 +1,15 @@ +#ifndef SSAOPassState_h__ +#define SSAOPassState_h__ + +#include "Rendering/RenderState.h" + +class SSAOPassState : public RenderState +{ +public: + SSAOPassState(); + ~SSAOPassState(); +private: + +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/SpriteJob.h b/include/Engine/Rendering/SpriteJob.h index 3bb43a1c..25c2d5cb 100644 --- a/include/Engine/Rendering/SpriteJob.h +++ b/include/Engine/Rendering/SpriteJob.h @@ -17,7 +17,7 @@ struct SpriteJob : RenderJob { - SpriteJob(ComponentWrapper cSprite, Camera* camera, glm::mat4 matrix, World* world, glm::vec4 fillColor, float fillPercentage, bool depthSorted) + SpriteJob(ComponentWrapper cSprite, Camera* camera, glm::mat4 matrix, World* world, glm::vec4 fillColor, float fillPercentage, bool depthSorted, bool isIndicator) : RenderJob() { Model = ResourceManager::Load<::Model>("Models/Core/UnitQuad.mesh"); @@ -30,7 +30,7 @@ struct SpriteJob : RenderJob StartIndex = matProp.material->StartIndex; EndIndex = matProp.material->EndIndex; - Matrix = matrix; + Matrix = matrix; Color = cSprite["Color"]; Entity = cSprite.EntityID; Position = Transform::AbsolutePosition(world, cSprite.EntityID); @@ -40,6 +40,8 @@ struct SpriteJob : RenderJob Depth = viewpos.z; } World = world; + Pickable = world->HasComponent(cSprite.EntityID, "Button"); + IsIndicator = isIndicator; FillColor = fillColor; FillPercentage = fillPercentage; @@ -61,6 +63,9 @@ struct SpriteJob : RenderJob unsigned int EndIndex = 0; World* World; + bool Pickable; + bool IsIndicator = false; + glm::vec4 FillColor = glm::vec4(0); float FillPercentage = 0.0; diff --git a/include/Engine/Rendering/Texture.h b/include/Engine/Rendering/Texture.h index 0fe650b3..d159e636 100644 --- a/include/Engine/Rendering/Texture.h +++ b/include/Engine/Rendering/Texture.h @@ -18,6 +18,7 @@ public: void Bind(GLenum textureUnit = GL_TEXTURE0); GLuint m_Texture = 0; + unsigned char* Data = nullptr; }; diff --git a/include/Game/Events/EDoubleJump.h b/include/Game/Events/EDoubleJump.h index 767d5b39..f5cad1fd 100644 --- a/include/Game/Events/EDoubleJump.h +++ b/include/Game/Events/EDoubleJump.h @@ -8,7 +8,7 @@ namespace Events struct DoubleJump : public Event { - + EntityID entityID; }; } diff --git a/include/Game/Game.h b/include/Game/Game.h index baf15656..06fd4703 100644 --- a/include/Game/Game.h +++ b/include/Game/Game.h @@ -8,7 +8,6 @@ #include "Core/EventBroker.h" #include "Rendering/Renderer.h" #include "Core/InputManager.h" -#include "GUI/Frame.h" #include "Core/World.h" #include "Input/InputProxy.h" #include "Input/KeyboardInputHandler.h" @@ -35,6 +34,9 @@ #include "Sound/SoundManager.h" #include "Systems/SoundSystem.h" +//Performance +#include "Core/PerformanceTimer.h" + class Game { public: @@ -53,7 +55,6 @@ private: IRenderer* m_Renderer; InputManager* m_InputManager; InputProxy* m_InputProxy; - GUI::Frame* m_FrameStack; World* m_World; Octree* m_OctreeCollision; Octree* m_OctreeTrigger; diff --git a/include/Game/MiniDump.h b/include/Game/MiniDump.h new file mode 100644 index 00000000..4ad4548d --- /dev/null +++ b/include/Game/MiniDump.h @@ -0,0 +1,8 @@ +#ifndef MiniDump_h__ +#define MiniDump_h__ + +#include + +void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag); + +#endif diff --git a/include/Game/Systems/AmmoPickupSystem.h b/include/Game/Systems/AmmoPickupSystem.h new file mode 100644 index 00000000..0fbd9e08 --- /dev/null +++ b/include/Game/Systems/AmmoPickupSystem.h @@ -0,0 +1,33 @@ +#ifndef AmmoPickupSystem_h__ +#define AmmoPickupSystem_h__ + +#include "Core/System.h" +#include "Core/Transform.h" +#include "Core/ResourceManager.h" +#include "Core/EntityFileParser.h" +#include "Core/EPickupSpawned.h" +#include "Core/EAmmoPickup.h" +#include "Engine/Collision/ETrigger.h" +#include "Common.h" + +class AmmoPickupSystem : public ImpureSystem +{ +public: + AmmoPickupSystem(SystemParams params); + + virtual void Update(double dt) override; + +private: + EventRelay m_ETriggerTouch; + bool OnTriggerTouch(Events::TriggerTouch& e); + + struct NewAmmoPickup { + glm::vec3 Pos; + double AmmoGain; + double RespawnTimer; + double DecreaseThisRespawnTimer; + EntityID parentID; + }; + std::vector m_ETriggerTouchVector; +}; +#endif diff --git a/include/Game/Systems/AmmunitionHUDSystem.h b/include/Game/Systems/AmmunitionHUDSystem.h new file mode 100644 index 00000000..b22a85b5 --- /dev/null +++ b/include/Game/Systems/AmmunitionHUDSystem.h @@ -0,0 +1,17 @@ +#ifndef AmmunitionHUDSystem_h__ +#define AmmunitionHUDSystem_h__ + +#include "../../Engine/Core/System.h" +#include "../../Engine/GLM.h" + +class AmmunitionHUDSystem : public ImpureSystem +{ +public: + AmmunitionHUDSystem(SystemParams params) + : System(params) + { } + + virtual void Update(double dt) override; +}; + +#endif \ No newline at end of file diff --git a/include/Game/Systems/DamageIndicatorSystem.h b/include/Game/Systems/DamageIndicatorSystem.h index 053b196b..ae9ba195 100644 --- a/include/Game/Systems/DamageIndicatorSystem.h +++ b/include/Game/Systems/DamageIndicatorSystem.h @@ -14,11 +14,13 @@ #include #include "Rendering/Util/CommonFunctions.h" +//#define INDICATOR_TEST -class DamageIndicatorSystem : public System +class DamageIndicatorSystem : public ImpureSystem { public: DamageIndicatorSystem(SystemParams params); + virtual void Update(double dt) override; private: EventRelay m_EPlayerDamage; @@ -28,6 +30,20 @@ private: bool OnSetCamera(const Events::SetCamera& e); EntityID m_CurrentCamera = -1; + struct DamageIndicatorStruct { + EntityWrapper spriteEntity; + glm::vec3 enemyPosition; + DamageIndicatorStruct(EntityWrapper sprite, glm::vec3 pos) + : spriteEntity(sprite) + , enemyPosition(pos) {} + }; + std::vector updateDamageIndicatorVector; + float CalculateAngle(EntityWrapper player, glm::vec3 enemyPos); + //for tests +#ifdef INDICATOR_TEST + glm::vec3 DamageIndicatorTest(EntityWrapper player); + int m_TestVar = 0; +#endif }; #endif diff --git a/include/Game/Systems/PlayerHUDSystem.h b/include/Game/Systems/HealthHUDSystem.h similarity index 57% rename from include/Game/Systems/PlayerHUDSystem.h rename to include/Game/Systems/HealthHUDSystem.h index 50b6a258..49a40041 100644 --- a/include/Game/Systems/PlayerHUDSystem.h +++ b/include/Game/Systems/HealthHUDSystem.h @@ -3,13 +3,11 @@ #include "../../Engine/Core/System.h" #include "../../Engine/GLM.h" -#include "../../Engine/Rendering/ESetCamera.h" -#include -class PlayerHUDSystem : public ImpureSystem +class HealthHUDSystem : public ImpureSystem { public: - PlayerHUDSystem(SystemParams params) + HealthHUDSystem(SystemParams params) : System(params) { } diff --git a/include/Game/Systems/HealthSystem.h b/include/Game/Systems/HealthSystem.h index b66d8eb0..962e3737 100644 --- a/include/Game/Systems/HealthSystem.h +++ b/include/Game/Systems/HealthSystem.h @@ -9,6 +9,8 @@ #include "Core/EPlayerDamage.h" #include "Core/EPlayerHealthPickup.h" #include "Core/EPlayerDeath.h" +#include "Core/ConfigFile.h" +#include "Input/EInputCommand.h" #include #include @@ -23,15 +25,18 @@ public: virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) override; private: - //methods which will take care of specific events + bool m_NetworkEnabled; + + // methods which will take care of specific events EventRelay m_EPlayerDamage; bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e); EventRelay m_EPlayerHealthPickup; bool HealthSystem::OnPlayerHealthPickup(Events::PlayerHealthPickup& e); + EventRelay m_InputCommand; + bool HealthSystem::OnInputCommand(Events::InputCommand& e); //vector which will keep track of health changes std::vector> m_DeltaHealthVector; - }; #endif \ No newline at end of file diff --git a/include/Game/Systems/KillFeedSystem.h b/include/Game/Systems/KillFeedSystem.h new file mode 100644 index 00000000..31423999 --- /dev/null +++ b/include/Game/Systems/KillFeedSystem.h @@ -0,0 +1,39 @@ +#ifndef KillFeedSystem_h__ +#define KillFeedSystem_h__ + +#include "../../Engine/Core/System.h" +#include "../../Engine/GLM.h" +#include "Core/EPlayerDeath.h" + +class KillFeedSystem : public ImpureSystem +{ +public: + KillFeedSystem(SystemParams params) + : System(params) + { + EVENT_SUBSCRIBE_MEMBER(m_EPlayerDeath, &KillFeedSystem::OnPlayerDeath); + + } + + virtual void Update(double dt) override; + +private: + + + + + EventRelay m_EPlayerDeath; + bool KillFeedSystem::OnPlayerDeath(Events::PlayerDeath& e); + + struct KillFeedInfo + { + std::string Content; + glm::vec4 Color; + float TimeToLive = 5.f; + }; + + std::list m_DeathQueue; + +}; + +#endif \ No newline at end of file diff --git a/include/Game/Systems/PickupSpawnSystem.h b/include/Game/Systems/PickupSpawnSystem.h index f912e8ff..66c5f630 100644 --- a/include/Game/Systems/PickupSpawnSystem.h +++ b/include/Game/Systems/PickupSpawnSystem.h @@ -27,6 +27,7 @@ private: double HealthGain; double RespawnTimer; double DecreaseThisRespawnTimer; + EntityID parentID; }; std::vector m_ETriggerTouchVector; }; diff --git a/include/Game/Systems/PlayerMovementSystem.h b/include/Game/Systems/PlayerMovementSystem.h index 2bcae866..92aa1915 100644 --- a/include/Game/Systems/PlayerMovementSystem.h +++ b/include/Game/Systems/PlayerMovementSystem.h @@ -34,10 +34,14 @@ private: glm::vec3 m_LastPosition = glm::vec3(); // The logic for making the sound play when player is moving void playerStep(double dt); + // Spawn a hexagon at origin of an Entity + void spawnHexagon(EntityWrapper target); EventRelay m_EPlayerSpawned; bool OnPlayerSpawned(Events::PlayerSpawned& e); + EventRelay m_EDoubleJump; + bool PlayerMovementSystem::OnDoubleJump(Events::DoubleJump & e); void updateMovementControllers(double dt); - void updateVelocity(double dt); + void updateVelocity(EntityWrapper player, double dt); }; \ No newline at end of file diff --git a/include/Game/Systems/PlayerSpawnSystem.h b/include/Game/Systems/PlayerSpawnSystem.h index c68bb21f..bf87bef8 100644 --- a/include/Game/Systems/PlayerSpawnSystem.h +++ b/include/Game/Systems/PlayerSpawnSystem.h @@ -13,6 +13,8 @@ public: PlayerSpawnSystem(SystemParams params); virtual void Update(double dt) override; + + static void SetRespawnTime(float respawnTime) { m_RespawnTime = respawnTime; }; private: struct SpawnRequest @@ -23,10 +25,19 @@ private: bool m_NetworkEnabled = false; std::vector m_SpawnRequests; + + //Player ID -> EntityWrapper. std::map m_PlayerEntities; + //EntityWrapper ID -> Player ID. + std::map m_PlayerIDs; + + static float m_RespawnTime; + float m_Timer; EventRelay m_OnInputCommand; - bool OnInputCommand(const Events::InputCommand& e); + bool OnInputCommand(Events::InputCommand& e); EventRelay m_OnPlayerSpawnerd; bool OnPlayerSpawned(Events::PlayerSpawned& e); + EventRelay m_OnPlayerDeath; + bool OnPlayerDeath(Events::PlayerDeath& e); }; \ No newline at end of file diff --git a/include/Game/Systems/SpawnerSystem.h b/include/Game/Systems/SpawnerSystem.h index 9cb4bd06..e4a44738 100644 --- a/include/Game/Systems/SpawnerSystem.h +++ b/include/Game/Systems/SpawnerSystem.h @@ -15,11 +15,16 @@ class SpawnerSystem : public System public: SpawnerSystem(SystemParams params); - static EntityWrapper Spawn(EntityWrapper spawner, EntityWrapper parent = EntityWrapper::Invalid); + // If dontCollideComponent is set, to e.g. "Player", then all the spawner + // will try to pick a spawn location so that the spawned entity doesn't + // collide with anything that has that component and is collidable. + static EntityWrapper Spawn(EntityWrapper spawner, EntityWrapper parent = EntityWrapper::Invalid, const std::string& dontCollideComponent = ""); private: EventRelay m_OnSpawnerSpawn; bool OnSpawnerSpawn(Events::SpawnerSpawn& e); + static void transformEntityToSpawnPoint(EntityWrapper spawnedEntity, EntityWrapper spawnPoint); + static bool spawnedEntityIsColliding(EntityWrapper spawnedEntity, EntityWrapper spawnPoint, const std::string& dontCollideComponent); }; #endif \ No newline at end of file diff --git a/include/Game/Systems/Weapon/AssaultWeaponBehaviour.h b/include/Game/Systems/Weapon/AssaultWeaponBehaviour.h index 915854ff..7bd9c175 100644 --- a/include/Game/Systems/Weapon/AssaultWeaponBehaviour.h +++ b/include/Game/Systems/Weapon/AssaultWeaponBehaviour.h @@ -1,11 +1,13 @@ #include "Sound/EPlaySoundOnEntity.h" #include "Collision/Collision.h" #include "Rendering/AnimationSystem.h" +#include "Core/ConfigFile.h" #include "WeaponBehaviour.h" #include "../SpawnerSystem.h" #include "Core/EPlayerDamage.h" #include "Core/EShoot.h" + class AssaultWeaponBehaviour : public WeaponBehaviour { public: @@ -19,11 +21,13 @@ public: private: EntityWrapper m_FirstPersonModel; + EntityWrapper m_ThirdPersonModel; // State bool m_Firing = false; bool m_Reloading = false; double m_ReloadTimer = 0.0; - EntityWrapper m_ReloadImpersonator; + EntityWrapper m_FirstPersonReloadImpersonator; + EntityWrapper m_ThirdPersonReloadImpersonator; double m_TimeSinceLastFire = 0.0; EventRelay m_EAnimationComplete; @@ -33,7 +37,8 @@ private: void fireRound(); void spawnTracer(); float traceRayDistance(glm::vec3 origin, glm::vec3 direction); - void playSound(); + void playFireSound(); + void playEmptySound(); void viewPunch(); void finishReload(); void playShootAnimation(); diff --git a/resources/DefaultConfig.ini b/resources/DefaultConfig.ini index 6911632f..60ee4823 100644 --- a/resources/DefaultConfig.ini +++ b/resources/DefaultConfig.ini @@ -4,6 +4,7 @@ LoadMap= ; if true -> Pool allocation is not used when calling Allocate/Free, just use regular dynamic allocation. ; if false -> Use pool allocation. DisableMemoryPool=false +RespawnTime = 8.0 EditorEnabled=false OutOfBodyExperience=false @@ -22,7 +23,7 @@ StartNetwork=false IsServer=false Name=Bob Address=127.0.0.1 -Port=13 +Port=27666 MaxConnections=8 SnapshotInterval=0.05 SendInputIntervalMs=33 diff --git a/resources/DefaultInput.ini b/resources/DefaultInput.ini index 683d48e2..776cbecd 100644 --- a/resources/DefaultInput.ini +++ b/resources/DefaultInput.ini @@ -24,4 +24,7 @@ F1=ToggleEditor C=ConnectToServer N=SwitchToServer M=SwitchToClient -P=SwitchToPlayer \ No newline at end of file +P=SwitchToPlayer +K=TakeDamage,1500 +F2=PerformanceTimingResetAllTimers +F3=PerformanceTimingCreateExcelData \ No newline at end of file diff --git a/resources/Schema/Components.xsd b/resources/Schema/Components.xsd index d81c3852..fc72762e 100644 --- a/resources/Schema/Components.xsd +++ b/resources/Schema/Components.xsd @@ -32,6 +32,7 @@ + @@ -39,7 +40,13 @@ + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/AmmoPickup.xml b/resources/Schema/Components/AmmoPickup.xml new file mode 100644 index 00000000..6da0b6fa --- /dev/null +++ b/resources/Schema/Components/AmmoPickup.xml @@ -0,0 +1,5 @@ + + + 3 + 30 + \ No newline at end of file diff --git a/resources/Schema/Components/AmmoPickup.xsd b/resources/Schema/Components/AmmoPickup.xsd new file mode 100644 index 00000000..1970eb78 --- /dev/null +++ b/resources/Schema/Components/AmmoPickup.xsd @@ -0,0 +1,21 @@ + + + + + + + + An Ammo Pickup + + + + + The respawn timer for a ammo pickup + + + How much percent ammo gain the player will get + + + + + diff --git a/resources/Schema/Components/AmmunitionHUD.xml b/resources/Schema/Components/AmmunitionHUD.xml new file mode 100644 index 00000000..63b86150 --- /dev/null +++ b/resources/Schema/Components/AmmunitionHUD.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/resources/Schema/Components/AmmunitionHUD.xsd b/resources/Schema/Components/AmmunitionHUD.xsd new file mode 100644 index 00000000..1a48d8d1 --- /dev/null +++ b/resources/Schema/Components/AmmunitionHUD.xsd @@ -0,0 +1,10 @@ + + + + + + + Hud element for tracking ammunition from parent with AssaultWeapon component. Child with the name "MagazineAmmo" tracks clip ammunition. Child with the name "Ammo" tracks ammo. + + + \ No newline at end of file diff --git a/resources/Schema/Components/Button.xml b/resources/Schema/Components/Button.xml new file mode 100644 index 00000000..cbfd80d4 --- /dev/null +++ b/resources/Schema/Components/Button.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/resources/Schema/Components/Button.xsd b/resources/Schema/Components/Button.xsd new file mode 100644 index 00000000..7d97fef1 --- /dev/null +++ b/resources/Schema/Components/Button.xsd @@ -0,0 +1,10 @@ + + + + + + + Makes sprites klickable. + + + \ No newline at end of file diff --git a/resources/Schema/Components/KillFeed.xml b/resources/Schema/Components/KillFeed.xml new file mode 100644 index 00000000..558d4983 --- /dev/null +++ b/resources/Schema/Components/KillFeed.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/resources/Schema/Components/KillFeed.xsd b/resources/Schema/Components/KillFeed.xsd new file mode 100644 index 00000000..fedce9fb --- /dev/null +++ b/resources/Schema/Components/KillFeed.xsd @@ -0,0 +1,9 @@ + + + + + + HUD element for tracking the 3 last kills, printed on the children with the names "KillFeed1", "KillFeed2", "KillFeed3" + + + \ No newline at end of file diff --git a/resources/Schema/Components/Menu.xml b/resources/Schema/Components/Menu.xml new file mode 100644 index 00000000..f17427b8 --- /dev/null +++ b/resources/Schema/Components/Menu.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/resources/Schema/Components/Menu.xsd b/resources/Schema/Components/Menu.xsd new file mode 100644 index 00000000..18ea8565 --- /dev/null +++ b/resources/Schema/Components/Menu.xsd @@ -0,0 +1,9 @@ + + + + + + Attach this to the center point of a menu that uses several pages. + + + \ No newline at end of file diff --git a/resources/Schema/Components/Model.xml b/resources/Schema/Components/Model.xml index 4b77dacb..f81c8210 100644 --- a/resources/Schema/Components/Model.xml +++ b/resources/Schema/Components/Model.xml @@ -8,4 +8,5 @@ true true true + 3.0 \ No newline at end of file diff --git a/resources/Schema/Components/Model.xsd b/resources/Schema/Components/Model.xsd index 9c0cd519..31203ff6 100644 --- a/resources/Schema/Components/Model.xsd +++ b/resources/Schema/Components/Model.xsd @@ -33,6 +33,9 @@ Whether the model should use the Glowmap or not + + Intensity of the glow map + diff --git a/resources/Schema/Components/Page.xml b/resources/Schema/Components/Page.xml new file mode 100644 index 00000000..db7b9cc2 --- /dev/null +++ b/resources/Schema/Components/Page.xml @@ -0,0 +1,4 @@ + + + 0 + \ No newline at end of file diff --git a/resources/Schema/Components/Page.xsd b/resources/Schema/Components/Page.xsd new file mode 100644 index 00000000..777124a2 --- /dev/null +++ b/resources/Schema/Components/Page.xsd @@ -0,0 +1,14 @@ + + + + + + Use this on a child to a Menu entity and make sure that ID is not the same as other pages. + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/SpriteIndicator.xml b/resources/Schema/Components/SpriteIndicator.xml new file mode 100644 index 00000000..cbed22f0 --- /dev/null +++ b/resources/Schema/Components/SpriteIndicator.xml @@ -0,0 +1,5 @@ + + + 10 + false + \ No newline at end of file diff --git a/resources/Schema/Components/SpriteIndicator.xsd b/resources/Schema/Components/SpriteIndicator.xsd new file mode 100644 index 00000000..bd8c1038 --- /dev/null +++ b/resources/Schema/Components/SpriteIndicator.xsd @@ -0,0 +1,19 @@ + + + + + + + + Billbord a Sprite around global Y axis + + + + + + Add a Team component to this Entity or Parent to make it visible only for that team + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/Text.xml b/resources/Schema/Components/Text.xml index 9ca629d1..d38d3961 100644 --- a/resources/Schema/Components/Text.xml +++ b/resources/Schema/Components/Text.xml @@ -1,6 +1,6 @@ - Text + true diff --git a/resources/Schema/Entities/AmmoHUD b/resources/Schema/Entities/AmmoHUD new file mode 100644 index 00000000..6cdb6568 --- /dev/null +++ b/resources/Schema/Entities/AmmoHUD @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + + + + + + + 0 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + 360 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + + + + + + + 0 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + 360 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/AmmoPickup.xml b/resources/Schema/Entities/AmmoPickup.xml new file mode 100644 index 00000000..bebde467 --- /dev/null +++ b/resources/Schema/Entities/AmmoPickup.xml @@ -0,0 +1,23 @@ + + + + + + + Models/Props/PickUps/AmmoPickUp.mesh + + + 8 + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/AmmoPickupTest.xml b/resources/Schema/Entities/AmmoPickupTest.xml new file mode 100644 index 00000000..22690c65 --- /dev/null +++ b/resources/Schema/Entities/AmmoPickupTest.xml @@ -0,0 +1,294 @@ + + + + + + + + + + + + Models/LevelBase/MapVersion1.mesh + + + + + + + + + 2 + + + Models/DirectionalLightWidget.mesh + false + + + + + + + + + + + + + 8 + 2.7999999523162842 + + + + + + + + + + + 8 + 2.7999999523162842 + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + false + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + false + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 22 + + + Models/Core/UnitSphere.mesh + + + + + + + + + + + + + 1 + 22 + + + Models/Core/UnitSphere.mesh + + + + + + + + + + + + + 4 + 44 + + + Models/Core/UnitSphere.mesh + + + + + + + + + + + diff --git a/resources/Schema/Entities/AmmunitionHUD.xml b/resources/Schema/Entities/AmmunitionHUD.xml new file mode 100644 index 00000000..2eaf2753 --- /dev/null +++ b/resources/Schema/Entities/AmmunitionHUD.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + + + + + + + 32 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + 360 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/Button.xml b/resources/Schema/Entities/Button.xml new file mode 100644 index 00000000..21f64334 --- /dev/null +++ b/resources/Schema/Entities/Button.xml @@ -0,0 +1,32 @@ + + + + + + + Textures/Core/White.png + + + + + + + + + + + + Play + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + diff --git a/resources/Schema/Entities/CapturePointHUDGroup b/resources/Schema/Entities/CapturePointHUDGroup.xml similarity index 95% rename from resources/Schema/Entities/CapturePointHUDGroup rename to resources/Schema/Entities/CapturePointHUDGroup.xml index 9dce0ffb..d35e1bde 100644 --- a/resources/Schema/Entities/CapturePointHUDGroup +++ b/resources/Schema/Entities/CapturePointHUDGroup.xml @@ -1,9 +1,10 @@ - + - + + diff --git a/resources/Schema/Entities/DeadGirl.xlm b/resources/Schema/Entities/DeadGirl.xlm new file mode 100644 index 00000000..90a0411d --- /dev/null +++ b/resources/Schema/Entities/DeadGirl.xlm @@ -0,0 +1,255 @@ + + + + + + + + + + 600 + + + + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + + + Fonts/DroidSans.ttf,100 + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + + + + + + + + 1 + + + + + Models/Core/UnitHexagon.mesh + + + + + + + + + + + + + + Textures/Weapons/Crosshair/SmallThickHoleDot.png + false + + + + + + + + + + + + + + Idle + 0.28963486380924053 + 1 + + + Models/Characters/Assault/FirstPerson.mesh + true + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + true + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + + + + Schema/Entities/WeaponReloadEffect.xml + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + Idle + 0.42156525436696768 + 1 + + + AimRifle + + + + + Models/Characters/Assault/AssaultAnimations.mesh + + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + false + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + diff --git a/resources/Schema/Entities/DeadGirls.xml b/resources/Schema/Entities/DeadGirls.xml new file mode 100644 index 00000000..2b004c34 --- /dev/null +++ b/resources/Schema/Entities/DeadGirls.xml @@ -0,0 +1,1137 @@ + + + + + + + + + + + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + Models/Characters/Assault/AssaultTPose.mesh + + + + + + + + + + + + + Models/Characters/Assault/AssaultTPose.mesh + + + + + + + + + + + + + + + + sModels/Widgets/Lights/DirectionalLightWidget.mesh + + + + + + + + + + + + + Models/Test/ObstacleCourse.mesh + + + + + + + + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + Models/Characters/Assault/AssaultTPose.mesh + + + + + + + + + + + + + Models/Characters/Assault/AssaultTPose.mesh + + + + + + + + + + + + + + + + + + 600 + + + + + 1 + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + + Fonts/DroidSans.ttf,100 + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + + + + + + + + 0.0099999997764825821 + + + + + Models/Core/UnitHexagon.mesh + + + + + + + + + + + + + + Textures/Weapons/Crosshair/SmallThickHoleDot.png + false + + + + + + + + + + + + + + Idle + 1.9484546004984589 + 1 + + + Models/Characters/Assault/FirstPerson.mesh + true + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + true + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + + + + Schema/Entities/WeaponReloadEffect.xml + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + Idle + 0.83038427580044871 + 1 + + + AimRifle + + + + + Models/Characters/Assault/AssaultAnimations.mesh + + + + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + false + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + + + + + + 600 + + + + + 1 + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + + Fonts/DroidSans.ttf,100 + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + + + + + + + + 0.0099999997764825821 + + + + + Models/Core/UnitHexagon.mesh + + + + + + + + + + + + + + Textures/Weapons/Crosshair/SmallThickHoleDot.png + false + + + + + + + + + + + + + + Idle + 1.6865388023565906 + 1 + + + Models/Characters/Assault/FirstPerson.mesh + true + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + true + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + + + + Schema/Entities/WeaponReloadEffect.xml + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + Idle + 0.71846833460743298 + 1 + + + AimRifle + + + + + Models/Characters/Assault/AssaultAnimations.mesh + + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + false + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + + + + + + 600 + + + + + 1 + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + + Fonts/DroidSans.ttf,100 + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + + + + + + + + 0.0099999997764825821 + + + + + Models/Core/UnitHexagon.mesh + + + + + + + + + + + + + + Textures/Weapons/Crosshair/SmallThickHoleDot.png + false + + + + + + + + + + + + + + Idle + 1.4291906531606173 + 1 + + + Models/Characters/Assault/FirstPerson.mesh + true + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + true + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + + + + Schema/Entities/WeaponReloadEffect.xml + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + Idle + 0.34445363000679663 + 1 + + + AimRifle + + + + + Models/Characters/Assault/AssaultAnimations.mesh + + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + false + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + + + + + + 600 + + + + + 1 + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + + Fonts/DroidSans.ttf,100 + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + + + + + + + + 0.0099999997764825821 + + + + + Models/Core/UnitHexagon.mesh + + + + + + + + + + + + + + Textures/Weapons/Crosshair/SmallThickHoleDot.png + false + + + + + + + + + + + + + + Idle + 0.94316388255725769 + 1 + + + Models/Characters/Assault/FirstPerson.mesh + true + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + true + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + + + + Schema/Entities/WeaponReloadEffect.xml + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + Idle + 1.7750936055429634 + 1 + + + AimRifle + + + + + Models/Characters/Assault/AssaultAnimations.mesh + + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + false + + + + + + + + + + + Schema/Entities/RayBlue.xml + + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + diff --git a/resources/Schema/Entities/DoubleJumpHexagon.xml b/resources/Schema/Entities/DoubleJumpHexagon.xml index c1aaec34..f4596efd 100644 --- a/resources/Schema/Entities/DoubleJumpHexagon.xml +++ b/resources/Schema/Entities/DoubleJumpHexagon.xml @@ -2,27 +2,26 @@ - - Models/Effects/JumpEffectHexagon.mesh - - true - - - - - 0.5 true - - + true 0.5 - + true + + Models/Effects/JumpEffectHexagon.mesh + + true + + + + + diff --git a/resources/Schema/Entities/GameMap.xml b/resources/Schema/Entities/GameMap.xml index 84a1e363..c3a16361 100644 --- a/resources/Schema/Entities/GameMap.xml +++ b/resources/Schema/Entities/GameMap.xml @@ -144,7 +144,7 @@ - + @@ -206,14 +206,16 @@ - + - + + + diff --git a/resources/Schema/Entities/HealthHUD.xml b/resources/Schema/Entities/HealthHUD.xml new file mode 100644 index 00000000..ec781d8f --- /dev/null +++ b/resources/Schema/Entities/HealthHUD.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + 1 + + + + + 100/100 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + 1 + + + + Textures/HealthHUD3.png + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/HealthPickup.xml b/resources/Schema/Entities/HealthPickup.xml index dfc6f938..b4b83392 100644 --- a/resources/Schema/Entities/HealthPickup.xml +++ b/resources/Schema/Entities/HealthPickup.xml @@ -2,13 +2,18 @@ - - Models/Core/UnitSphere.mesh + Models/Props/PickUps/HealthPickUp.mesh + + 8 + + - + + + diff --git a/resources/Schema/Entities/NewMap.xml b/resources/Schema/Entities/NewMap.xml index f4c768f0..00dc476f 100644 --- a/resources/Schema/Entities/NewMap.xml +++ b/resources/Schema/Entities/NewMap.xml @@ -89,6 +89,7 @@ Models/Props/Highground5.mesh + @@ -99,6 +100,7 @@ Models/Props/Highground6.mesh + @@ -210,8 +212,7 @@ - Models/Props/Pillars/SciFiPillar1.mesh - + Models/Props/Pillars/SciFiPillar1Blue.mesh @@ -224,12 +225,11 @@ - Models/Props/Pillars/SciFiPillar1.mesh - + Models/Props/Pillars/SciFiPillar1Red.mesh - - + + @@ -263,7 +263,7 @@ - Models/Props/Pillars/SciFiPillar2.mesh + Models/Props/Pillars/SciFiPillar2Red.mesh @@ -276,7 +276,7 @@ - Models/Props/Pillars/SciFiPillar3.mesh + Models/Props/Pillars/SciFiPillar3Blue.mesh @@ -290,10 +290,10 @@ - Models/Props/Pillars/SciFiPillar1.mesh + Models/Props/Pillars/SciFiPillar1Blue.mesh - + @@ -304,10 +304,10 @@ - Models/Props/Pillars/SciFiPillar1.mesh + Models/Props/Pillars/SciFiPillar1Red.mesh - + @@ -318,7 +318,7 @@ - Models/Props/Pillars/SciFiPillar3.mesh + Models/Props/Pillars/SciFiPillar3Red.mesh @@ -360,8 +360,8 @@ Models/Props/Pillars/StonePillar.mesh - - + + @@ -370,7 +370,7 @@ - Models/Props/Pillars/SciFiPillar2.mesh + Models/Props/Pillars/SciFiPillar2Blue.mesh @@ -383,10 +383,10 @@ - Models/Props/Pillars/SciFiPillar2.mesh + Models/Props/Pillars/SciFiPillar2Red.mesh - + @@ -418,6 +418,141 @@ + + + + + Models/Props/Pillars/SciFiPillar2Blue.mesh + + + + + + + + + + + + + + Models/Props/Pillars/SciFiPillar2Blue.mesh + + + + + + + + + + + + + + Models/Props/Pillars/SciFiPillar2Blue.mesh + + + + + + + + + + + + + + Models/Props/Pillars/SciFiPillar2Blue.mesh + + + + + + + + + + + + + Models/Props/Pillars/StonePillar.mesh + + + + + + + + + + + + + Models/Props/Pillars/SciFiPillar2Red.mesh + + + + + + + + + + + + + + Models/Props/Pillars/SciFiPillar2Red.mesh + + + + + + + + + + + + + Models/Props/Pillars/SciFiPillar2Red.mesh + + + + + + + + + + + + + + Models/Props/Pillars/StonePillar.mesh + + + + + + + + + + + + + Models/Props/Pillars/SciFiPillar2Red.mesh + + + + + + + + @@ -429,7 +564,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -440,7 +575,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -455,10 +590,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh - + @@ -466,7 +601,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -480,10 +615,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh - + @@ -492,9 +627,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh + @@ -506,10 +642,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh - + @@ -518,10 +654,11 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh - + + @@ -532,7 +669,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh @@ -544,7 +681,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh @@ -558,7 +695,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh @@ -574,7 +711,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -586,10 +723,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh - + @@ -599,10 +736,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh - + @@ -682,8 +819,8 @@ Models/Props/Walls/SmallWall2.mesh - - + + @@ -807,10 +944,10 @@ - Models/Props/Walls/MediumWall3.mesh + Models/Props/Walls/MediumWall1.mesh - + @@ -819,10 +956,10 @@ - Models/Props/Walls/MediumWall3.mesh + Models/Props/Walls/MediumWall1.mesh - + @@ -831,10 +968,10 @@ - Models/Props/Walls/MediumWall3.mesh + Models/Props/Walls/MediumWall1.mesh - + @@ -845,7 +982,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh @@ -856,7 +993,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh @@ -868,7 +1005,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh @@ -879,7 +1016,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh @@ -895,7 +1032,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh @@ -908,7 +1045,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh @@ -922,7 +1059,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -933,7 +1070,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -945,7 +1082,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -957,7 +1094,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -971,7 +1108,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -1061,10 +1198,11 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh - + + @@ -1072,10 +1210,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh - + @@ -1083,9 +1221,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh + @@ -1114,7 +1253,7 @@ Models/Props/Walls/SmallWall2.mesh - + @@ -1122,7 +1261,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -1133,7 +1272,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -1149,10 +1288,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh - + @@ -1160,10 +1299,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh - + @@ -1172,10 +1311,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh - + @@ -1184,10 +1323,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh - + @@ -1198,7 +1337,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -1210,7 +1349,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallBlue.mesh @@ -1228,8 +1367,8 @@ Models/Props/Walls/MediumWall3.mesh - - + + @@ -1238,10 +1377,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh - + @@ -1249,10 +1388,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh - + @@ -1261,10 +1400,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh - + @@ -1275,10 +1414,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh - + @@ -1287,10 +1426,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh - + @@ -1301,11 +1440,11 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh - - + + @@ -1313,7 +1452,7 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh @@ -1328,7 +1467,7 @@ Models/Props/Walls/SmallWall2.mesh - + @@ -1337,10 +1476,10 @@ - Models/Props/Walls/BigWall.mesh + Models/Props/Walls/BigWallRed.mesh - + @@ -1366,8 +1505,9 @@ Models/Props/Walls/SpecialWall1.mesh - - + + + @@ -1377,6 +1517,7 @@ Models/Props/Flora/SpecialRoot.mesh + @@ -1418,7 +1559,7 @@ Models/Props/Walls/MediumWall1.mesh - + @@ -1430,8 +1571,8 @@ Models/Props/Walls/MediumWall3.mesh - - + + @@ -1443,7 +1584,7 @@ Models/Props/Walls/MediumWall1.mesh - + @@ -1469,12 +1610,232 @@ Models/Props/Walls/SmallWall2.mesh - + + + + + + Models/Props/Walls/SmallWall2.mesh + + + + + + + + + + + + Models/Props/Walls/SmallWall2.mesh + + + + + + + + + + + + Models/Props/Walls/MediumWall3.mesh + + + + + + + + + + + + Models/Props/Walls/MediumWall2.mesh + + + + + + + + + + + Models/Props/Walls/MediumWall2.mesh + + + + + + + + + + + + + Models/Props/Walls/SmallWall3.mesh + + + + + + + + + + + + Models/Props/Walls/SmallWall4.mesh + + + + + + + + + + + + + + + + Models/Props/Walls/MediumWall3.mesh + + + + + + + + + + + + + Models/Props/Walls/MediumWall2.mesh + + + + + + + + + + + + + Models/Props/Walls/SpecialWall1.mesh + + + + + + + + + + + + + + Models/Props/Walls/MediumWall1.mesh + + + + + + + + + + + + + Models/Props/Walls/SpecialWall1.mesh + + + + + + + + + + + + Models/Props/Walls/SpecialWall1.mesh + + + + + + + + + + + + + Models/Props/Walls/SpecialWall1.mesh + + + + + + + + + + + + + Models/Props/Walls/SpecialWall1.mesh + + + + + + + + + + + + + Models/Props/Walls/SpecialWall1.mesh + + + + + + + + + + + + + + Models/Props/Walls/SpecialWall1.mesh + + + + + + + + + @@ -1489,23 +1850,9 @@ Models/Props/Bridges/WoodenBridge.mesh - - - - - - - - - - - - Models/Props/Bridges/WoodenBridge.mesh - - - + - + @@ -1517,7 +1864,21 @@ Models/Props/Bridges/WoodenBridge.mesh - + + + + + + + + + + + + Models/Props/Bridges/WoodenBridge.mesh + + + @@ -1527,7 +1888,7 @@ - Models/Props/Bridges/SciFiBridge.mesh + Models/Props/Bridges/SciFiBridge1Red.mesh @@ -1646,7 +2007,7 @@ - Models/Props/Bridges/SciFiBridge.mesh + Models/Props/Bridges/SciFiBridge1Blue.mesh @@ -1766,12 +2127,92 @@ - Models/Props/Flora/TreeLog.mesh + Models/Props/Pillars/SciFiBridgePillar1.mesh - - - + + + + + + + + + + + + Models/Props/Bridges/SciFiBridge1Blue.mesh + + + + + + + + + + + + + Models/Props/Bridges/SciFiBridge1Blue.mesh + + + + + + + + + + + + + Models/Props/Pillars/SciFiBridgePillar1.mesh + + + + + + + + + + + + + + Models/Props/Bridges/SciFiBridge1Red.mesh + + + + + + + + + + + + + Models/Props/Bridges/SciFiBridge1Red.mesh + + + + + + + + + + + + + Models/Props/Bridges/WoodenBridge.mesh + + + + + @@ -1869,8 +2310,7 @@ Models/Props/Pillars/SciFiBridgePillar1.mesh - - + @@ -1897,8 +2337,8 @@ Models/Props/Walls/MediumWall1.mesh - - + + @@ -1981,6 +2421,93 @@ + + + + Models/Core/UnitPlane.mesh + + true + + + + + + + + + + + + + Models/Core/UnitPlane.mesh + + true + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + + + + Models/Props/Pillars/SciFiBridgePillar1.mesh + + + + + + + + + + + + + + Models/Props/Pillars/SciFiBridgePillar1.mesh + + + + + + + + + @@ -1995,59 +2522,11 @@ true - + + - - - - - Models/Props/Flora/AliveBush.mesh - true - - - - - - - - - - - Models/Props/Flora/AliveBush.mesh - true - - - - - - - - - - - Models/Props/Flora/AliveBush.mesh - true - - - - - - - - - - - Models/Props/Flora/AliveBush.mesh - true - - - - - - - - + @@ -2155,7 +2634,7 @@ true - + @@ -2254,6 +2733,7 @@ Models/Props/Flora/SpecialRoot.mesh + @@ -2383,6 +2863,218 @@ + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + + + Models/Props/Flora/SpecialRoot.mesh + + + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + + + Models/Props/Flora/AliveBush.mesh + true + + + + + + + + + + + + + + Models/Props/Flora/SpecialRoot.mesh + + + + + + + + + @@ -2407,28 +3099,15 @@ - Models/Props/Stones/BigStone.mesh + Models/Props/Stones/MediumStone2.mesh - - + + + - - - - - - Models/Props/Stones/BigStone.mesh - - - - - - - - - + @@ -2443,149 +3122,6 @@ - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - @@ -2593,8 +3129,9 @@ Models/Props/Stones/BigStone.mesh - - + + + @@ -2647,8 +3184,8 @@ Models/Props/Stones/BigStone.mesh - - + + @@ -2660,8 +3197,9 @@ Models/Props/Stones/MediumStone1.mesh - - + + + @@ -2670,11 +3208,12 @@ - Models/Props/Stones/MediumStone1.mesh + Models/Props/Stones/SmallStone1.mesh - - + + + @@ -2726,35 +3265,22 @@ Models/Props/Stones/MediumStone1.mesh - - + + + - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - + - Models/Props/Stones/MediumStone1.mesh + Models/Props/Stones/BigStone.mesh - - + + @@ -2762,11 +3288,11 @@ - Models/Props/Stones/MediumStone1.mesh + Models/Props/Stones/BigStone.mesh - - + + @@ -2775,11 +3301,11 @@ - Models/Props/Stones/MediumStone1.mesh + Models/Props/Stones/BigStone.mesh - - + + @@ -2788,11 +3314,38 @@ - Models/Props/Stones/MediumStone1.mesh + Models/Props/Stones/BigStone.mesh - - + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + + + Models/Props/Stones/BigStone.mesh + + + + @@ -2800,10 +3353,11 @@ - Models/Props/Stones/MediumStone1.mesh + Models/Props/Stones/BigStone.mesh - + + @@ -2819,47 +3373,7 @@ Models/Props/Stones/BigStone.mesh - - - - - - - - - - Models/Props/Stones/BigStone.mesh - - - - - - - - - - - - - Models/Props/Stones/BigStone.mesh - - - - - - - - - - - - - - - Models/Props/Stones/BigStone.mesh - - - + @@ -2883,8 +3397,48 @@ Models/Props/Stones/BigStone.mesh - - + + + + + + + + + + + Models/Props/Stones/BigStone.mesh + + + + + + + + + + + + + Models/Props/Stones/SmallStone1.mesh + + + + + + + + + + + + + + Models/Props/Stones/BigStone.mesh + + + + @@ -2910,8 +3464,9 @@ Models/Props/Stones/BigStone.mesh - - + + + @@ -2925,8 +3480,8 @@ Models/Props/Stones/BigStone.mesh - - + + @@ -2973,38 +3528,12 @@ - Models/Props/Stones/MediumStone1.mesh + Models/Props/Stones/SmallStone2.mesh - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - + + + @@ -3013,10 +3542,11 @@ - Models/Props/Stones/MediumStone1.mesh + Models/Props/Stones/MediumStone2.mesh - + + @@ -3025,10 +3555,11 @@ - Models/Props/Stones/ShinyStoneCrystal.mesh + Models/Props/Stones/ShinyStoneCrystalRed.mesh - + + @@ -3038,11 +3569,12 @@ - Models/Props/Stones/ShinyStoneCrystal.mesh + Models/Props/Stones/ShinyStoneCrystalRed.mesh - - + + + @@ -3051,11 +3583,12 @@ - Models/Props/Stones/ShinyStoneCrystal.mesh + Models/Props/Stones/ShinyStoneCrystalRed.mesh - - + + + @@ -3067,7 +3600,477 @@ Models/Props/Walls/MediumWall1.mesh - + + + + + + + + + + Models/Props/Stones/BigStone.mesh + + + + + + + + + + + + + Models/Props/Stones/SmallStone1.mesh + + + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + + Models/Props/Stones/SmallStone2.mesh + + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + + + Models/Props/Stones/SmallStone1.mesh + + + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + + + Models/Props/Stones/SmallStone1.mesh + + + + + + + + + + + + + Models/Props/Stones/SmallStone2.mesh + + + + + + + + + + + + + + Models/Props/Stones/SmallStone1.mesh + + + + + + + + + + + + + + Models/Props/Stones/MediumStone1.mesh + + + + + + + + + + + + + + + + + + + Models/Props/Stones/SmallStone1.mesh + + + + + + + + + + + + + Models/Props/Stones/SmallStone2.mesh + + + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + + Models/Props/Stones/SmallStone1.mesh + + + + + + + + + + + + + + + Models/Props/Stones/SmallStone1.mesh + + + + + + + + + + + + + + Models/Props/Stones/SmallStone1.mesh + + + + + + + + + + + + + + + + Models/Props/Stones/SmallStone2.mesh + + + + + + + + + + + + + + Models/Props/Stones/SmallStone1.mesh + + + + + + + + + + + + + Models/Props/Stones/SmallStone2.mesh + + + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + + + Models/Props/Stones/BigStone.mesh + + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + + Models/Props/Stones/SmallStone1.mesh + + + + + + + + + + + + + + + + Models/Props/Stones/SmallStone2.mesh + + + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + + + Models/Props/Stones/MediumStone1.mesh + + + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + + + + + + + + + Models/Props/Stones/BigStone.mesh + + + + + + + + + + + + + Models/Props/Stones/SmallStone2.mesh + + + + + + + + + + + + + + Models/Props/Stones/MediumStone2.mesh + + + + + @@ -3087,7 +4090,7 @@ - + @@ -3100,9 +4103,9 @@ Models/Props/PickUps/PickUpHolder.mesh - - - + + + @@ -3115,7 +4118,7 @@ - + @@ -3128,8 +4131,31 @@ Models/Props/PickUps/PickUpHolder.mesh - - + + + + + + + + + + + + + + + + + + + + Models/Props/PickUps/PickUpHolder.mesh + + + + + @@ -3141,9 +4167,8 @@ Models/Props/PickUps/PickUpHolder.mesh - - - + + @@ -3155,8 +4180,37 @@ Models/Props/PickUps/PickUpHolder.mesh - - + + + + + + + + + + + + Models/Props/PickUps/PickUpHolder.mesh + + + + + + + + + + + + + + Models/Props/PickUps/PickUpHolder.mesh + + + + + @@ -3277,6 +4331,62 @@ + + + + + Models/Props/Stones/AssaultHolder.mesh + + + + + + + + + + + + + + Models/Props/Stones/AssaultHolder.mesh + + + + + + + + + + + + + + Models/Props/Stones/AssaultHolder.mesh + + + + + + + + + + + + + + Models/Props/Stones/AssaultHolder.mesh + + + + + + + + + @@ -3304,8 +4414,8 @@ Models/Props/SciFiHolder1.mesh - - + + @@ -3336,6 +4446,299 @@ + + + + + Models/Props/SciFiHolder1.mesh + + + + + + + + + + + + + Models/Props/SciFiHolder1.mesh + + + + + + + + + + + + + Models/Props/SciFiHolder1.mesh + + + + + + + + + + + + + Models/Props/SciFiHolder1.mesh + + + + + + + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalRed.mesh + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalRed.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalRed.mesh + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalBlue.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalBlue.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalBlue.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalBlue.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalBlue.mesh + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalRed.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalRed.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalBlue.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalBlue.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalRed.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalRed.mesh + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalBlue.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalBlue.mesh + + + + + + + + + + + + + + Models/Props/Stones/ShinyStoneCrystalRed.mesh + + + + + + + + + @@ -3352,7 +4755,7 @@ Models/Props/CapturePoint/CapturePointBlue.mesh - + @@ -3362,10 +4765,11 @@ + 4 Models/Core/UnitCylinder.mesh - + true @@ -3397,11 +4801,11 @@ - 1 + 3 Models/Core/UnitCylinder.mesh - + true @@ -3461,11 +4865,12 @@ - 3 + 1.5498908015879351 + 1 Models/Core/UnitCylinder.mesh - + true @@ -3483,7 +4888,7 @@ - Models/Props/CapturePoint/CapturePointNeutral.mesh + Models/Props/CapturePoint/CapturePointRed.mesh @@ -3496,11 +4901,10 @@ - 4 Models/Core/UnitCylinder.mesh - + true @@ -3596,29 +5000,18 @@ 1 - + - - - - Models/Characters/Assault/AssaultTPose.mesh - - - - - - - - Schema/Entities/Player.xml + Schema/Entities/PlayerRed.xml @@ -3626,7 +5019,7 @@ - + @@ -3635,9 +5028,10 @@ Models/Characters/Assault/AssaultTPose.mesh + false - + @@ -3647,9 +5041,10 @@ Models/Characters/Assault/AssaultTPose.mesh + false - + @@ -3659,9 +5054,10 @@ Models/Characters/Assault/AssaultTPose.mesh + false - + @@ -3671,159 +5067,240 @@ Models/Characters/Assault/AssaultTPose.mesh + false - + - + - - - Models/Props/Stones/MediumStone1.mesh - + + + Schema/Entities/Player.xml + + + + + + - - - + + + + + + + Models/Characters/Assault/AssaultTPose.mesh + false + + + + + + + + + + + + Models/Characters/Assault/AssaultTPose.mesh + false + + + + + + + + + + + + Models/Characters/Assault/AssaultTPose.mesh + false + + + + + + + + + + + + Models/Characters/Assault/AssaultTPose.mesh + false + + + + + + + + + + + + + - + - Models/Props/Stones/MediumStone1.mesh + Models/Props/PickUps/HealthPickUp.mesh + + 0.10000000149011612 + + - - + + + + - + - Models/Props/Stones/MediumStone1.mesh + Models/Props/PickUps/AmmoPickUp.mesh + + 0.10000000149011612 + + - - + + + + - + - Models/Props/Stones/MediumStone1.mesh + Models/Props/PickUps/HealthPickUp.mesh + + 0.10000000149011612 + + - - + + + + - + - Models/Props/Stones/MediumStone1.mesh + Models/Props/PickUps/HealthPickUp.mesh + + 0.10000000149011612 + + - - + + + + - + - Models/Props/Stones/MediumStone1.mesh + Models/Props/PickUps/AmmoPickUp.mesh + + 0.10000000149011612 + + - - + + + + - + - Models/Props/Stones/MediumStone1.mesh + Models/Props/PickUps/HealthPickUp.mesh + + 0.10000000149011612 + + - + + + + - + - Models/Props/Stones/MediumStone1.mesh + Models/Props/PickUps/AmmoPickUp.mesh + + 0.10000000149011612 + + - - + + + + - + - Models/Props/Stones/MediumStone1.mesh + Models/Props/PickUps/AmmoPickUp.mesh + + 0.10000000149011612 + + - + + + - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - - - Models/Props/Stones/MediumStone1.mesh - - - - - - - - - - - + diff --git a/resources/Schema/Entities/Player.xml b/resources/Schema/Entities/Player.xml index 9f8e0955..4f012955 100644 --- a/resources/Schema/Entities/Player.xml +++ b/resources/Schema/Entities/Player.xml @@ -23,9 +23,7 @@ - - - + @@ -37,20 +35,6 @@ - - - - Fonts/DroidSans.ttf,100 - - - - - - - - - - @@ -71,30 +55,12 @@ - - - - 1 - - - - - Models/Core/UnitHexagon.mesh - - - - - - - - - - Textures/Weapons/Crosshair/SmallThickHoleDot.png false + @@ -112,18 +78,299 @@ + + + + + + + + + 1 + + + + + 100/100 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + 1 + + + + Textures/HealthHUD3.png + + + + + + + + + + + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + 2 + + + + + + Textures/Core/UnitHexagon_Rotated.png + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + 3 + + + 0.80222018197612788 + + + + Textures/Core/UnitHexagon_Rotated.png + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + 4 + + + + + + Textures/Core/UnitHexagon_Rotated.png + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + 1 + + + + + + Textures/Core/UnitHexagon_Rotated.png + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon_Rotated.png + + + + + + + + + + + + + + + + + + + + + + + + + + + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + Idle - 0.1719161089749548 + 0.97725610639912475 1 + + Models/Characters/Assault/FirstPerson.mesh - true + @@ -135,11 +382,10 @@ Models/Weapons/Blue/AssaultWeaponBlue.mesh - true - - + + @@ -154,17 +400,76 @@ + + + + Schema/Entities/ReloadEffectView.xml + + + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + + + + + + + + 32 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + 360 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + - - - - Schema/Entities/WeaponReloadEffect.xml - - - - - @@ -187,8 +492,10 @@ Idle - 1.8038469763698401 + 0.87583812735846323 1 + + AimRifle @@ -209,11 +516,10 @@ Models/Weapons/Blue/AssaultWeaponBlue.mesh - false - - + + @@ -228,6 +534,15 @@ + + + + Schema/Entities/ReloadEffectWorld.xml + + + + + @@ -259,6 +574,41 @@ + + + + Insert name here + Fonts/DroidSans.ttf,100 + + + + + + + + + + + + + + Textures/Icons/Arrow.png + false + + + + + + 50 + true + + + + + + + + diff --git a/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml b/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml index 8a9f5e5b..3ec16e4e 100644 --- a/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml +++ b/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml @@ -2,9 +2,7 @@ - - Hold Pos - + 2.5 diff --git a/resources/Schema/Entities/PlayerRed.xml b/resources/Schema/Entities/PlayerRed.xml new file mode 100644 index 00000000..d56fa3c1 --- /dev/null +++ b/resources/Schema/Entities/PlayerRed.xml @@ -0,0 +1,615 @@ + + + + + + + + + + 600 + + + + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + + + + + + + + Textures/Weapons/Crosshair/SmallThickHoleDot.png + false + + + + + + + + + + + + + Schema/Entities/HitMarker.xml + + + + + + + + + + + + + + 1 + + + + + 100/100 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + 1 + + + + Textures/HealthHUD3.png + + + + + + + + + + + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + 2 + + + + + + Textures/Core/UnitHexagon_Rotated.png + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + 3 + + + 0.80222018197612788 + + + + Textures/Core/UnitHexagon_Rotated.png + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + 4 + + + + + + Textures/Core/UnitHexagon_Rotated.png + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + 1 + + + + + + Textures/Core/UnitHexagon_Rotated.png + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon_Rotated.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + + + Idle + 1.2667383999985162 + 1 + + + + + Models/Characters/Assault/FirstPerson.mesh + + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Red/AssaultWeaponRed.mesh + + + + + + + + + + + Schema/Entities/RayRed.xml + + + + + + + + + + + Schema/Entities/ReloadEffectViewRed.xml + + + + + + + + + + + + + + + + + + Textures/Core/UnitHexagon.png + + + + + + + + + + + + + + + + + + + 32 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + 360 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + Idle + 0.26532318661337229 + 1 + + + + + AimRifle + + + + + Models/Characters/Assault/AssaultAnimations.mesh + + + + + + + + + R_Arm_Weapon_Joint + + + Models/Weapons/Red/AssaultWeaponRed.mesh + + + + + + + + + + + Schema/Entities/RayRed.xml + + + + + + + + + + + Schema/Entities/ReloadEffectWorldRed.xml + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + Insert name here + Fonts/DroidSans.ttf,100 + + + + + + + + + + + + + + Textures/Icons/Arrow.png + false + + + + + + 50 + true + + + + + + + + + + + diff --git a/resources/Schema/Entities/QualityAssurance.xml b/resources/Schema/Entities/QualityAssurance.xml index 40951468..0aa2fbe7 100644 --- a/resources/Schema/Entities/QualityAssurance.xml +++ b/resources/Schema/Entities/QualityAssurance.xml @@ -40,7 +40,7 @@ - + @@ -77,18 +77,6 @@ - - - - Sound Test - Fonts/DroidSans.ttf,64 - - - - - - - @@ -105,7 +93,7 @@ - + @@ -180,7 +168,7 @@ - + @@ -225,7 +213,7 @@ - + @@ -289,7 +277,7 @@ - + @@ -321,7 +309,7 @@ - + @@ -671,7 +659,7 @@ - + @@ -718,7 +706,7 @@ - + @@ -778,7 +766,7 @@ - + @@ -825,7 +813,7 @@ - + @@ -871,7 +859,7 @@ - + @@ -918,7 +906,7 @@ - + @@ -965,7 +953,7 @@ - + @@ -1027,7 +1015,7 @@ Models/Core/UnitCube.mesh - + true @@ -1077,7 +1065,7 @@ Models/Core/UnitCube.mesh - + true @@ -1167,7 +1155,7 @@ Models/Core/UnitCube.mesh - + true @@ -1217,7 +1205,7 @@ Models/Core/UnitCube.mesh - + true @@ -1379,7 +1367,7 @@ - + @@ -1388,7 +1376,7 @@ true - 0.75205058136495551 + 0.8256214817261025 3.7999999523162842 true @@ -1435,7 +1423,7 @@ - + @@ -1444,7 +1432,7 @@ - 1.2019563319790627 + 1.8641349174045843 Models/Characters/Assault/AssaultTPose.mesh @@ -1487,7 +1475,7 @@ - + @@ -1498,7 +1486,7 @@ true - 0.68540211563899389 + 1.8641349174045843 true @@ -1543,7 +1531,7 @@ - + @@ -1553,7 +1541,7 @@ true - 0.95150063648635763 + 1.2301962937648341 10 3 @@ -1601,7 +1589,7 @@ - + @@ -1611,7 +1599,7 @@ true - 1.3515288978624223 + 3.4214855659573402 true 5 true @@ -1678,10 +1666,10 @@ + Models/Core/UnitCube.mesh - @@ -1792,7 +1780,7 @@ true - 1.3682019578975679 + 0.8256214817261025 3.7999999523162842 true @@ -1836,12 +1824,12 @@ + Models/AssaultAnimated.mesh - @@ -2095,7 +2083,7 @@ Textures/Core/UnitHexagon.png - + @@ -2107,7 +2095,7 @@ 1 - + Textures/Core/UnitHexagon_Rotated.png @@ -2145,6 +2133,355 @@ + + + + + + + + + + + + Models/Widgets/Camera.mesh + false + + + + + + + + + + + + + + + + + + + + + Textures/Core/ErrorTexture.png + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Play + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Host + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Connect + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Settings + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Quit + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + Textures/Core/ErrorTexture.png + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + 1920x1080 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + 1280x720 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + 854x480 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + FullScreen + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + + + + + + Menu test area + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + Sound Test + Fonts/DroidSans.ttf,64 + + + + + + + diff --git a/resources/Schema/Entities/RayRed.xml b/resources/Schema/Entities/RayRed.xml index 11a9b077..0a20f148 100644 --- a/resources/Schema/Entities/RayRed.xml +++ b/resources/Schema/Entities/RayRed.xml @@ -6,12 +6,12 @@ 0.25 - Models/Weapons/CylinderBullet.mesh - + Models/Effects/CylinderShot.mesh + true - + diff --git a/resources/Schema/Entities/WeaponReloadEffect.xml b/resources/Schema/Entities/ReloadEffectView.xml similarity index 72% rename from resources/Schema/Entities/WeaponReloadEffect.xml rename to resources/Schema/Entities/ReloadEffectView.xml index 3099b405..ca7e6e1a 100644 --- a/resources/Schema/Entities/WeaponReloadEffect.xml +++ b/resources/Schema/Entities/ReloadEffectView.xml @@ -2,9 +2,6 @@ - - R_Arm_Weapon_Joint - 2 @@ -18,12 +15,8 @@ Models/Weapons/Blue/AssaultWeaponBlue.mesh - true - - - - + diff --git a/resources/Schema/Entities/ReloadEffectViewRed.xml b/resources/Schema/Entities/ReloadEffectViewRed.xml new file mode 100644 index 00000000..56b9d140 --- /dev/null +++ b/resources/Schema/Entities/ReloadEffectViewRed.xml @@ -0,0 +1,24 @@ + + + + + + 2 + + + true + + + true + + true + + + Models/Weapons/Red/AssaultWeaponRed.mesh + + + + + + + diff --git a/resources/Schema/Entities/ReloadEffectWorld.xml b/resources/Schema/Entities/ReloadEffectWorld.xml new file mode 100644 index 00000000..ae6d3a3e --- /dev/null +++ b/resources/Schema/Entities/ReloadEffectWorld.xml @@ -0,0 +1,25 @@ + + + + + + 2 + + + true + + + true + + true + + + Models/Weapons/Blue/AssaultWeaponBlue.mesh + true + + + + + + + diff --git a/resources/Schema/Entities/ReloadEffectWorldRed.xml b/resources/Schema/Entities/ReloadEffectWorldRed.xml new file mode 100644 index 00000000..55c37389 --- /dev/null +++ b/resources/Schema/Entities/ReloadEffectWorldRed.xml @@ -0,0 +1,25 @@ + + + + + + 2 + + + true + + + true + + true + + + Models/Weapons/Red/AssaultWeaponRed.mesh + true + + + + + + + diff --git a/resources/Schema/Entities/TestMenu.xml b/resources/Schema/Entities/TestMenu.xml new file mode 100644 index 00000000..73678601 --- /dev/null +++ b/resources/Schema/Entities/TestMenu.xml @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + Models/Widgets/Camera.mesh + + + + + + + + + + + + + + + + + + + + + Textures/Core/ErrorTexture.png + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Play + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Host + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Connect + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Settings + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Quit + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + Textures/Core/ErrorTexture.png + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Resolution + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Option2 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + Textures/Core/White.png + + + + + + + + + + + Butts + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/temp b/resources/Schema/Entities/temp new file mode 100644 index 00000000..baf4ce61 --- /dev/null +++ b/resources/Schema/Entities/temp @@ -0,0 +1,38 @@ + + + + + + + + + + + + 0 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + 360 + Fonts/DroidSans.ttf,64 + + + + + + + + + + + + diff --git a/resources/Schema/Types/Entity.xsd b/resources/Schema/Types/Entity.xsd index 19eded09..4fba7420 100644 --- a/resources/Schema/Types/Entity.xsd +++ b/resources/Schema/Types/Entity.xsd @@ -46,6 +46,13 @@ + + + + + + + diff --git a/resources/Shaders/DrawColorCorrection.frag.glsl b/resources/Shaders/DrawColorCorrection.frag.glsl index 76db3e82..bae50887 100644 --- a/resources/Shaders/DrawColorCorrection.frag.glsl +++ b/resources/Shaders/DrawColorCorrection.frag.glsl @@ -19,6 +19,8 @@ void main() vec4 bloomColor = texture(BloomTexture, Input.TextureCoordinate); vec4 hdrColorLowRes = texture(SceneTextureLowRes, Input.TextureCoordinate); vec4 bloomColorLowRes = texture(BloomTextureLowRes, Input.TextureCoordinate); + + //hdrColor = hdrColor * SSAO; hdrColor += bloomColor; hdrColorLowRes; @@ -33,7 +35,6 @@ void main() //gamme correction result = pow(result, vec3(1.0 / Gamma)); - fragmentColor = vec4(result, 1.0); //fragmentColor = hdrColor; //fragmentColor = bloomColor; diff --git a/resources/Shaders/ForwardPlus.frag.glsl b/resources/Shaders/ForwardPlus.frag.glsl index 471ee20b..6fbc9c27 100644 --- a/resources/Shaders/ForwardPlus.frag.glsl +++ b/resources/Shaders/ForwardPlus.frag.glsl @@ -1,5 +1,7 @@ #version 430 +#define MIN_AMBIENT_LIGHT 0.3 + uniform mat4 M; uniform mat4 V; uniform mat4 P; @@ -9,15 +11,19 @@ uniform vec2 ScreenDimensions; uniform vec4 FillColor; uniform vec4 AmbientColor; uniform float FillPercentage; +uniform float GlowIntensity = 10; +uniform vec3 CameraPosition; uniform vec2 DiffuseUVRepeat; uniform vec2 NormalUVRepeat; uniform vec2 SpecularUVRepeat; uniform vec2 GlowUVRepeat; -layout (binding = 0) uniform sampler2D DiffuseTexture; -layout (binding = 1) uniform sampler2D NormalMapTexture; -layout (binding = 2) uniform sampler2D SpecularMapTexture; -layout (binding = 3) uniform sampler2D GlowMapTexture; +layout (binding = 0) uniform sampler2D AOTexture; +layout (binding = 1) uniform sampler2D DiffuseTexture; +layout (binding = 2) uniform sampler2D NormalMapTexture; +layout (binding = 3) uniform sampler2D SpecularMapTexture; +layout (binding = 4) uniform sampler2D GlowMapTexture; +layout (binding = 5) uniform samplerCube CubeMap; #define TILE_SIZE 16 @@ -72,7 +78,7 @@ struct LightResult { }; float CalcAttenuation(float radius, float dist, float falloff) { - return 1.0 - smoothstep(radius * 0.3, radius, dist); + return 1.0 - smoothstep(radius * falloff, radius, dist); } vec4 CalcSpecular(vec4 lightColor, vec4 viewVec, vec4 lightVec, vec4 normal) { @@ -119,6 +125,8 @@ vec4 CalcNormalMappedValue(vec3 normal, vec3 tangent, vec3 bitangent, vec2 textu void main() { + float ao = texelFetch(AOTexture, ivec2(gl_FragCoord.xy), 0).r; + ao = (clamp(1.0 - (1.0 - ao), 0.0, 1.0) + MIN_AMBIENT_LIGHT) / (1.0 + MIN_AMBIENT_LIGHT); vec4 diffuseTexel = texture2D(DiffuseTexture, Input.TextureCoordinate * DiffuseUVRepeat); vec4 glowTexel = texture2D(GlowMapTexture, Input.TextureCoordinate * GlowUVRepeat); vec4 specularTexel = texture2D(SpecularMapTexture, Input.TextureCoordinate * SpecularUVRepeat); @@ -126,14 +134,18 @@ void main() vec4 normal = V * CalcNormalMappedValue(Input.Normal, Input.Tangent, Input.BiTangent, Input.TextureCoordinate * NormalUVRepeat, NormalMapTexture); normal = normalize(normal); //vec4 normal = normalize(V * vec4(Input.Normal, 0.0)); - vec4 viewVec = normalize(-position); + vec4 viewVec = normalize(-position); + vec3 I = normalize(vec3(M * vec4(Input.Position, 1.0)) - CameraPosition); + vec3 R = reflect(-I, Input.Normal); + //R = vec3(P * vec4(R, 1.0)); + vec4 reflectionColor = texture(CubeMap, R); vec2 tilePos; tilePos.x = int(gl_FragCoord.x/TILE_SIZE); tilePos.y = int(gl_FragCoord.y/TILE_SIZE); LightResult totalLighting; - totalLighting.Diffuse = vec4(AmbientColor.rgb, 1.0); + totalLighting.Diffuse = vec4(AmbientColor.rgb * ao, 1.0); int currentTile = int(floor(gl_FragCoord.x/TILE_SIZE) + (floor(gl_FragCoord.y/TILE_SIZE) * int(ScreenDimensions.x/TILE_SIZE))); int start = int(LightGrids.Data[currentTile].Start); @@ -151,12 +163,14 @@ void main() } else if (light.Type == 2) { //Directional light_result = CalcDirectionalLightSource(V * light.Direction, light.Color, light.Intensity, viewVec, normal); } - totalLighting.Diffuse += light_result.Diffuse; - totalLighting.Specular += light_result.Specular; + totalLighting.Diffuse += vec4(light_result.Diffuse.rgb * ao, light_result.Diffuse.a); + totalLighting.Specular += vec4(light_result.Specular.rgb * ao, light_result.Specular.a); } vec4 color_result = mix((Color * diffuseTexel * DiffuseColor), Input.ExplosionColor, Input.ExplosionPercentageElapsed); color_result = color_result * (totalLighting.Diffuse + (totalLighting.Specular * specularTexel)); + float specularResult = (specularTexel.r + specularTexel.g + specularTexel.b)/3.0; + color_result = color_result * clamp(1/specularTexel, 0, 1)*2 + reflectionColor * clamp(specularTexel, 0, 1)/2; //vec4 color_result = (DiffuseColor + Input.ExplosionColor) * (totalLighting.Diffuse + (totalLighting.Specular * specularTexel)) * diffuseTexel * Color; @@ -166,9 +180,10 @@ void main() color_result += FillColor; } sceneColor = vec4(color_result.xyz, clamp(color_result.a, 0, 1)); - color_result += glowTexel*3; + //sceneColor = vec4(reflectionColor.xyz, 1); + color_result += glowTexel*GlowIntensity; - bloomColor = vec4(clamp(color_result.xyz - 1.0, 0, 100), 1.0); + bloomColor = vec4(clamp(color_result.xyz - 1.0, 0, 100), clamp(color_result.a, 0, 1)); //Tiled Debug Code /* diff --git a/resources/Shaders/ForwardPlus.vert.glsl b/resources/Shaders/ForwardPlus.vert.glsl index d475d825..26686222 100644 --- a/resources/Shaders/ForwardPlus.vert.glsl +++ b/resources/Shaders/ForwardPlus.vert.glsl @@ -23,12 +23,12 @@ out VertexData{ void main() { gl_Position = P*V*M * vec4(Position, 1.0); - + mat4 TIM = transpose(inverse(M)); Output.Position = Position; Output.TextureCoordinate = TextureCoords; - Output.Normal = vec3(M * vec4(Normal, 0.0)); - Output.Tangent = vec3(M * vec4(Tangent, 0.0)); - Output.BiTangent = vec3(M * vec4(BiTangent, 0.0)); + Output.Normal = vec3(TIM * vec4(Normal, 0.0)); + Output.Tangent = vec3(TIM * vec4(Tangent, 0.0)); + Output.BiTangent = vec3(TIM * vec4(BiTangent, 0.0)); Output.ExplosionColor = vec4(1.0); Output.ExplosionPercentageElapsed = 0.0; } \ No newline at end of file diff --git a/resources/Shaders/ForwardPlusSplatMapRGB.frag.glsl b/resources/Shaders/ForwardPlusSplatMapRGB.frag.glsl index e862d926..cf358b96 100644 --- a/resources/Shaders/ForwardPlusSplatMapRGB.frag.glsl +++ b/resources/Shaders/ForwardPlusSplatMapRGB.frag.glsl @@ -1,5 +1,7 @@ #version 430 +#define MIN_AMBIENT_LIGHT 0.3 + uniform mat4 M; uniform mat4 V; uniform mat4 P; @@ -23,19 +25,20 @@ uniform vec2 SpecularUVRepeat3; uniform vec2 GlowUVRepeat1; uniform vec2 GlowUVRepeat2; uniform vec2 GlowUVRepeat3; -layout (binding = 0) uniform sampler2D SplatMapTexture; -layout (binding = 1) uniform sampler2D DiffuseTexture1; -layout (binding = 2) uniform sampler2D DiffuseTexture2; -layout (binding = 3) uniform sampler2D DiffuseTexture3; -layout (binding = 4) uniform sampler2D NormalMapTexture1; -layout (binding = 5) uniform sampler2D NormalMapTexture2; -layout (binding = 6) uniform sampler2D NormalMapTexture3; -layout (binding = 7) uniform sampler2D SpecularMapTexture1; -layout (binding = 8) uniform sampler2D SpecularMapTexture2; -layout (binding = 9) uniform sampler2D SpecularMapTexture3; -layout (binding = 10) uniform sampler2D GlowMapTexture1; -layout (binding = 11) uniform sampler2D GlowMapTexture2; -layout (binding = 12) uniform sampler2D GlowMapTexture3; +layout (binding = 0) uniform sampler2D AOTexture; +layout (binding = 1) uniform sampler2D SplatMapTexture; +layout (binding = 2) uniform sampler2D DiffuseTexture1; +layout (binding = 3) uniform sampler2D DiffuseTexture2; +layout (binding = 4) uniform sampler2D DiffuseTexture3; +layout (binding = 5) uniform sampler2D NormalMapTexture1; +layout (binding = 6) uniform sampler2D NormalMapTexture2; +layout (binding = 7) uniform sampler2D NormalMapTexture3; +layout (binding = 8) uniform sampler2D SpecularMapTexture1; +layout (binding = 9) uniform sampler2D SpecularMapTexture2; +layout (binding = 10) uniform sampler2D SpecularMapTexture3; +layout (binding = 11) uniform sampler2D GlowMapTexture1; +layout (binding = 12) uniform sampler2D GlowMapTexture2; +layout (binding = 13) uniform sampler2D GlowMapTexture3; #define TILE_SIZE 16 @@ -174,6 +177,9 @@ vec4 CalcBlendedNormal(vec4 blendValue, sampler2D R, sampler2D G, sampler2D B, void main() { + float ao = texelFetch(AOTexture, ivec2(gl_FragCoord.xy), 0).r; + ao = (clamp(1.0 - (1.0 - ao), 0.0, 1.0) + MIN_AMBIENT_LIGHT) / (1.0 + MIN_AMBIENT_LIGHT); + vec4 splatTexel = texture2D(SplatMapTexture, Input.TextureCoordinate); vec4 diffuseTexel = CalcBlendedTexel(splatTexel, DiffuseTexture1, DiffuseTexture2, DiffuseTexture3, @@ -195,7 +201,7 @@ void main() tilePos.y = int(gl_FragCoord.y/TILE_SIZE); LightResult totalLighting; - totalLighting.Diffuse = vec4(AmbientColor.rgb, 1.0); + totalLighting.Diffuse = vec4(AmbientColor.rgb * ao, 1.0); int currentTile = int(floor(gl_FragCoord.x/TILE_SIZE) + (floor(gl_FragCoord.y/TILE_SIZE) * int(ScreenDimensions.x/TILE_SIZE))); int start = int(LightGrids.Data[currentTile].Start); @@ -213,8 +219,8 @@ void main() } else if (light.Type == 2) { //Directional light_result = CalcDirectionalLightSource(V * light.Direction, light.Color, light.Intensity, viewVec, normal); } - totalLighting.Diffuse += light_result.Diffuse; - totalLighting.Specular += light_result.Specular; + totalLighting.Diffuse += vec4(light_result.Diffuse.rgb * ao, light_result.Diffuse.a); + totalLighting.Specular += vec4(light_result.Specular.rgb * ao, light_result.Specular.a); } vec4 color_result = mix((Color * diffuseTexel * DiffuseColor), Input.ExplosionColor, Input.ExplosionPercentageElapsed); diff --git a/resources/Shaders/SSAO.frag.glsl b/resources/Shaders/SSAO.frag.glsl new file mode 100644 index 00000000..68c830f8 --- /dev/null +++ b/resources/Shaders/SSAO.frag.glsl @@ -0,0 +1,114 @@ +#version 430 + +//Number of samples per pixel +uniform int uNumOfSamples; +//#define NUM_SAMPLES (11) + +//Number of turns around the cirle +uniform int uNumOfTurns; +//#define NUM_TURNS (7) + +layout (binding = 0) uniform sampler2D ViewSpaceZ; + +uniform vec4 uProjInfo; + +uniform float uProjScale; +//#define ProjScale 500 + +uniform float uRadius; +//#define Radius 1.0f + +uniform float uBias; +//#define Bias 0.012f + +uniform float uContrast; +//#define IntensityDivR6 1 + +uniform float uIntensityScale; + +out float AO; + +vec3 getVSPosition(ivec2 ScreenSpaceCoord) { + float z = texelFetch(ViewSpaceZ, ScreenSpaceCoord, 0).r; + //Get the xy view space coordinates and add the z value from ViewSpaceZ buffer. + return vec3((uProjInfo[0] + (ScreenSpaceCoord.x * uProjInfo[1])) * z, (uProjInfo[2] + (ScreenSpaceCoord.y * uProjInfo[3])) * z, z); +} + +vec3 getVSFaceNormal(vec3 ViewSpacePosition) { + // Get tangets vector for the plane and ViewSpacePositin... don't ask how this functions works. It's pure magic. + // They do this and it just works... I would guess that they approximate the function of a plane from pixels close to the pixel were on now. + return normalize(cross(dFdx(ViewSpacePosition), dFdy(ViewSpacePosition))); +} + + +vec3 getSampleViewSpacePos(ivec2 ScreenSpaceCoord, int SampleIndex, float RotationAngle, float ScreenSpaceSampleRadius){ + // Pure Magic... + float alpha = float(SampleIndex) * (1.0 / uNumOfSamples); + + // Angle to where to sample + float angle = alpha * (uNumOfTurns * 6.28) + RotationAngle; + + //Lenght to were to sample + ScreenSpaceSampleRadius = ScreenSpaceSampleRadius * alpha; + + vec2 screenSpaceSampleOffsetVecor = vec2(cos(angle), sin(angle)); + + // Get texel coordinate on where to sample by going screenSpaceSampleOffsetVecor direction in ScreenSpaceSampleRadius units from ScreenSpaceCoord (the point being shaded); + ivec2 screenSpaceSampleTexel = ivec2(ScreenSpaceSampleRadius * screenSpaceSampleOffsetVecor) + ScreenSpaceCoord; + + return getVSPosition(screenSpaceSampleTexel); +} + + + +float sampleAO(ivec2 ScreenSpaceCoord, vec3 Origin, vec3 OriginNormal, float ScreenSpaceSampleRadius, int SampleIndex, float RotationAngle, float Radius) { + float radius2 = Radius * Radius; + vec3 sampleViewSpacePosition = getSampleViewSpacePos(ScreenSpaceCoord, SampleIndex, RotationAngle, ScreenSpaceSampleRadius); + + vec3 sampleVector = Origin - sampleViewSpacePosition; + + // vv = sampleVectorLenght ^ 2 + float vv = dot(sampleVector, sampleVector); + // vn = angle between sampleVector and Normal + float vn = dot(sampleVector, OriginNormal); + + const float epsilon = 0.0001f; + + // vv < radius2 if the vector is shorter then the radius; + // vn - bias, offset the angle to reduse self occlusion. + // epsilon is here to make divison by 0 impossible. + return float(vv < radius2) * max((vn - uBias) / (epsilon + vv), 0.0); + //float f = max(radius2 - vv, 0.0); + //return f * f * f * max((vn - uBias) / (epsilon + vv), 0.0); +} + + +void main() { + ivec2 originScreenCoord = ivec2(gl_FragCoord.xy); + + vec3 origin = getVSPosition(originScreenCoord); + + float radius; + if(origin.z < uRadius){ + radius = origin.z; + } else { + radius = uRadius; + } + + + vec3 originNormal = getVSFaceNormal(origin); + + float screenSpaceSampleRadius = -uProjScale * radius / origin.z; + + float rotationAngleOffset = 30 * originScreenCoord.x ^ originScreenCoord.y + 10 * originScreenCoord.x * originScreenCoord.y; + + float sum = 0.0; + for (int i = 0; i < uNumOfSamples; i++) { + sum += sampleAO(originScreenCoord, origin, originNormal, screenSpaceSampleRadius, i, rotationAngleOffset, radius); + } + + //float A = max(0.0, 1.0 - sum * (2.0f / uNumOfSamples)); + float A = 1.0 - sum * (2.0f * uIntensityScale / float(uNumOfSamples)); + AO = clamp(pow(A, uContrast), 0.0f, 1.0f); + //AO = vec4(originNormal, 1.0f); +} diff --git a/resources/Shaders/SSAO.vert.glsl b/resources/Shaders/SSAO.vert.glsl new file mode 100644 index 00000000..a019c5ef --- /dev/null +++ b/resources/Shaders/SSAO.vert.glsl @@ -0,0 +1,8 @@ +#version 430 + +layout (location = 0) in vec3 Position; + +void main() +{ + gl_Position = vec4(Position, 1.0); +} \ No newline at end of file diff --git a/resources/Shaders/SSAOViewSpaceZ.frag.glsl b/resources/Shaders/SSAOViewSpaceZ.frag.glsl new file mode 100644 index 00000000..dbcfd899 --- /dev/null +++ b/resources/Shaders/SSAOViewSpaceZ.frag.glsl @@ -0,0 +1,14 @@ +#version 430 + +layout (binding = 0) uniform sampler2D DepthBuffer; +uniform vec3 ClipInfo; + +out float depthLinear; +//Just for Debug, should be depthLinear +//out vec4 fragmentColor; +void main() { + float depthSample = texelFetch(DepthBuffer, ivec2(gl_FragCoord.xy), 0).r; + depthLinear = ClipInfo[0] / (ClipInfo[1] * depthSample + ClipInfo[2]); + //float depthLinear = (NearClip) / ( -depthSample + 1.0f); + //fragmentColor = vec4(depthLinear, depthLinear, depthLinear, 1.0f); +} \ No newline at end of file diff --git a/resources/Shaders/Sprite.frag.glsl b/resources/Shaders/Sprite.frag.glsl index 9ce2bbdf..754be6ac 100644 --- a/resources/Shaders/Sprite.frag.glsl +++ b/resources/Shaders/Sprite.frag.glsl @@ -7,8 +7,8 @@ uniform mat4 M; uniform mat4 V; uniform mat4 P; -layout (binding = 0) uniform sampler2D DiffuseTexture; -layout (binding = 1) uniform sampler2D GlowMapTexture; +layout (binding = 1) uniform sampler2D DiffuseTexture; +layout (binding = 2) uniform sampler2D GlowMapTexture; in VertexData{ diff --git a/src/Engine/CMakeLists.txt b/src/Engine/CMakeLists.txt index 19763310..e74214cf 100644 --- a/src/Engine/CMakeLists.txt +++ b/src/Engine/CMakeLists.txt @@ -3,7 +3,7 @@ project(TacticalZ-Engine) find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) find_package(GLFW REQUIRED) -find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono program_options) +find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono timer program_options) find_package(assimp REQUIRED) find_package(ZLIB REQUIRED) find_package(PNG REQUIRED) diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index ab2098b7..68d99d92 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -207,7 +207,7 @@ bool RayVsModel(const Ray& ray, glm::vec3 v0 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix); glm::vec3 v1 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix); glm::vec3 v2 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix); - float dist = INFINITY; + float dist = outDistance; float u; float v; if (RayVsTriangle(ray, v0, v1, v2, dist, u, v)) { @@ -366,7 +366,8 @@ bool AABBvsTriangle(const AABB& box, float verticalStepHeight, bool& isOnGround, glm::vec3& boxVelocity, - glm::vec3& outResolution) + glm::vec3& outResolution, + bool resolveCollision) { //Check so we don't have a zero area triangle when calculating the normal. //Also, don't check a triangle facing away from the player. @@ -426,7 +427,7 @@ bool AABBvsTriangle(const AABB& box, //if projections don't overlap, return false. if (!rectangleVsTriangle(boxMin, boxMax, t2D, resolutionVector, resolutionDist, pushedFromTriangleLine)) { return false; - } else { + } else if (resolveCollision) { //Overwrite the smallest resolution if this is smaller. if (resolutionDist < resolveShortest.DistanceSq) { resolveShortest.Vector = glm::vec3(0.f); @@ -463,6 +464,11 @@ bool AABBvsTriangle(const AABB& box, if (glm::abs(t) > 1) { return false; } + + if (!resolveCollision) { + return true; + } + glm::vec3 cornerResolution = (1+t) * diagonal; //Overwrite the smallest resolution if cornerResolution is smaller. float lenSq = glm::length2(cornerResolution); @@ -537,7 +543,8 @@ bool AABBvsTriangles(const AABB& box, glm::vec3& boxVelocity, float verticalStepHeight, bool& isOnGround, - glm::vec3& outResolutionVector) + glm::vec3& outResolutionVector, + bool resolveCollision) { bool hit = false; @@ -553,7 +560,7 @@ bool AABBvsTriangles(const AABB& box, }; glm::vec3 outVec; bool collideWithGround = isOnGround; - if (AABBvsTriangle(newBox, triVertices, originalBoxVelocity, verticalStepHeight, collideWithGround, boxVelocity, outVec)) { + if (AABBvsTriangle(newBox, triVertices, originalBoxVelocity, verticalStepHeight, collideWithGround, boxVelocity, outVec, resolveCollision)) { hit = true; outResolutionVector += outVec; newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); @@ -569,6 +576,44 @@ bool AABBvsTriangles(const AABB& box, return hit; } +bool AABBvsTriangles(const AABB& box, + const RawModel::Vertex* modelVertices, + const std::vector& modelIndices, + const glm::mat4& modelMatrix, + glm::vec3& boxVelocity, + float verticalStepHeight, + bool& isOnGround, + glm::vec3& outResolutionVector) +{ + return AABBvsTriangles(box, + modelVertices, + modelIndices, + modelMatrix, + boxVelocity, + verticalStepHeight, + isOnGround, + outResolutionVector, + true); +} + +bool AABBvsTriangles(const AABB& box, + const RawModel::Vertex* modelVertices, + const std::vector& modelIndices, + const glm::mat4& modelMatrix) +{ + glm::vec3 vel, outres; + bool g; + return AABBvsTriangles(box, + modelVertices, + modelIndices, + modelMatrix, + vel, + 0.f, + g, + outres, + false); +} + boost::optional EntityAbsoluteAABB(EntityWrapper& entity, bool takeModelBox) { AABB modelSpaceBox; @@ -648,8 +693,9 @@ boost::optional EntityFirstHitByRay(const Ray& ray, std::vector boundingBox = Collision::EntityAbsoluteAABB(entity); if (!boundingBox) { return; } ComponentWrapper& cTransform = entity["Transform"]; EntityAABB& boxA = *boundingBox; + bool everHitTheGround = false; + + auto prevPosIt = m_PrevPositions.find(entity); + if (prevPosIt != m_PrevPositions.end()) { + glm::vec3 size = boxA.Size(); + float diameter = std::min(size.x, size.z); + glm::vec3 prevOrigin = prevPosIt->second; + glm::vec3 toCurrentPos = boxA.Origin() - prevOrigin; + float rayLength = glm::length(toCurrentPos) + 0.5f*diameter; + //If the entity has moved farther than the size of its box, we need to handle it specially. + if (rayLength > diameter) { + Ray ray(prevOrigin, toCurrentPos); + m_OctreeResult.clear(); + m_Octree->ObjectsPossiblyHitByRay(ray, m_OctreeResult); + for (auto& boxB : m_OctreeResult) { + if (boxA.Entity == boxB.Entity) { + continue; + } + bool hit; + float dist; + if (boxB.Entity.HasComponent("Model")) { + RawModel* model; + std::string res = (std::string)boxB.Entity["Model"]["Resource"]; + try { + model = ResourceManager::Load(res); + } catch (const std::exception&) { + continue; + } + float u, v; + hit = Collision::RayVsModel(ray, model->Vertices(), model->m_Indices, Transform::ModelMatrix(boxB.Entity), dist, u, v); + } else { + hit = Collision::RayVsAABB(ray, boxB, dist); + } + if (hit && dist < rayLength) { + //Set the entity to where it was colliding, minus the maximum box size. + //TODO: Perhaps this should be done slightly more properly. + glm::vec3 newOriginPos = ray.Origin() + (dist - 0.707107f*diameter) * ray.Direction(); + glm::vec3 resolve = newOriginPos - boxA.Origin(); + (glm::vec3&)cTransform["Position"] += resolve; + boxA = *Collision::EntityAbsoluteAABB(entity); + if (resolve.y > 0) { + everHitTheGround = true; + (bool)cPhysics["IsOnGround"] = true; + ((glm::vec3&)cPhysics["Velocity"]).y = 0.f; + } + break; + } + } + } + } // Collide against octree items m_OctreeResult.clear(); m_Octree->ObjectsInSameRegion(*boundingBox, m_OctreeResult); - bool everHitTheGround = false; for (auto& boxB : m_OctreeResult) { glm::vec3 resolutionVector; if (boxA.Entity == boxB.Entity) { @@ -43,6 +90,7 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c float verticalStepHeight = (float)(double)cPhysics["VerticalStepHeight"]; if (Collision::AABBvsTriangles(boxA, model->Vertices(), model->m_Indices, modelMatrix, inOutVelocity, verticalStepHeight, isOnGround, resolutionVector)) { (glm::vec3&)cTransform["Position"] += resolutionVector; + boxA = *Collision::EntityAbsoluteAABB(entity); cPhysics["Velocity"] = inOutVelocity; if (isOnGround) { everHitTheGround = true; @@ -52,6 +100,7 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c } else if (Collision::AABBVsAABB(boxA, boxB, resolutionVector)) { //Enter here if boxB has no Model. (glm::vec3&)cTransform["Position"] += resolutionVector; + boxA = *Collision::EntityAbsoluteAABB(entity); if (resolutionVector.y > 0) { everHitTheGround = true; (bool)cPhysics["IsOnGround"] = true; @@ -64,4 +113,6 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c if (!everHitTheGround) { (bool)cPhysics["IsOnGround"] = false; } + + m_PrevPositions[entity] = boxA.Origin(); } diff --git a/src/Engine/Core/ComponentPool.cpp b/src/Engine/Core/ComponentPool.cpp index 7b465fbc..059b2e38 100644 --- a/src/Engine/Core/ComponentPool.cpp +++ b/src/Engine/Core/ComponentPool.cpp @@ -1,7 +1,5 @@ #include "Core/ComponentPool.h" - - ComponentWrapper ComponentPoolForwardIterator::operator*() const { char* data = &(*m_MemoryPoolIterator); @@ -32,6 +30,34 @@ ComponentPoolForwardIterator& ComponentPoolForwardIterator::operator++() return *this; } +ComponentPool::ComponentPool(const ComponentPool& other) + : m_ComponentInfo(other.m_ComponentInfo) + , m_Pool(other.m_Pool) + , m_EntityToComponent() +{ + // Update EntityToComponent pointers + for (char& ptr : m_Pool) { + EntityID entity = *reinterpret_cast(&ptr); + m_EntityToComponent[entity] = &ptr; + } + + // Duplicate strings + for (auto& name : m_ComponentInfo.StringFields) { + for (auto& c : *this) { + std::string& val = c[name]; + ComponentWrapper::SolidifyStrings(c); + } + } +} + +ComponentPool::~ComponentPool() +{ + // Destroy component data + for (auto& c : *this) { + ComponentWrapper::Destroy(c.Info, c.Data); + } +} + //const ::ComponentInfo& ComponentPool::ComponentInfo() const //{ // return m_ComponentInfo; @@ -39,10 +65,19 @@ ComponentPoolForwardIterator& ComponentPoolForwardIterator::operator++() ComponentWrapper ComponentPool::Allocate(EntityID entity) { + // Allocate pool data char* data = m_Pool.Allocate(); + // Copy EntityID memcpy(data, &entity, sizeof(EntityID)); + m_EntityToComponent[entity] = data; - return ComponentWrapper(m_ComponentInfo, data); + ComponentWrapper component(m_ComponentInfo, data); + + // Copy defaults + memcpy(component.Data, m_ComponentInfo.Defaults.get(), m_ComponentInfo.Stride); + ComponentWrapper::SolidifyStrings(component); + + return component; } ComponentWrapper ComponentPool::GetByEntity(EntityID ent) @@ -57,6 +92,7 @@ bool ComponentPool::KnowsEntity(EntityID ent) void ComponentPool::Delete(ComponentWrapper& wrapper) { + ComponentWrapper::Destroy(wrapper.Info, wrapper.Data); m_EntityToComponent.erase(wrapper.EntityID); m_Pool.Free(wrapper.Data - sizeof(EntityID)); } diff --git a/src/Engine/Core/EntityFilePreprocessor.cpp b/src/Engine/Core/EntityFilePreprocessor.cpp index a1370dd2..592daedb 100644 --- a/src/Engine/Core/EntityFilePreprocessor.cpp +++ b/src/Engine/Core/EntityFilePreprocessor.cpp @@ -185,6 +185,9 @@ void EntityFilePreprocessor::parseComponentInfo() field.Offset = fieldOffset; field.Stride = stride; compInfo.FieldsInOrder.push_back(name); + if (field.Type == "string") { + compInfo.StringFields.push_back(name); + } fieldOffset += stride; } @@ -201,7 +204,7 @@ void EntityFilePreprocessor::parseDefaults() for (auto& ci : m_ComponentInfo) { // Allocate memory for default values - ci.second.Defaults = std::shared_ptr(new char[ci.second.Stride]); + ci.second.Defaults = boost::shared_array(new char[ci.second.Stride], std::bind(&ComponentWrapper::Destroy, ci.second, std::placeholders::_1)); memset(ci.second.Defaults.get(), 0, ci.second.Stride); std::string componentName = ci.first; diff --git a/src/Engine/Core/PerformanceTimer.cpp b/src/Engine/Core/PerformanceTimer.cpp new file mode 100644 index 00000000..50983e79 --- /dev/null +++ b/src/Engine/Core/PerformanceTimer.cpp @@ -0,0 +1,76 @@ +#include "Core/PerformanceTimer.h" +#include +#include + +cpu_timer PerformanceTimer::m_Timer; +std::map PerformanceTimer::timers; +std::string PerformanceTimer::currentTimerRunning = ""; + +void PerformanceTimer::StartTimer(std::string nameOfTimer) +{ + timers[nameOfTimer].stop(); + timers[nameOfTimer].start(); + currentTimerRunning = nameOfTimer; +} + +void PerformanceTimer::StartTimerAndStopPrevious(std::string nameOfTimer) +{ + //stop the current timer and start some other - useful to not have to stop timers all the time + if (currentTimerRunning != "") { + timers[currentTimerRunning].stop(); + } + timers[nameOfTimer].stop(); + timers[nameOfTimer].start(); + currentTimerRunning = nameOfTimer; +} + +void PerformanceTimer::StopTimer(std::string nameOfTimer) +{ + timers[nameOfTimer].stop(); + currentTimerRunning = nameOfTimer; +} + +void PerformanceTimer::SetFrameNumber(int frameNumber) +{ +} + +void PerformanceTimer::ResetAllTimers() +{ + //stop all timers + for (auto aTimer : timers) + { + aTimer.second.stop(); + } + currentTimerRunning = ""; + timers.clear(); +} + +void PerformanceTimer::CreateExcelData() +{ + //get time + std::time_t t = std::time(NULL); + char tStr[16]; + std::strftime(tStr, 32, " %a %H-%M-%S", std::localtime(&t)); + std::string time(tStr); + std::string path("TacticalZ"); + path += time + ".csv"; + std::ofstream someFileStream; + someFileStream.open(path, std::ofstream::out); + someFileStream << "classname" << ',' << "walltime" << ',' << "userTime" << ',' << "systemTime" << '\n'; + + //write all timers to file + for (auto aTimer : timers) + { + //remove the "class" name in front of the string + auto className = aTimer.first; + if (className.find("class ") != std::string::npos) { + className.replace(0, 6, ""); + } + auto wallTime = (double)aTimer.second.elapsed().wall*1e-3; + auto userTime = (double)aTimer.second.elapsed().user*1e-3; + auto systemTime = (double)aTimer.second.elapsed().system*1e-3; + + someFileStream << className << "," << wallTime << ',' << userTime << ',' << systemTime << '\n'; + } + someFileStream.close(); +} diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 69c25f61..8788a92e 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -9,7 +9,20 @@ World::~World() } } -EntityID World::CreateEntity(EntityID parent /*= 0*/) +World::World(const World& other) + : m_EventBroker(other.m_EventBroker) + , m_CurrentEntityID(other.m_CurrentEntityID) + , m_EntityParents(other.m_EntityParents) + , m_EntityChildren(other.m_EntityChildren) + , m_EntityNames(other.m_EntityNames) +{ + // Deep copy component pools + for (auto& kv : other.m_ComponentPools) { + m_ComponentPools[kv.first] = new ComponentPool(*kv.second); + } +} + +EntityID World::CreateEntity(EntityID parent /*= EntityID_Invalid*/) { EntityID newEntity = generateEntityID(); if (newEntity == parent) { @@ -44,10 +57,8 @@ ComponentWrapper World::AttachComponent(EntityID entity, const std::string& comp ComponentPool* pool = m_ComponentPools.at(componentType); const ComponentInfo& ci = pool->ComponentInfo(); - // Allocate space for the component + // Allocate component with default values ComponentWrapper c = pool->Allocate(entity); - // Write default values - memcpy(c.Data, ci.Defaults.get(), ci.Stride); return c; } diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index 75b66bc9..97ea9d53 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -40,8 +40,11 @@ EditorSystem::EditorSystem(SystemParams params, IRenderer* renderer, RenderFrame m_EditorStats = new EditorStats(); + m_Enabled = ResourceManager::Load("Config.ini")->Get("Debug.EditorEnabled", false); if (m_Enabled) { Enable(); + } else { + Disable(); } } @@ -222,6 +225,12 @@ bool EditorSystem::OnInputCommand(const Events::InputCommand& e) Enable(); } } + if (e.Command == "PerformanceTimingResetAllTimers" && e.Value > 0) { + PerformanceTimer::ResetAllTimers(); + } + if (e.Command == "PerformanceTimingCreateExcelData" && e.Value > 0) { + PerformanceTimer::CreateExcelData(); + } return true; } diff --git a/src/Engine/GUI/ButtonSystem.cpp b/src/Engine/GUI/ButtonSystem.cpp new file mode 100644 index 00000000..93ae1811 --- /dev/null +++ b/src/Engine/GUI/ButtonSystem.cpp @@ -0,0 +1,84 @@ +#include "GUI/ButtonSystem.h" + +ButtonSystem::ButtonSystem(SystemParams params, IRenderer* renderer) + : System(params) + , PureSystem("Button") + , m_Renderer(renderer) +{ + EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &ButtonSystem::OnMousePress); + EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &ButtonSystem::OnMouseRelease); + EVENT_SUBSCRIBE_MEMBER(m_EMouseLock, &ButtonSystem::OnMouseLock); + EVENT_SUBSCRIBE_MEMBER(m_EMouseUnlock, &ButtonSystem::OnMouseUnlock); +} + + +bool ButtonSystem::OnMouseLock(const Events::LockMouse& e) +{ + m_MouseIsLocked = true; + return true; +} + + +bool ButtonSystem::OnMouseUnlock(const Events::UnlockMouse& e) +{ + m_MouseIsLocked = false; + return true; +} + + +bool ButtonSystem::OnMousePress(const Events::MousePress& e) +{ + if (e.Button == GLFW_MOUSE_BUTTON_1 && !m_MouseIsLocked) { + m_PickData = m_Renderer->Pick(glm::vec2(e.X, e.Y)); + if (m_PickData.Entity != EntityID_Invalid && m_PickData.World == m_World) { + if(m_World->HasComponent(m_PickData.Entity, "Button")) { + //Entity is a button, save it and send pressed event. + + m_PickEntity = EntityWrapper(m_World, m_PickData.Entity); + + //You have clicked on a button entity, send pressed event. + Events::ButtonPressed ePressed; + ePressed.Entity = m_PickEntity; + ePressed.EntityName = m_PickEntity.Name(); + m_EventBroker->Publish(ePressed); + } + } + } + return true; +} + +bool ButtonSystem::OnMouseRelease(const Events::MouseRelease& e) +{ + if(!m_MouseIsLocked) { + //Mouse is not locked, send release event. + m_PickData = m_Renderer->Pick(glm::vec2(e.X, e.Y)); + if(m_PickData.Entity != EntityID_Invalid && m_PickData.World == m_World) { + EntityWrapper ent = EntityWrapper(m_World, m_PickData.Entity); + + Events::ButtonReleased eReleased; + eReleased.EntityName = m_PickEntity.Name(); + eReleased.Entity = m_PickEntity; + m_EventBroker->Publish(eReleased); + + if(m_World->HasComponent(m_PickData.Entity, "Button")) { + if (ent == m_PickEntity) { + //The entity you released the mouse button on is the same as you pressed it on. "Clicked" + Events::ButtonClicked eClicked; + eClicked.Entity = m_PickEntity; + eClicked.EntityName = m_PickEntity.Name(); + m_EventBroker->Publish(eClicked); + } + } + } + } + return true; +} + +void ButtonSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cHealth, double dt) +{ + +} + + + + \ No newline at end of file diff --git a/src/Engine/GUI/MainMenuSystem.cpp b/src/Engine/GUI/MainMenuSystem.cpp new file mode 100644 index 00000000..f8bf032b --- /dev/null +++ b/src/Engine/GUI/MainMenuSystem.cpp @@ -0,0 +1,57 @@ +#include "GUI/MainMenuSystem.h" + +MainMenuSystem::MainMenuSystem(SystemParams params, IRenderer* renderer) + : System(params) + , ImpureSystem() + , m_Renderer(renderer) +{ + EVENT_SUBSCRIBE_MEMBER(m_EPressed, &MainMenuSystem::OnButtonPress); + EVENT_SUBSCRIBE_MEMBER(m_EReleased, &MainMenuSystem::OnButtonRelease); + EVENT_SUBSCRIBE_MEMBER(m_EClicked, &MainMenuSystem::OnButtonClick); +} + +void MainMenuSystem::Update(double dt) +{ + +} + +bool MainMenuSystem::OnButtonClick(const Events::ButtonClicked& e) +{ + if(e.EntityName == "Play") { + //Run play code + } else if(e.EntityName == "Connect") { + //Run connect code + } else if(e.EntityName == "Host") { + //Run host code + } else if(e.EntityName == "Quit") { + printf("No, you stay"); + } else if (e.EntityName == "Res1080") { + glfwSetWindowSize(m_Renderer->Window(), 1920, 1080); + printf("\n1080"); + } else if (e.EntityName == "Res720") { + glfwSetWindowSize(m_Renderer->Window(), 1280, 720); + glViewport(0, 0, 1280, 720); + printf("\n720"); + } else if (e.EntityName == "Res480") { + glfwSetWindowSize(m_Renderer->Window(), 854, 480); + glViewport(0, 0, 854, 480); + printf("\n480"); + } else if (e.EntityName == "FullScreen") { + printf("No fullscreen for now"); + } + + return true; +} + +bool MainMenuSystem::OnButtonRelease(const Events::ButtonReleased& e) +{ + + return true; +} + +bool MainMenuSystem::OnButtonPress(const Events::ButtonPressed& e) +{ + + return true; +} + diff --git a/src/Engine/Network/Client.cpp b/src/Engine/Network/Client.cpp index b03aad07..d94ccbc6 100644 --- a/src/Engine/Network/Client.cpp +++ b/src/Engine/Network/Client.cpp @@ -1,10 +1,8 @@ #include "Network/Client.h" - using namespace boost::asio::ip; -Client::Client(World* world, EventBroker* eventBroker) +Client::Client(World* world, EventBroker* eventBroker) : Network(world, eventBroker) - , m_Socket(m_IOService) { // Asumes root node is EntityID_Invalid insertIntoServerClientMaps(EntityID_Invalid, EntityID_Invalid); @@ -14,8 +12,9 @@ Client::Client(World* world, EventBroker* eventBroker) auto config = ResourceManager::Load("Config.ini"); m_PlayerName = config->Get("Networking.Name", "Raptorcopter"); m_SendInputIntervalMs = config->Get("Networking.SendInputIntervalMs", 33); - LOG_INFO("Client initialized"); + + m_ServerlistRequest.Connect(m_PlayerName, "192.168.1.255", 32554); } Client::Client(World* world, EventBroker* eventBroker, std::unique_ptr snapshotFilter) @@ -26,70 +25,94 @@ Client::Client(World* world, EventBroker* eventBroker, std::unique_ptr("Config.ini"); + m_Address = address; if (address.empty()) { - address = config->Get("Networking.Address", "127.0.0.1"); + m_Address = config->Get("Networking.Address", "127.0.0.1"); } + m_Port = port; if (port == 0) { - port = config->Get("Networking.Port", 27666); + m_Port = config->Get("Networking.Port", 27666); } - - m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port); - LOG_INFO("Client connecting..."); - m_Socket.connect(m_ReceiverEndpoint); - connect(); } void Client::Update() { m_EventBroker->Process(); - readFromServer(); + while (m_Unreliable.IsSocketAvailable()) { + // Packet will get real data in receive + Packet packet(MessageType::Invalid); + m_Unreliable.Receive(packet); + if (packet.GetMessageType() == MessageType::Connect) { + parseUDPConnect(packet); + } else { + parseMessageType(packet); + } + } + while (m_Reliable.IsSocketAvailable()) { + // Packet will get real data in receive + Packet packet(MessageType::Invalid); + m_Reliable.Receive(packet); + if (packet.GetMessageType() == MessageType::Connect) { + parseTCPConnect(packet); + } else { + parseMessageType(packet); + } + + } + + while (m_ServerlistRequest.IsSocketAvailable()) { + Packet packet(MessageType::Invalid); + m_ServerlistRequest.Receive(packet); + if (packet.GetMessageType() == MessageType::ServerlistRequest) { + parseServerlist(packet); + } + } + + if (m_SearchingForServers) { + if (m_SearchingTime < (1000* (std::clock() - m_StartSearchTime) / (double)CLOCKS_PER_SEC)) { + m_SearchingForServers = false; + displayServerlist(); + } + } + if (m_IsConnected) { - hasServerTimedOut(); - // Don't sent 1 input in 1 packet, bunch em up. + // Don't send 1 input in 1 packet, bunch em up. if (m_SendInputIntervalMs < (1000 * (std::clock() - m_TimeSinceSentInputs) / (double)CLOCKS_PER_SEC)) { sendInputCommands(); m_TimeSinceSentInputs = std::clock(); } // HACK: Send absolute player positions for now to avoid desync until we have reliable messages sendLocalPlayerTransform(); - } - Network::Update(); -} -void Client::readFromServer() -{ - while (m_Socket.available()) { - bytesRead = receive(readBuf); - if (bytesRead > 0) { - Packet packet(readBuf, bytesRead); - parseMessageType(packet); - } + hasServerTimedOut(); } + //Network::Update(); } void Client::parseMessageType(Packet& packet) { + // Pop packetSize which is used by TCP Client to + // create a packet of the correct size + packet.ReadPrimitive(); int messageType = packet.ReadPrimitive(); if (messageType == -1) return; // Read packet ID m_PreviousPacketID = m_PacketID; // Set previous packet id m_PacketID = packet.ReadPrimitive(); //Read new packet id - identifyPacketLoss(); + //identifyPacketLoss(); switch (static_cast(messageType)) { - case MessageType::Connect: - parseConnect(packet); - break; case MessageType::Ping: parsePing(); break; @@ -115,17 +138,43 @@ void Client::parseMessageType(Packet& packet) case MessageType::ComponentDeleted: parseComponentDeletion(packet); break; + case MessageType::OnPlayerDamage: + parsePlayerDamage(packet); + break; + case MessageType::OnDoubleJump: + parseDoubleJump(packet); + break; default: break; } } -void Client::parseConnect(Packet& packet) +void Client::parseUDPConnect(Packet& packet) { // Map ServerEntityID and your PlayerID LOG_INFO("I be connected PogChamp"); } +void Client::parseTCPConnect(Packet& packet) +{ + LOG_INFO("Received TCP connect from server"); + // Pop size of message int + packet.ReadPrimitive(); + int messageType = packet.ReadPrimitive(); + // Read packet ID + m_PreviousPacketID = m_PacketID; // Set previous packet id + m_PacketID = packet.ReadPrimitive(); //Read new packet id + // parse player id and other stuff + m_PlayerID = packet.ReadPrimitive(); + m_PlayerID = packet.ReadPrimitive(); + LOG_INFO("A Player connected"); + Packet UnreliablePacket(MessageType::Connect, m_SendPacketID); + // Add player id and other stuff + packet.WritePrimitive(m_PlayerID); + m_Unreliable.Send(packet); + LOG_INFO("Sent UDP Connect Server"); +} + void Client::parsePlayerConnected(Packet & packet) { // Map ServerEntityID and other player's PlayerID @@ -143,7 +192,23 @@ void Client::parsePing() Packet packet(MessageType::Ping, m_SendPacketID); packet.WriteString("Ping recieved"); - send(packet); + m_Reliable.Send(packet); +} + + +void Client::parseServerlist(Packet& packet) +{ + // Pop size, message type, and ID + packet.ReadPrimitive(); + packet.ReadPrimitive(); + packet.ReadPrimitive(); + std::string address = packet.ReadString(); + int port = packet.ReadPrimitive(); + std::string serverName = packet.ReadString(); + int playersConnected = packet.ReadPrimitive(); + //TODO: This should not happen when a client is connected to a server + + m_Serverlist.push_back({ address, port, serverName, playersConnected }); } void Client::parseKick() @@ -152,14 +217,41 @@ void Client::parseKick() m_IsConnected = false; } +void Client::parseSpawnEvents() +{ + std::vector tempSpawn; + for (int i = 0; i < m_PlayerSpawnEvents.size(); i++) { + Events::PlayerSpawned e; + if (!serverClientMapsHasEntity(m_PlayerSpawnEvents.at(i).Player.ID)) { + tempSpawn.push_back(m_PlayerSpawnEvents.at(i)); + continue; + } + e.Player = EntityWrapper(m_World, m_ServerIDToClientID.at(m_PlayerSpawnEvents.at(i).Player.ID)); + //e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID.at(m_PlayerSpawnEvents.at(i).Spawner.ID)); + e.PlayerID = -1; + e.PlayerName = m_PlayerSpawnEvents.at(i).PlayerName; + m_EventBroker->Publish(e); + } + m_PlayerSpawnEvents = tempSpawn; + // m_PlayerSpawnEvents.clear(); +} + void Client::parsePlayersSpawned(Packet& packet) { + //Events::PlayerSpawned e; + //e.Player = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive()]); + //e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive()]); + //e.PlayerID = -1; + //e.PlayerName = packet.ReadString(); + //m_EventBroker->Publish(e); + Events::PlayerSpawned e; - e.Player = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive()]); - e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive()]); + e.Player = EntityWrapper(m_World, packet.ReadPrimitive()); + e.Spawner = EntityWrapper(m_World, packet.ReadPrimitive()); e.PlayerID = -1; e.PlayerName = packet.ReadString(); - m_EventBroker->Publish(e); + m_PlayerSpawnEvents.push_back(e); + parseSpawnEvents(); } void Client::parseEntityDeletion(Packet & packet) @@ -184,6 +276,20 @@ void Client::parseComponentDeletion(Packet & packet) } } +void Client::parseDoubleJump(Packet & packet) +{ + EntityID serverID = packet.ReadPrimitive(); + if (!serverClientMapsHasEntity(serverID)) { + return; + } + Events::DoubleJump e; + e.entityID = m_ServerIDToClientID.at(serverID); + // If player is local player do not publish to prevent infinite feedback loop + if (e.entityID != m_LocalPlayer.ID) { + m_EventBroker->Publish(e); + } +} + void Client::updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID) { for (auto field : componentInfo.FieldsInOrder) { @@ -235,10 +341,15 @@ void Client::parseSnapshot(Packet& packet) for (std::size_t i = 0; i < numInputCommands; ++i) { Events::InputCommand e; e.PlayerID = packet.ReadPrimitive(); - e.Player = EntityWrapper(m_World, m_ServerIDToClientID.at(packet.ReadPrimitive())); - e.Command = packet.ReadString(); - e.Value = packet.ReadPrimitive(); - m_EventBroker->Publish(e); + EntityID player = packet.ReadPrimitive(); + std::string command = packet.ReadString(); + float value = packet.ReadPrimitive(); + if (m_ServerIDToClientID.find(player) != m_ServerIDToClientID.end()) { + e.Player = EntityWrapper(m_World, m_ServerIDToClientID.at(player)); + e.Command = command; + e.Value = value; + m_EventBroker->Publish(e); + } } // Read world state @@ -253,19 +364,20 @@ void Client::parseSnapshot(Packet& packet) if (serverClientMapsHasEntity(serverEntityID)) { EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID); EntityWrapper localEntity(m_World, localEntityID); - // Update entity if (m_World->HasComponent(localEntityID, componentType)) { + // TODO Fix memory leak here SharedComponentWrapper newComponent = createSharedComponent(packet, localEntityID, componentInfo); bool shouldApply = true; // Apply potential filter function if (m_SnapshotFilter != nullptr) { shouldApply = m_SnapshotFilter->FilterComponent(localEntity, newComponent); } - if (shouldApply) { + if (shouldApply) { ComponentWrapper currentComponent = m_World->GetComponent(localEntityID, componentType); memcpy(currentComponent.Data, newComponent.Data, componentInfo.Stride); } + //if (localEntity != m_LocalPlayer && !localEntity.IsChildOf(m_LocalPlayer)) { // updateFields(packet, componentInfo, localEntityID); //} else { @@ -282,7 +394,11 @@ void Client::parseSnapshot(Packet& packet) if (serverParentID == EntityID_Invalid) { newLocalEntityID = m_World->CreateEntity(EntityID_Invalid); } else { - newLocalEntityID = m_World->CreateEntity(m_ServerIDToClientID.at(serverParentID)); + if (serverClientMapsHasEntity(serverParentID)) { + newLocalEntityID = m_World->CreateEntity(m_ServerIDToClientID.at(serverParentID)); + } else { + newLocalEntityID = m_World->CreateEntity(EntityID_Invalid); + } } m_World->SetName(newLocalEntityID, serverEntityName); insertIntoServerClientMaps(serverEntityID, newLocalEntityID); @@ -292,72 +408,42 @@ void Client::parseSnapshot(Packet& packet) } // Parent logic // This should be enough beacause we know that the entities arives in pre-order (there will always be a parent) - if (serverParentID != EntityID_Invalid) { + if (serverParentID != EntityID_Invalid && serverClientMapsHasEntity(serverParentID)) { EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID); - m_World->SetParent(localEntityID, m_ServerIDToClientID.at(serverParentID)); + if (m_World->GetParent(localEntityID) != m_ServerIDToClientID.at(serverParentID)) { + m_World->SetParent(localEntityID, m_ServerIDToClientID.at(serverParentID)); + } } } -} - -size_t Client::receive(char* data) -{ - boost::system::error_code error; - - size_t bytesReceived = m_Socket.receive_from(boost - ::asio::buffer((void*)data, INPUTSIZE), - m_ReceiverEndpoint, - 0, error); - // Network Debug data - if (isReadingData) { - m_NetworkData.TotalDataReceived += bytesReceived; - m_NetworkData.DataReceivedThisInterval += bytesReceived; - m_NetworkData.AmountOfMessagesReceived++; - } - if (error) { - //LOG_ERROR("receive: %s", error.message().c_str()); - } - return bytesReceived; -} - -void Client::send(Packet& packet) -{ - m_Socket.send_to(boost::asio::buffer( - packet.Data(), - packet.Size()), - m_ReceiverEndpoint, 0); - // Network Debug data - if (isReadingData) { - m_NetworkData.TotalDataSent += packet.Size(); - m_NetworkData.DataSentThisInterval += packet.Size(); - m_NetworkData.AmountOfMessagesSent++; - } -} - -void Client::connect() -{ - Packet packet(MessageType::Connect, m_SendPacketID); - packet.WriteString(m_PlayerName); - m_StartPingTime = std::clock(); - send(packet); + parseSpawnEvents(); } void Client::disconnect() { + m_IsConnected = false; m_PreviousPacketID = 0; m_PacketID = 0; Packet packet(MessageType::Disconnect, m_SendPacketID); - send(packet); + m_Reliable.Send(packet); + m_Reliable.Disconnect(); } bool Client::OnInputCommand(const Events::InputCommand & e) { + // TEMP + if (e.Command == "SearchForServers" && e.Value > 0) { + Events::SearchForServers e; + m_EventBroker->Publish(e); + } + if (e.PlayerID != -1) { return false; } if (e.Command == "ConnectToServer") { // Connect for now if (e.Value > 0) { - connect(); + m_Reliable.Connect(m_PlayerName, m_Address, m_Port); + m_Unreliable.Connect(m_PlayerName, m_Address, m_Port); } //LOG_DEBUG("Client::OnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); return true; @@ -380,7 +466,9 @@ bool Client::OnInputCommand(const Events::InputCommand & e) m_SaveDataTimer = std::clock(); } } else { - m_InputCommandBuffer.push_back(e); + if (m_IsConnected) { + m_InputCommandBuffer.push_back(e); + } //LOG_DEBUG("Client::OnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); return true; } @@ -389,12 +477,22 @@ bool Client::OnInputCommand(const Events::InputCommand & e) bool Client::OnPlayerDamage(const Events::PlayerDamage & e) { + if (e.Inflictor != m_LocalPlayer) { + return false; + } + // Could this happen? + //if (!clientServerMapsHasEntity(e.Inflictor.ID) + // || !clientServerMapsHasEntity(e.Victim.ID)) { + // return; + //} + Packet packet(MessageType::OnPlayerDamage, m_SendPacketID); packet.WritePrimitive(m_ClientIDToServerID.at(e.Inflictor.ID)); packet.WritePrimitive(m_ClientIDToServerID.at(e.Victim.ID)); packet.WritePrimitive(e.Damage); - send(packet); - return false; + m_Reliable.Send(packet); + + return true; } bool Client::OnPlayerSpawned(const Events::PlayerSpawned& e) @@ -405,23 +503,72 @@ bool Client::OnPlayerSpawned(const Events::PlayerSpawned& e) return true; } +bool Client::OnSearchForServers(const Events::SearchForServers& e) +{ + m_SearchingForServers = true; + m_StartSearchTime = std::clock(); + m_Serverlist.clear(); + LOG_INFO("Searching for LAN servers...\n"); + Packet packet(MessageType::ServerlistRequest); + m_ServerlistRequest.Broadcast(packet, 13); // TODO: Config + return true; +} + +void Client::parsePlayerDamage(Packet& packet) +{ + Events::PlayerDamage e; + PlayerID victimID = packet.ReadPrimitive(); + PlayerID inflictorID = packet.ReadPrimitive(); + if (!serverClientMapsHasEntity(victimID) || !serverClientMapsHasEntity(inflictorID)) { + return; + } + e.Inflictor = EntityWrapper(m_World, m_ServerIDToClientID.at(victimID)); + e.Victim = EntityWrapper(m_World, m_ServerIDToClientID.at(inflictorID)); + e.Damage = packet.ReadPrimitive(); + // Don't rebroadcast our own player damage events or we'll have an infinite loop! + if (e.Inflictor != m_LocalPlayer) { + m_EventBroker->Publish(e); + } +} + +bool Client::OnDoubleJump(Events::DoubleJump & e) +{ + if (!clientServerMapsHasEntity(e.entityID) || e.entityID != m_LocalPlayer.ID) { + return false; + } + Packet packet(MessageType::OnDoubleJump); + packet.WritePrimitive(m_ClientIDToServerID.at(e.entityID)); + m_Reliable.Send(packet); + return true; +} + void Client::sendLocalPlayerTransform() { if (!m_LocalPlayer.Valid()) { return; } + Packet packet(MessageType::PlayerTransform, m_SendPacketID); + ComponentWrapper cTransform = m_LocalPlayer["Transform"]; glm::vec3& position = cTransform["Position"]; glm::vec3& orientation = cTransform["Orientation"]; - Packet packet(MessageType::PlayerTransform, m_SendPacketID); packet.WritePrimitive(position.x); packet.WritePrimitive(position.y); packet.WritePrimitive(position.z); packet.WritePrimitive(orientation.x); packet.WritePrimitive(orientation.y); packet.WritePrimitive(orientation.z); - send(packet); + + bool hasAssaultWeapon = m_LocalPlayer.HasComponent("AssaultWeapon"); + packet.WritePrimitive(hasAssaultWeapon); + if (hasAssaultWeapon) { + ComponentWrapper cAssaultWeapon = m_LocalPlayer["AssaultWeapon"]; + packet.WritePrimitive((int)cAssaultWeapon["MagazineAmmo"]); + packet.WritePrimitive((int)cAssaultWeapon["Ammo"]); + } + + m_Unreliable.Send(packet); } void Client::identifyPacketLoss() @@ -433,17 +580,15 @@ void Client::identifyPacketLoss() } } -bool Client::hasServerTimedOut() +void Client::hasServerTimedOut() { // Time in ms double timeSincePing = 1000 * (std::clock() - m_StartPingTime) / static_cast(CLOCKS_PER_SEC); if (timeSincePing > m_TimeoutMs) { // Clear everything and go to menu. LOG_INFO("Server has timed out, returning to menu, Beep Boop."); - m_IsConnected = false; - return true; + disconnect(); } - return false; } EntityID Client::createPlayer() @@ -464,7 +609,7 @@ void Client::sendInputCommands() packet.WriteString(m_InputCommandBuffer[i].Command); packet.WritePrimitive(m_InputCommandBuffer[i].Value); } - send(packet); + m_Reliable.Send(packet); m_InputCommandBuffer.clear(); } } @@ -472,7 +617,17 @@ void Client::sendInputCommands() void Client::becomePlayer() { Packet packet = Packet(MessageType::BecomePlayer, m_SendPacketID); - send(packet); + m_Reliable.Send(packet); +} + + +void Client::displayServerlist() +{ + LOG_INFO("This is a serverlist:\n"); + for (int i = 0; i < m_Serverlist.size(); i++) { + ServerInfo si = m_Serverlist[i]; + LOG_INFO("%s:%i\t%s\t%i\n", si.Address.c_str(), si.Port, si.Name.c_str(), si.PlayersConnected); + } } bool Client::clientServerMapsHasEntity(EntityID clientEntityID) @@ -503,7 +658,6 @@ void Client::insertIntoServerClientMaps(EntityID serverEntityID, EntityID client { m_ServerIDToClientID.insert(std::make_pair(serverEntityID, clientEntityID)); m_ClientIDToServerID.insert(std::make_pair(clientEntityID, serverEntityID)); - } void Client::deleteFromServerClientMaps(EntityID serverEntityID, EntityID clientEntityID) diff --git a/src/Engine/Network/Network.cpp b/src/Engine/Network/Network.cpp index 534df1cd..6ce9ef82 100644 --- a/src/Engine/Network/Network.cpp +++ b/src/Engine/Network/Network.cpp @@ -14,6 +14,21 @@ void Network::Update() updateNetworkData(); } +void Network::logSentData(int bytesSent) +{ + +} + +void Network::logReceivedData(int bytesReceived) +{ + // Network Debug data + if (isReadingData) { + m_NetworkData.TotalDataReceived += bytesReceived; + m_NetworkData.DataReceivedThisInterval += bytesReceived; + m_NetworkData.AmountOfMessagesReceived++; + } +} + void Network::saveToFile() { std::ofstream outfile; diff --git a/src/Engine/Network/NetworkClient.cpp b/src/Engine/Network/NetworkClient.cpp new file mode 100644 index 00000000..7a61d5f3 --- /dev/null +++ b/src/Engine/Network/NetworkClient.cpp @@ -0,0 +1,11 @@ +#include "Network/NetworkClient.h" + +NetworkClient::NetworkClient() +{ + m_ReadBuffer = new char[m_BufferSize]; +} + +NetworkClient::~NetworkClient() +{ + delete[] m_ReadBuffer; +} diff --git a/src/Engine/Network/NetworkServer.cpp b/src/Engine/Network/NetworkServer.cpp new file mode 100644 index 00000000..1412621d --- /dev/null +++ b/src/Engine/Network/NetworkServer.cpp @@ -0,0 +1,11 @@ +#include "Network/NetworkServer.h" + +NetworkServer::NetworkServer() +{ + m_ReadBuffer = new char[m_BufferSize]; +} + +NetworkServer::~NetworkServer() +{ + delete[] m_ReadBuffer; +} diff --git a/src/Engine/Network/Packet.cpp b/src/Engine/Network/Packet.cpp index 21226a07..475ca673 100644 --- a/src/Engine/Network/Packet.cpp +++ b/src/Engine/Network/Packet.cpp @@ -34,10 +34,12 @@ void Packet::Init(MessageType type, unsigned int & packetID) m_ReturnDataOffset = 0; m_Offset = 0; // Create message header + // allocate memory for size of packet(only used in tcp) + WritePrimitive(0); // Add message type int messageType = static_cast(type); - Packet::WritePrimitive(messageType); - Packet::WritePrimitive(packetID); + WritePrimitive(messageType); + WritePrimitive(packetID); packetID++; m_HeaderSize = m_Offset; } @@ -56,9 +58,12 @@ void Packet::WriteString(const std::string& str) void Packet::WriteData(char * data, int sizeOfData) { + if (m_Offset + sizeOfData > m_MaxPacketSize) { //LOG_WARNING("Packet::WriteData(): Data size in packet exceeded maximum packet size. New size is %i bytes\n", m_MaxPacketSize*2); - resizeData(); + while (m_Offset + sizeOfData > m_MaxPacketSize) { + resizeData(); + } } memcpy(m_Data + m_Offset, data, sizeOfData); m_Offset += sizeOfData; @@ -76,14 +81,35 @@ std::string Packet::ReadString() return returnValue; } -char * Packet::ReadData(int SizeOfData) +void Packet::ReconstructFromData(char * data, size_t sizeOfData) { - if (m_Offset < m_ReturnDataOffset + SizeOfData) { + if (sizeOfData > m_MaxPacketSize) { + // Delete our data + delete[] m_Data; + // Set new max size + m_MaxPacketSize = sizeOfData; + m_Data = new char[m_MaxPacketSize]; + // while we resized the old data container. + } + memcpy(m_Data, data, sizeOfData); + m_Offset = sizeOfData; + +} + +void Packet::UpdateSize() +{ + int whatisoffset = m_Offset; + memcpy(m_Data, &m_Offset, sizeof(int)); +} + +char * Packet::ReadData(int sizeOfData) +{ + if (m_Offset < m_ReturnDataOffset + sizeOfData) { //LOG_WARNING("packet ReadData(): Oh no! You are trying to remove things outside my memory kingdom"); return nullptr; } size_t oldReturnDataOffset = m_ReturnDataOffset; - m_ReturnDataOffset += SizeOfData; + m_ReturnDataOffset += sizeOfData; return (m_Data + oldReturnDataOffset); } @@ -91,25 +117,36 @@ void Packet::ChangePacketID(unsigned int & packetID) { packetID = packetID + 1; // Overwrite old PacketID - memcpy(m_Data + sizeof(int), &packetID, sizeof(int)); + memcpy(m_Data + 2*sizeof(int), &packetID, sizeof(int)); +} + +MessageType Packet::GetMessageType() +{ + MessageType messagType; + memcpy(&messagType, m_Data + sizeof(int), sizeof(int)); + return messagType; } void Packet::resizeData() { + resizeData(m_MaxPacketSize * 2); +} +void Packet::resizeData(int size) +{ // Allocate memory to store our data in char* holdData = new char[m_MaxPacketSize]; // Copy our data to the newly allocated memory memcpy(holdData, m_Data, m_Offset); // Increase max packet size - m_MaxPacketSize = m_MaxPacketSize * 2; + m_MaxPacketSize = size; // Delete our data - delete m_Data; - // Allocate twice the memory we had before + delete[] m_Data; + // Allocate memory m_Data = new char[m_MaxPacketSize]; // Copy our data to new location memcpy(m_Data, holdData, m_Offset); // Delete the memory allocated to hold our data // while we resized the old data container. - delete holdData; + delete[] holdData; } diff --git a/src/Engine/Network/Server.cpp b/src/Engine/Network/Server.cpp index 1f0b3bc7..7ed069d6 100644 --- a/src/Engine/Network/Server.cpp +++ b/src/Engine/Network/Server.cpp @@ -1,24 +1,24 @@ #include "Network/Server.h" -Server::Server(World* world, EventBroker* eventBroker, int port) +Server::Server(World* world, EventBroker* eventBroker, int port) : Network(world, eventBroker) + , m_ServerlistRequest(13) { ConfigFile* config = ResourceManager::Load("Config.ini"); snapshotInterval = 1000 * config->Get("Networking.SnapshotInterval", 0.05f); pingIntervalMs = config->Get("Networking.PingIntervalMs", 1000); - // Subscribe to events EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Server::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Server::OnPlayerSpawned); EVENT_SUBSCRIBE_MEMBER(m_EEntityDeleted, &Server::OnEntityDeleted); EVENT_SUBSCRIBE_MEMBER(m_EComponentDeleted, &Server::OnComponentDeleted); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &Server::OnPlayerDamage); - // Bind + // BindWW if (port == 0) { port = config->Get("Networking.Port", 27666); } m_Port = port; - m_Socket = std::make_unique(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port)); LOG_INFO("Server initialized and bound to port %i", port); } @@ -29,32 +29,64 @@ Server::~Server() void Server::Update() { - readFromClients(); - m_EventBroker->Process(); - if (isReadingData) { - Network::Update(); - } + m_Reliable.AcceptNewConnections(m_NextPlayerID, m_ConnectedPlayers); -} - -void Server::readFromClients() -{ - while (m_Socket->available()) { - try { - bytesRead = receive(readBuffer); - Packet packet(readBuffer, bytesRead); - parseMessageType(packet); - } catch (const std::exception&) { - //LOG_ERROR("%i: Read from client crashed %s", m_PacketID, err.what()); + for (auto& kv : m_ConnectedPlayers) { + while (kv.second.TCPSocket->available()) { + // Packet will get real data in receive + Packet packet(MessageType::Invalid); + m_Reliable.Receive(packet, kv.second); + m_Address = kv.second.TCPSocket->remote_endpoint().address(); + m_Port = kv.second.TCPSocket->remote_endpoint().port(); + if (packet.GetMessageType() == MessageType::Connect) { + parseTCPConnect(packet); + } else { + parseMessageType(packet); + } } } + + PlayerDefinition pd; + while (m_Unreliable.IsSocketAvailable()) { + // Packet will get real data in receive + Packet packet(MessageType::Invalid); + m_Unreliable.Receive(packet, pd); + m_Address = pd.Endpoint.address(); + m_Port = pd.Endpoint.port(); + if (packet.GetMessageType() == MessageType::Connect) { + parseUDPConnect(packet); + } else { + parseMessageType(packet); + } + } + + while (m_ServerlistRequest.IsSocketAvailable()) { + Packet packet(MessageType::Invalid); + PlayerDefinition localArea; + localArea.Endpoint = boost::asio::ip::udp::endpoint(); + m_ServerlistRequest.Receive(packet, localArea); + if (packet.GetMessageType() == MessageType::ServerlistRequest) { + packet.ReadPrimitive(); // Pop size + packet.ReadPrimitive(); // Pop MsgType + packet.ReadPrimitive(); // Pop packet ID + int port = packet.ReadPrimitive(); + std::string address = localArea.Endpoint.address().to_string(); + parseServerlistRequest(boost::asio::ip::udp::endpoint(boost::asio::ip::address().from_string(address), port)); + } + } + + // Check if players have disconnected + for (int i = 0; i < m_PlayersToDisconnect.size(); i++) { + disconnect(m_PlayersToDisconnect.at(i)); + } + m_PlayersToDisconnect.clear(); + std::clock_t currentTime = std::clock(); // Send snapshot if (snapshotInterval < (1000 * (currentTime - previousSnapshotMessage) / (double)CLOCKS_PER_SEC)) { sendSnapshot(); previousSnapshotMessage = currentTime; } - // Send pings each if (pingIntervalMs < (1000 * (currentTime - previousePingMessage) / (double)CLOCKS_PER_SEC)) { sendPing(); @@ -66,19 +98,26 @@ void Server::readFromClients() checkForTimeOuts(); timOutTimer = currentTime; } + m_EventBroker->Process(); + if (isReadingData) { + Network::Update(); + } } void Server::parseMessageType(Packet& packet) { - int messageType = packet.ReadPrimitive(); // Read what type off message was sent from server + // Pop packetSize which is used by TCP Client to + // create a packet of the correct size + packet.ReadPrimitive(); + int messageType = packet.ReadPrimitive(); // Read what type off message was sent from server // Read packet ID m_PreviousPacketID = m_PacketID; // Set previous packet id m_PacketID = packet.ReadPrimitive(); //Read new packet id //identifyPacketLoss(); switch (static_cast(messageType)) { case MessageType::Connect: - parseConnect(packet); + //parseConnect(packet); break; case MessageType::Ping: parsePing(); @@ -99,65 +138,27 @@ void Server::parseMessageType(Packet& packet) case MessageType::PlayerTransform: parsePlayerTransform(packet); break; + case MessageType::OnDoubleJump: + parseDoubleJump(packet); + break; default: break; } } -size_t Server::receive(char * data) -{ - size_t length = m_Socket->receive_from( - boost::asio::buffer((void*)data - , INPUTSIZE) - , m_ReceiverEndpoint, 0); - // Network Debug data - if (isReadingData) { - m_NetworkData.TotalDataReceived += length; - m_NetworkData.DataReceivedThisInterval += length; - m_NetworkData.AmountOfMessagesReceived++; - } - return length; -} - -void Server::send(PlayerID player, Packet& packet) -{ - try { - size_t bytesSent = m_Socket->send_to( - boost::asio::buffer(packet.Data(), packet.Size()), - m_ConnectedPlayers[player].Endpoint, - 0); - // Network Debug data - if (isReadingData) { - m_NetworkData.TotalDataSent += packet.Size(); - m_NetworkData.DataSentThisInterval += packet.Size(); - m_NetworkData.AmountOfMessagesSent++; - } - } catch (const boost::system::system_error&) { - // TODO: Clean up invalid endpoints out of m_ConnectedPlayers later - m_ConnectedPlayers[player].Endpoint = boost::asio::ip::udp::endpoint(); - } -} - -void Server::send(Packet & packet) -{ - m_Socket->send_to( - boost::asio::buffer( - packet.Data(), - packet.Size()), - m_ReceiverEndpoint, - 0); - if (isReadingData) { - // Network Debug data - m_NetworkData.TotalDataSent += packet.Size(); - m_NetworkData.DataSentThisInterval += packet.Size(); - } -} - -void Server::broadcast(Packet& packet) +void Server::reliableBroadcast(Packet& packet) { for (auto& kv : m_ConnectedPlayers) { packet.ChangePacketID(kv.second.PacketID); - send(kv.first, packet); + m_Reliable.Send(packet, kv.second); + } +} + +void Server::unreliableBroadcast(Packet& packet) +{ + for (auto& kv : m_ConnectedPlayers) { + packet.ChangePacketID(kv.second.PacketID); + m_Unreliable.Send(packet, kv.second); } } @@ -166,8 +167,8 @@ void Server::sendSnapshot() { Packet packet(MessageType::Snapshot); addInputCommandsToPacket(packet); - addChildrenToPacket(packet, EntityID_Invalid); - broadcast(packet); + addPlayersToPacket(packet, EntityID_Invalid); + unreliableBroadcast(packet); } void Server::addInputCommandsToPacket(Packet& packet) @@ -183,6 +184,54 @@ void Server::addInputCommandsToPacket(Packet& packet) m_InputCommandsToBroadcast.clear(); } +void Server::addPlayersToPacket(Packet & packet, EntityID entityID) +{ + auto itPair = m_World->GetChildren(entityID); + std::unordered_map worldComponentPools = m_World->GetComponentPools(); + // Loop through every child + for (auto it = itPair.first; it != itPair.second; it++) { + EntityID childEntityID = it->second; + // HACK: Only sync players for now, since the map turned out to be TOO LARGE to send in one snapshot and Simon's computer shits itself + // HACK: Also checked CapturePointHUD for now. (this would get out of sync); + EntityWrapper childEntity(m_World, childEntityID); + if (shouldSendToClient(childEntity)) { + // Write EntityID and parentsID and Entity name + packet.WritePrimitive(childEntityID); + packet.WritePrimitive(entityID); + packet.WriteString(m_World->GetName(childEntityID)); + // Write components to child + int numberOfComponents = 0; + for (auto& i : worldComponentPools) { + if (i.second->KnowsEntity(childEntityID)) { + numberOfComponents++; + } + } + // Write how many components should be read + packet.WritePrimitive(numberOfComponents); + for (auto& i : worldComponentPools) { + // If the entity exist in the pool + if (i.second->KnowsEntity(childEntityID)) { + ComponentWrapper componentWrapper = i.second->GetByEntity(childEntityID); + // ComponentType + packet.WriteString(componentWrapper.Info.Name); + // Loop through fields + for (auto& componentField : componentWrapper.Info.FieldsInOrder) { + ComponentInfo::Field_t fieldInfo = componentWrapper.Info.Fields.at(componentField); + if (fieldInfo.Type == "string") { + std::string& value = componentWrapper[componentField]; + packet.WriteString(value); + } else { + packet.WriteData(componentWrapper.Data + fieldInfo.Offset, fieldInfo.Stride); + } + } + } + } + } + // Go to to your children + addPlayersToPacket(packet, childEntityID); + } +} + void Server::addChildrenToPacket(Packet & packet, EntityID entityID) { auto itPair = m_World->GetChildren(entityID); @@ -241,24 +290,117 @@ void Server::sendPing() // Time message m_StartPingTime = std::clock(); // Send message - broadcast(packet); + reliableBroadcast(packet); } + + void Server::checkForTimeOuts() { double startPing = 1000 * m_StartPingTime / static_cast(CLOCKS_PER_SEC); - for (int i = 0; i < m_ConnectedPlayers.size(); i++) { - if (m_ConnectedPlayers[i].Endpoint.address() != boost::asio::ip::address()) { - double stopPing = 1000 * m_ConnectedPlayers[i].StopTime / + std::vector playersToRemove; + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.TCPAddress != boost::asio::ip::address()) { + int stopPing = 1000 * kv.second.StopTime / static_cast(CLOCKS_PER_SEC); if (startPing > stopPing + m_TimeoutMs) { - //LOG_INFO("User %i timed out!", i); - //disconnect(i); + LOG_INFO("User %i timed out!", kv.second.Name); + playersToRemove.push_back(kv.first); } } } + for (size_t i = 0; i < playersToRemove.size(); i++) { + disconnect(playersToRemove.at(i)); + } +} + +void Server::parseUDPConnect(Packet & packet) +{ + // Pop size of message int + packet.ReadPrimitive(); + int messageType = packet.ReadPrimitive(); + // Read packet ID + m_PreviousPacketID = m_PacketID; // Set previous packet id + m_PacketID = packet.ReadPrimitive(); //Read new packet id + // parse player id and other stuff + PlayerID playerID = packet.ReadPrimitive(); + // Do something here? + boost::asio::ip::udp::endpoint endpoint(m_Address, m_Port); + m_ConnectedPlayers.at(playerID).Endpoint = endpoint; + LOG_INFO("parseUDPConnect: Spectator \"%s\" connected on IP: %s", m_ConnectedPlayers.at(playerID).Name.c_str(), m_ConnectedPlayers.at(playerID).Endpoint.address().to_string().c_str()); + // Send a message to the player that connected + Packet connnectPacket(MessageType::Connect, m_ConnectedPlayers.at(playerID).PacketID); + m_Unreliable.Send(connnectPacket); + LOG_INFO("UDP Connect sent to client"); +} + +void Server::parseTCPConnect(Packet & packet) +{ + // Pop size of message int + packet.ReadPrimitive(); + int messageType = packet.ReadPrimitive(); + // Read packet ID + m_PreviousPacketID = m_PacketID; // Set previous packet id + m_PacketID = packet.ReadPrimitive(); //Read new packet id + + LOG_INFO("Parsing connections"); + // Check if player is already connected + // Ska vara till lagd i TCPServer receive + PlayerID playerID = GetPlayerIDFromEndpoint(); + if (playerID == -1) { + return; + } + // Create a new player + m_ConnectedPlayers.at(playerID).EntityID = 0; // Overlook this + m_ConnectedPlayers.at(playerID).Name = packet.ReadString(); + m_ConnectedPlayers.at(playerID).PacketID = 0; + m_ConnectedPlayers.at(playerID).StopTime = std::clock(); + m_ConnectedPlayers.at(playerID).TCPAddress = m_Address; + m_ConnectedPlayers.at(playerID).TCPPort = m_Port; + + LOG_INFO("parseTCPConnect: Spectator \"%s\" connected on IP: %s", m_ConnectedPlayers.at(playerID).Name.c_str(), + m_ConnectedPlayers.at(playerID).TCPAddress.to_string().c_str()); + + // Send a message to the player that connected + Packet connnectPacket(MessageType::Connect, m_ConnectedPlayers.at(playerID).PacketID); + // Write playerID to packet + connnectPacket.WritePrimitive(playerID); + m_Reliable.Send(connnectPacket); + + Packet firstSnapshot(MessageType::Snapshot); + addInputCommandsToPacket(firstSnapshot); + addChildrenToPacket(firstSnapshot, EntityID_Invalid); + m_Reliable.Send(firstSnapshot); + + // Send notification that a player has connected + //Packet notificationPacket(MessageType::PlayerConnected); + //broadcast(notificationPacket); +} + +void Server::parseDisconnect() +{ + LOG_INFO("%i: Parsing disconnect", m_PacketID); + + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.TCPAddress == m_Address && + kv.second.TCPPort == m_Port) { + m_PlayersToDisconnect.push_back(kv.first); + break; + } + } +} + + +void Server::parseServerlistRequest(boost::asio::ip::udp::endpoint endpoint) +{ + Packet packet(MessageType::ServerlistRequest); + packet.WriteString(m_Reliable.Address()); + packet.WritePrimitive(m_Reliable.Port()); + packet.WriteString("SERVERNAME"); + packet.WritePrimitive(m_ConnectedPlayers.size()); + m_ServerlistRequest.Send(packet); } void Server::disconnect(PlayerID playerID) @@ -267,33 +409,15 @@ void Server::disconnect(PlayerID playerID) LOG_INFO("User %s disconnected/timed out", m_ConnectedPlayers[playerID].Name.c_str()); // Remove enteties and stuff (When we can remove entity, remove it and tell clients to remove the copy they have) Events::PlayerDisconnected e; - e.Entity = m_ConnectedPlayers[playerID].EntityID; + e.Entity = m_ConnectedPlayers.at(playerID).EntityID; e.PlayerID = playerID; m_EventBroker->Publish(e); - + //m_World->DeleteEntity(m_ConnectedPlayers[playerID].EntityID); + m_ConnectedPlayers[playerID].TCPSocket->shutdown(boost::asio::ip::tcp::socket::shutdown_both); + m_ConnectedPlayers[playerID].TCPSocket->close(); + m_World->DeleteEntity(m_ConnectedPlayers[playerID].EntityID); m_ConnectedPlayers.erase(playerID); -} - -void Server::parseOnInputCommand(Packet& packet) -{ - PlayerID player = -1; - // Check which player it was who sent the message - player = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); - if (player != -1) { - while (packet.DataReadSize() < packet.Size()) { - Events::InputCommand e; - e.Command = packet.ReadString(); - e.PlayerID = player; // Set correct player id - e.Player = EntityWrapper(m_World, m_ConnectedPlayers.at(player).EntityID); - e.Value = packet.ReadPrimitive(); - m_EventBroker->Publish(e); - - if (e.Command == "PrimaryFire") { - m_InputCommandsToBroadcast.push_back(e); - } - //LOG_INFO("Server::parseOnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); - } - } + // Send disconnect to the other players. } void Server::parseOnPlayerDamage(Packet & packet) @@ -303,78 +427,10 @@ void Server::parseOnPlayerDamage(Packet & packet) e.Victim = EntityWrapper(m_World, packet.ReadPrimitive()); e.Damage = packet.ReadPrimitive(); m_EventBroker->Publish(e); + //LOG_DEBUG("Server::parseOnPlayerDamage: Command is %s. Value is %f. PlayerID is %i.", e.DamageAmount, e.PlayerDamagedID, e.TypeOfDamage.c_str()); } -void Server::parseConnect(Packet& packet) -{ - LOG_INFO("Parsing connections"); - // Check if player is already connected - if (GetPlayerIDFromEndpoint(m_ReceiverEndpoint) != -1) { - return; - } - for (auto& kv : m_ConnectedPlayers) { - if (kv.second.Endpoint.address() == m_ReceiverEndpoint.address() && - kv.second.Endpoint.port() == m_ReceiverEndpoint.port()) { - // Already connected - return; - } - } - // Create a new player - PlayerDefinition pd; - pd.EntityID = 0; // Overlook this - pd.Endpoint = m_ReceiverEndpoint; - pd.Name = packet.ReadString(); - pd.PacketID = 0; - pd.StopTime = std::clock(); - m_ConnectedPlayers[m_NextPlayerID++] = pd; - LOG_INFO("Spectator \"%s\" connected on IP: %s", pd.Name.c_str(), pd.Endpoint.address().to_string().c_str()); - - // Send a message to the player that connected - Packet connnectPacket(MessageType::Connect, pd.PacketID); - send(connnectPacket); - - // Send notification that a player has connected - Packet notificationPacket(MessageType::PlayerConnected); - broadcast(notificationPacket); -} - -void Server::parseDisconnect() -{ - LOG_INFO("%i: Parsing disconnect", m_PacketID); - - for (auto& kv : m_ConnectedPlayers) { - if (kv.second.Endpoint.address() == m_ReceiverEndpoint.address() && - kv.second.Endpoint.port() == m_ReceiverEndpoint.port()) { - disconnect(kv.first); - break; - } - } -} - -void Server::parseClientPing() -{ - LOG_INFO("%i: Parsing ping", m_PacketID); - PlayerID player = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); - if (player == -1) { - return; - } - // Return ping - Packet packet(MessageType::Ping, m_ConnectedPlayers[player].PacketID); - packet.WriteString("Ping received"); - send(packet); -} - -void Server::parsePing() -{ - for (int i = 0; i < m_ConnectedPlayers.size(); i++) { - if (m_ConnectedPlayers[i].Endpoint.address() == m_ReceiverEndpoint.address()) { - m_ConnectedPlayers[i].StopTime = std::clock(); - break; - } - } -} - void Server::identifyPacketLoss() { // if no packets lost, difference should be equal to 1 @@ -388,18 +444,7 @@ void Server::kick(PlayerID player) { disconnect(player); Packet packet = Packet(MessageType::Kick); - send(packet); -} - -PlayerID Server::GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint) -{ - for (auto& kv : m_ConnectedPlayers) { - if (kv.second.Endpoint.address() == endpoint.address() && - kv.second.Endpoint.port() == endpoint.port()) { - return kv.first; - } - } - return -1; + m_Reliable.Send(packet); } bool Server::OnInputCommand(const Events::InputCommand & e) @@ -411,8 +456,7 @@ bool Server::OnInputCommand(const Events::InputCommand & e) } isReadingData = !isReadingData; m_SaveDataTimer = std::clock(); - } - if (e.Command == "KickPlayer" && e.Value > 0) { + } else if (e.Command == "KickPlayer" && e.Value > 0) { kick(0); } @@ -428,7 +472,7 @@ bool Server::OnPlayerSpawned(const Events::PlayerSpawned & e) packet.WritePrimitive(e.Spawner.ID); // We don't send PlayerID here because it will always be set to -1 packet.WriteString(m_ConnectedPlayers[e.PlayerID].Name); - send(e.PlayerID, packet); + m_Reliable.Send(packet, m_ConnectedPlayers[e.PlayerID]); return false; } @@ -437,7 +481,7 @@ bool Server::OnEntityDeleted(const Events::EntityDeleted & e) if (!e.Cascaded) { Packet packet = Packet(MessageType::EntityDeleted); packet.WritePrimitive(e.DeletedEntity); - broadcast(packet); + reliableBroadcast(packet); } return false; } @@ -445,16 +489,87 @@ bool Server::OnEntityDeleted(const Events::EntityDeleted & e) bool Server::OnComponentDeleted(const Events::ComponentDeleted & e) { if (!e.Cascaded) { - Packet packet = Packet(MessageType::ComponentDeleted); - packet.WritePrimitive(e.Entity); - packet.WriteString(e.ComponentType); - broadcast(packet); + if (shouldSendToClient(EntityWrapper(m_World, e.Entity))) { + Packet packet = Packet(MessageType::ComponentDeleted); + packet.WritePrimitive(e.Entity); + packet.WriteString(e.ComponentType); + reliableBroadcast(packet); + } } return false; } +bool Server::OnPlayerDamage(const Events::PlayerDamage& e) +{ + Packet packet(MessageType::OnPlayerDamage); + packet.WritePrimitive(e.Inflictor.ID); + packet.WritePrimitive(e.Victim.ID); + packet.WritePrimitive(e.Damage); + reliableBroadcast(packet); + + return true; +} + +void Server::parseClientPing() +{ + LOG_INFO("%i: Parsing ping", m_PacketID); + PlayerID player = GetPlayerIDFromEndpoint(); + if (player == -1) { + return; + } + // Return ping + Packet packet(MessageType::Ping, m_ConnectedPlayers[player].PacketID); + packet.WriteString("Ping received"); + m_Reliable.Send(packet); +} + +void Server::parsePing() +{ + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.TCPAddress == m_Address && + kv.second.TCPPort == m_Port + || (kv.second.Endpoint.address() == m_Address + && kv.second.Endpoint.port() == m_Port)) { + kv.second.StopTime = std::clock(); + break; + } + } +} + +bool Server::parseDoubleJump(Packet & packet) +{ + reliableBroadcast(packet); + return true; +} + +void Server::parseOnInputCommand(Packet& packet) +{ + PlayerID player = -1; + // Check which player it was who sent the message + player = GetPlayerIDFromEndpoint(); + if (player != -1) { + while (packet.DataReadSize() < packet.Size()) { + Events::InputCommand e; + e.Command = packet.ReadString(); + e.PlayerID = player; // Set correct player id + e.Player = EntityWrapper(m_World, m_ConnectedPlayers.at(player).EntityID); + e.Value = packet.ReadPrimitive(); + m_EventBroker->Publish(e); + if (e.Command == "PrimaryFire" || e.Command == "Reload") { + m_InputCommandsToBroadcast.push_back(e); + } + //LOG_INFO("Server::parseOnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); + } + } +} + void Server::parsePlayerTransform(Packet& packet) { + PlayerID playerID = GetPlayerIDFromEndpoint(); + if (playerID == -1) { + return; + } + glm::vec3 position; glm::vec3 orientation; position.x = packet.ReadPrimitive(); @@ -464,11 +579,49 @@ void Server::parsePlayerTransform(Packet& packet) orientation.y = packet.ReadPrimitive(); orientation.z = packet.ReadPrimitive(); - PlayerID playerID = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); - EntityWrapper player(m_World, m_ConnectedPlayers.at(playerID).EntityID); + bool hasAssaultWeapon = packet.ReadPrimitive(); + int magazineAmmo; + int ammo; + if (hasAssaultWeapon) { + magazineAmmo = packet.ReadPrimitive(); + ammo = packet.ReadPrimitive(); + } + EntityWrapper player(m_World, m_ConnectedPlayers.at(playerID).EntityID); if (player.Valid()) { player["Transform"]["Position"] = position; player["Transform"]["Orientation"] = orientation; + + if (hasAssaultWeapon) { + player["AssaultWeapon"]["MagazineAmmo"] = magazineAmmo; + player["AssaultWeapon"]["Ammo"] = ammo; + } } } + +bool Server::shouldSendToClient(EntityWrapper childEntity) +{ + auto children = m_World->GetChildren(childEntity.ID); + for (auto it = children.first; it != children.second; it++) { + EntityWrapper child(m_World, it->second); + if(child.HasComponent("CapturePoint")) { + return true; + } + } + return childEntity.HasComponent("Player") || childEntity.FirstParentWithComponent("Player").Valid() + || childEntity.HasComponent("CapturePoint"); +} + +PlayerID Server::GetPlayerIDFromEndpoint() +{ + // check both tcp and udp connection + for (auto& kv : m_ConnectedPlayers) { + if ((kv.second.TCPAddress == m_Address + && kv.second.TCPPort == m_Port) + || (kv.second.Endpoint.address() == m_Address + && kv.second.Endpoint.port() == m_Port)) { + return kv.first; + } + } + return -1; +} \ No newline at end of file diff --git a/src/Engine/Network/TCPClient.cpp b/src/Engine/Network/TCPClient.cpp new file mode 100644 index 00000000..f3394d3d --- /dev/null +++ b/src/Engine/Network/TCPClient.cpp @@ -0,0 +1,117 @@ +#include "Network/TCPClient.h" + +using namespace boost::asio::ip; + +TCPClient::TCPClient() +{ +} + +TCPClient::~TCPClient() +{ +} + +void TCPClient::Connect(std::string playerName, std::string address, int port) +{ + if (m_Socket) { + if (m_IsConnected) { + Packet packet(MessageType::Connect, m_SendPacketID); + packet.WriteString(playerName); + Send(packet); + LOG_INFO("Connect message sent again!"); + } + } + else if (!m_IsConnected) { + boost::system::error_code error = boost::asio::error::host_not_found; + m_Endpoint = tcp::endpoint(boost::asio::ip::address::from_string(address), port); + m_Socket = std::unique_ptr(new tcp::socket(m_IOService)); + m_Socket->connect(m_Endpoint, error); + tcp::no_delay option(true); + m_Socket->set_option(option); + LOG_INFO(error.message().c_str()); + if (!error) { + m_IsConnected = true; + Packet packet(MessageType::Connect, m_SendPacketID); + packet.WriteString(playerName); + Send(packet); + LOG_INFO("Connect message sent!"); + } + // If error + else { + m_Socket->close(); + m_Socket = nullptr; + } + } +} + +void TCPClient::Disconnect() +{ + if (!m_IsConnected) { + return; + } + m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both); + m_Socket->close(); + m_Socket = nullptr; + m_IsConnected = false; +} + +void TCPClient::Receive(Packet& packet) +{ + size_t bytesRead = readBuffer(); + if (bytesRead > 0) { + packet.ReconstructFromData(m_ReadBuffer, bytesRead); + } +} + +size_t TCPClient::readBuffer() +{ + if (!m_Socket) { + return 0; + } + boost::system::error_code error; + // Read size of packet + m_Socket->receive(boost + ::asio::buffer((void*)m_ReadBuffer, sizeof(int)), + boost::asio::ip::tcp::socket::message_peek, error); + unsigned int sizeOfPacket = 0; + memcpy(&sizeOfPacket, m_ReadBuffer, sizeof(int)); + + // if the buffer is to small increase the size of it + // TODO if message is huge 1 time the buffer will not decrease. + if (sizeOfPacket > m_BufferSize) { + delete[] m_ReadBuffer; + m_ReadBuffer = new char[sizeOfPacket]; + m_BufferSize = sizeOfPacket; + } + // Read the rest of the message + size_t bytesReceived = m_Socket->read_some(boost + ::asio::buffer((void*)(m_ReadBuffer), sizeOfPacket), + error); + if (error) { + //LOG_ERROR("receive: %s", error.message().c_str()); + } + if (sizeOfPacket > 1000000) + LOG_WARNING("The packets received are bigger than 1MB"); + + return bytesReceived; +} + +void TCPClient::Send(Packet & packet) +{ + if (!m_Socket) { + LOG_WARNING("TCPClient::Send: Socket is null"); + return; + } + packet.UpdateSize(); + boost::system::error_code error; + m_Socket->send(boost::asio::buffer( + packet.Data(), + packet.Size()), 0, error); +} + +bool TCPClient::IsSocketAvailable() +{ + if (!m_Socket) { + return false; + } + return m_Socket->available(); +} \ No newline at end of file diff --git a/src/Engine/Network/TCPServer.cpp b/src/Engine/Network/TCPServer.cpp new file mode 100644 index 00000000..acd3d6d0 --- /dev/null +++ b/src/Engine/Network/TCPServer.cpp @@ -0,0 +1,127 @@ +#include "Network/TCPServer.h" +using namespace boost::asio::ip; + +TCPServer::TCPServer() +{ + acceptor = std::unique_ptr(new tcp::acceptor(m_IOService, tcp::endpoint(tcp::v4(), 27666))); + // Make the acceptor non-blocking so we wont get stuck in AcceptNewConnections(). + acceptor->non_blocking(true); + m_Port = GetPort(); + m_Address = GetAddress(); +} + +TCPServer::~TCPServer() +{ } + +void TCPServer::AcceptNewConnections(int& nextPlayerID, std::map& connectedPlayers) +{ + boost::system::error_code error; + boost::shared_ptr newSocket = boost::shared_ptr(new tcp::socket(m_IOService)); + acceptor->accept(*newSocket, error); + // If no error occured add new tcp connection + if (!error) { + // Add tcp socket to connections + boost::asio::ip::tcp::no_delay option(true); + newSocket->set_option(option); + PlayerDefinition pd; + pd.StopTime = std::clock(); + pd.TCPSocket = newSocket; + pd.TCPAddress = newSocket.get()->remote_endpoint().address(); + pd.TCPPort = newSocket.get()->remote_endpoint().port(); + connectedPlayers[nextPlayerID++] = pd; + } +} + +PlayerID TCPServer::getPlayerIDFromEndpoint(const std::map& connectedPlayers, + boost::asio::ip::address address, unsigned short port) +{ + for (auto& kv : connectedPlayers) { + if (kv.second.TCPAddress == address && + kv.second.TCPPort == port) { + return kv.first; + } + } + return -1; +} + +void TCPServer::Send(Packet & packet, PlayerDefinition & playerDefinition) +{ + packet.UpdateSize(); + try { + int bytesSent = playerDefinition.TCPSocket->send( + boost::asio::buffer(packet.Data(), packet.Size()), + 0); + } catch (const boost::system::system_error& e) { + // TODO: Clean up invalid endpoints out of m_ConnectedPlayers later + playerDefinition.Endpoint = boost::asio::ip::udp::endpoint(); + } +} + +void TCPServer::Send(Packet & packet) +{ + packet.UpdateSize(); + lastReceivedSocket->send( + boost::asio::buffer( + packet.Data(), + packet.Size()), + 0); +} + +void TCPServer::Disconnect() +{ +} + +int TCPServer::GetPort() +{ + return acceptor->local_endpoint().port(); +} + +std::string TCPServer::GetAddress() +{ + boost::asio::ip::tcp::resolver resolver(m_IOService); + boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), boost::asio::ip::host_name(), ""); + boost::asio::ip::tcp::resolver::iterator it = resolver.resolve(query); + boost::asio::ip::tcp::endpoint endpoint = *it; + return endpoint.address().to_string().c_str(); +} + +void TCPServer::Receive(Packet & packet, PlayerDefinition & playerDefinition) +{ + int bytesRead = readBuffer(playerDefinition); + if (bytesRead > 0) { + packet.ReconstructFromData(m_ReadBuffer, bytesRead); + } + lastReceivedSocket = playerDefinition.TCPSocket; +} + +int TCPServer::readBuffer(PlayerDefinition & playerDefinition) +{ + if (!playerDefinition.TCPSocket) { + return 0; + } + boost::system::error_code error; + // Read size of packet + playerDefinition.TCPSocket->receive(boost + ::asio::buffer((void*)m_ReadBuffer, sizeof(int)), + boost::asio::ip::tcp::socket::message_peek, error); + unsigned int sizeOfPacket = 0; + memcpy(&sizeOfPacket, m_ReadBuffer, sizeof(int)); + + // if the buffer is to small increase the size of it + if (sizeOfPacket > m_BufferSize) { + delete[] m_ReadBuffer; + m_ReadBuffer = new char[sizeOfPacket]; + m_BufferSize = sizeOfPacket; + } + // Read the rest of the message + size_t bytesReceived = playerDefinition.TCPSocket->read_some(boost + ::asio::buffer((void*)(m_ReadBuffer), sizeOfPacket), + error); + if (error) { + //LOG_ERROR("receive: %s", error.message().c_str()); + } + if (sizeOfPacket > 1000000) + LOG_WARNING("The packets received are bigger than 1MB"); + + return bytesReceived; +} \ No newline at end of file diff --git a/src/Engine/Network/UDPClient.cpp b/src/Engine/Network/UDPClient.cpp new file mode 100644 index 00000000..51c29920 --- /dev/null +++ b/src/Engine/Network/UDPClient.cpp @@ -0,0 +1,98 @@ +#include "Network/UDPClient.h" + +using namespace boost::asio::ip; + +UDPClient::UDPClient() +{ +} + +UDPClient::~UDPClient() +{ +} + +void UDPClient::Connect(std::string playerName, std::string address, int port) +{ + if (m_Socket) { + return; + } + m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address().from_string(address), port); + m_Socket = boost::shared_ptr(new boost::asio::ip::udp::socket(m_IOService)); + m_Socket->open(boost::asio::ip::udp::v4()); +} + +void UDPClient::Disconnect() +{ + +} + +void UDPClient::Receive(Packet& packet) +{ + int bytesRead = readBuffer(); + if (bytesRead > 0) { + packet.ReconstructFromData(m_ReadBuffer, bytesRead); + } +} + +int UDPClient::readBuffer() +{ + if (!m_Socket) { + return 0; + } + boost::system::error_code error; + // Read size of packet + m_Socket->receive(boost + ::asio::buffer((void*)m_ReadBuffer, sizeof(int)), + boost::asio::ip::udp::socket::message_peek, error); + int sizeOfPacket = 0; + memcpy(&sizeOfPacket, m_ReadBuffer, sizeof(int)); + + // if the buffer is to small increase the size of it + if (sizeOfPacket > m_BufferSize) { + delete[] m_ReadBuffer; + m_ReadBuffer = new char[sizeOfPacket]; + m_BufferSize = sizeOfPacket; + } + + size_t availableData = m_Socket->available(); + // Read the rest of the message + size_t bytesReceived = m_Socket->receive_from(boost + ::asio::buffer((void*)(m_ReadBuffer), + sizeOfPacket), + m_ReceiverEndpoint, 0, error); + if (error) { + //LOG_ERROR("receive: %s", error.message().c_str()); + } + if (sizeOfPacket > 1000000) + LOG_WARNING("The packets received are bigger than 1MB"); + + return bytesReceived; +} + +void UDPClient::Send(Packet& packet) +{ + packet.UpdateSize(); + m_Socket->send_to(boost::asio::buffer( + packet.Data(), + packet.Size()), + m_ReceiverEndpoint, 0); +} + +void UDPClient::Broadcast(Packet& packet, int port) +{ + packet.UpdateSize(); + m_Socket->set_option(boost::asio::socket_base::broadcast(true)); + m_Socket->send_to(boost::asio::buffer( + packet.Data(), + packet.Size()), + udp::endpoint(boost::asio::ip::address_v4().broadcast(), port) + , 0); + m_Socket->set_option(boost::asio::socket_base::broadcast(false)); +} + +bool UDPClient::IsSocketAvailable() +{ + if (!m_Socket) { + return false; + } + return m_Socket->available(); +} \ No newline at end of file diff --git a/src/Engine/Network/UDPServer.cpp b/src/Engine/Network/UDPServer.cpp new file mode 100644 index 00000000..635ebd4d --- /dev/null +++ b/src/Engine/Network/UDPServer.cpp @@ -0,0 +1,117 @@ +#include "Network/UDPServer.h" + +UDPServer::UDPServer() +{ + m_Socket = std::unique_ptr(new boost::asio::ip::udp::socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 27666))); +} + +UDPServer::UDPServer(int port) +{ + m_Socket = std::unique_ptr(new boost::asio::ip::udp::socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port))); +} + +UDPServer::~UDPServer() +{ } + +void UDPServer::Send(Packet& packet, PlayerDefinition & playerDefinition) +{ + packet.UpdateSize(); + try { + int bytesSent = m_Socket->send_to( + boost::asio::buffer(packet.Data(), packet.Size()), + playerDefinition.Endpoint, + 0); + } catch (const boost::system::system_error& e) { + // TODO: Clean up invalid endpoints out of m_ConnectedPlayers later + playerDefinition.Endpoint = boost::asio::ip::udp::endpoint(); + } +} +// Send back to endpoint of received packet +void UDPServer::Send(Packet & packet) +{ + packet.UpdateSize(); + m_Socket->send_to( + boost::asio::buffer( + packet.Data(), + packet.Size()), + m_ReceiverEndpoint, + 0); +} + +// Broadcasting respond specific logic +void UDPServer::Send(Packet & packet, boost::asio::ip::udp::endpoint endpoint) +{ + packet.UpdateSize(); + m_Socket->send_to( + boost::asio::buffer( + packet.Data(), + packet.Size()), + endpoint, + 0); +} + +// Broadcasting +void UDPServer::Broadcast(Packet & packet, int port) +{ + packet.UpdateSize(); + m_Socket->set_option(boost::asio::socket_base::broadcast(true)); + m_Socket->send_to( + boost::asio::buffer( + packet.Data(), + packet.Size()), + boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4().broadcast(),port), + 0); + m_Socket->set_option(boost::asio::socket_base::broadcast(false)); +} + +void UDPServer::Receive(Packet & packet, PlayerDefinition & playerDefinition) +{ + int bytesRead = readBuffer(); + if (bytesRead > 0) { + packet.ReconstructFromData(m_ReadBuffer, bytesRead); + } + playerDefinition.Endpoint = m_ReceiverEndpoint; +} + +bool UDPServer::IsSocketAvailable() +{ + return m_Socket->available(); +} + +int UDPServer::readBuffer() +{ + if (!m_Socket) { + return 0; + } + int addasdasd = m_Socket->available(); + boost::system::error_code error; + // Read size of packet + m_Socket->receive_from(boost + ::asio::buffer((void*)m_ReadBuffer, sizeof(int)), + m_ReceiverEndpoint, boost::asio::ip::udp::socket::message_peek, error); + unsigned int sizeOfPacket = 0; + memcpy(&sizeOfPacket, m_ReadBuffer, sizeof(int)); + + // if the buffer is to small increase the size of it + if (sizeOfPacket > m_BufferSize) { + delete[] m_ReadBuffer; + m_ReadBuffer = new char[sizeOfPacket]; + m_BufferSize = sizeOfPacket; + } + + // Read the rest of the message + size_t bytesReceived = m_Socket->receive_from(boost + ::asio::buffer((void*)(m_ReadBuffer), + sizeOfPacket), + m_ReceiverEndpoint, 0, error); + if (error) { + //LOG_ERROR("receive: %s", error.message().c_str()); + } + if (sizeOfPacket > 1000000) + LOG_WARNING("The packets received are bigger than 1MB"); + + return bytesReceived; +} + +void UDPServer::AcceptNewConnections(int& nextPlayerID, std::map& connectedPlayers) +{ } \ No newline at end of file diff --git a/src/Engine/Rendering/CubeMapPass.cpp b/src/Engine/Rendering/CubeMapPass.cpp new file mode 100644 index 00000000..75f5e1c9 --- /dev/null +++ b/src/Engine/Rendering/CubeMapPass.cpp @@ -0,0 +1,40 @@ +#include "Rendering/CubeMapPass.h" + +CubeMapPass::CubeMapPass(IRenderer* renderer) + :m_Renderer(renderer) +{ + LoadTextures("Nevada"); +} + +void CubeMapPass::LoadTextures(std::string input) +{ + if (m_PreviusCubeMapTexture != input) { + m_CubeMapTextures.clear(); + for (int i = 0; i < 6; i++) { + std::string str; + str = "Textures/Test/CubeMap/" + input + "/CubeMapTest0" + std::to_string(i) + ".png"; + Texture* img = ResourceManager::Load(str); + m_CubeMapTextures.push_back(img); + } + GenerateCubeMapTexture(); + } +} + +void CubeMapPass::GenerateCubeMapTexture() +{ + if (m_CubeMapTexture == -1) { + glGenTextures(1, &m_CubeMapTexture); + } + glBindTexture(GL_TEXTURE_CUBE_MAP, m_CubeMapTexture); + + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA32F, m_CubeMapTextures[0]->Width, m_CubeMapTextures[0]->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_CubeMapTextures[i]->Data); + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + GLERROR("Generate Cubemap"); +} + diff --git a/src/Engine/Rendering/DrawBloomPass.cpp b/src/Engine/Rendering/DrawBloomPass.cpp index 46612d5e..e8ad4cd5 100644 --- a/src/Engine/Rendering/DrawBloomPass.cpp +++ b/src/Engine/Rendering/DrawBloomPass.cpp @@ -19,16 +19,20 @@ void DrawBloomPass::InitializeTextures() void DrawBloomPass::InitializeShaderPrograms() { m_GaussianProgram_horiz = ResourceManager::Load("##GaussianProgramHoriz"); - m_GaussianProgram_horiz->AddShader(std::shared_ptr(new VertexShader("Shaders/Gaussian_horiz.vert.glsl"))); - m_GaussianProgram_horiz->AddShader(std::shared_ptr(new FragmentShader("Shaders/Gaussian_horiz.frag.glsl"))); - m_GaussianProgram_horiz->Compile(); - m_GaussianProgram_horiz->Link(); + if (m_GaussianProgram_horiz->GetHandle() == 0) { + m_GaussianProgram_horiz->AddShader(std::shared_ptr(new VertexShader("Shaders/Gaussian_horiz.vert.glsl"))); + m_GaussianProgram_horiz->AddShader(std::shared_ptr(new FragmentShader("Shaders/Gaussian_horiz.frag.glsl"))); + m_GaussianProgram_horiz->Compile(); + m_GaussianProgram_horiz->Link(); + } - m_GaussianProgram_vert = ResourceManager::Load("##GaussianProgramVert"); - m_GaussianProgram_vert->AddShader(std::shared_ptr(new VertexShader("Shaders/Gaussian_vert.vert.glsl"))); - m_GaussianProgram_vert->AddShader(std::shared_ptr(new FragmentShader("Shaders/Gaussian_vert.frag.glsl"))); - m_GaussianProgram_vert->Compile(); - m_GaussianProgram_vert->Link(); + m_GaussianProgram_vert = ResourceManager::Load("##GaussianProgramVert"); + if (m_GaussianProgram_vert->GetHandle() == 0) { + m_GaussianProgram_vert->AddShader(std::shared_ptr(new VertexShader("Shaders/Gaussian_vert.vert.glsl"))); + m_GaussianProgram_vert->AddShader(std::shared_ptr(new FragmentShader("Shaders/Gaussian_vert.frag.glsl"))); + m_GaussianProgram_vert->Compile(); + m_GaussianProgram_vert->Link(); + } } @@ -48,6 +52,7 @@ void DrawBloomPass::InitializeBuffers() void DrawBloomPass::ClearBuffer() { + GLERROR("PRE"); m_GaussianFrameBuffer_horiz.Bind(); glClearColor(0.f, 0.f, 0.f, 0.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -56,6 +61,7 @@ void DrawBloomPass::ClearBuffer() glClearColor(0.f, 0.f, 0.f, 0.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_GaussianFrameBuffer_vert.Unbind(); + GLERROR("END"); } void DrawBloomPass::Draw(GLuint texture) @@ -71,15 +77,12 @@ void DrawBloomPass::Draw(GLuint texture) //Horizontal pass, first use the given texture then save it to the horizontal framebuffer. m_GaussianFrameBuffer_horiz.Bind(); m_GaussianProgram_horiz->Bind(); - glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); - glBindVertexArray(m_ScreenQuad->VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer); glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex +1 , GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex); - //Iterate some times to make it more gaussian. for (int i = 1; i < m_iterations; i++) { //Vertical pass @@ -92,7 +95,6 @@ void DrawBloomPass::Draw(GLuint texture) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer); glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex +1 , GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex); - //horizontal pass m_GaussianFrameBuffer_horiz.Bind(); @@ -112,7 +114,6 @@ void DrawBloomPass::Draw(GLuint texture) m_GaussianProgram_vert->Bind(); glBindTexture(GL_TEXTURE_2D, m_GaussianTexture_horiz); - glBindVertexArray(m_ScreenQuad->VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer); glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex +1 @@ -121,6 +122,15 @@ void DrawBloomPass::Draw(GLuint texture) GLERROR("DrawBloomPass::Draw: END"); } + +void DrawBloomPass::OnWindowResize() +{ + GenerateTexture(&m_GaussianTexture_vert, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_RGB16F, GL_RGB, GL_FLOAT); + m_GaussianFrameBuffer_vert.Generate(); + GenerateTexture(&m_GaussianTexture_horiz, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_RGB16F, GL_RGB, GL_FLOAT); + m_GaussianFrameBuffer_horiz.Generate(); +} + void DrawBloomPass::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const { glGenTextures(1, texture); diff --git a/src/Engine/Rendering/DrawFinalPass.cpp b/src/Engine/Rendering/DrawFinalPass.cpp index 7360d064..d8f7275f 100644 --- a/src/Engine/Rendering/DrawFinalPass.cpp +++ b/src/Engine/Rendering/DrawFinalPass.cpp @@ -1,10 +1,11 @@ #include "Rendering/DrawFinalPass.h" -DrawFinalPass::DrawFinalPass(IRenderer* renderer, LightCullingPass* lightCullingPass) +DrawFinalPass::DrawFinalPass(IRenderer* renderer, LightCullingPass* lightCullingPass, CubeMapPass* cubeMapPass) + : m_Renderer(renderer) + , m_LightCullingPass(lightCullingPass) + , m_CubeMapPass(cubeMapPass) { //TODO: Make sure that uniforms are not sent into shader if not needed. - m_Renderer = renderer; - m_LightCullingPass = lightCullingPass; m_ShieldPixelRate = 8; InitializeTextures(); InitializeShaderPrograms(); @@ -27,6 +28,7 @@ void DrawFinalPass::InitializeFrameBuffers() glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height); GLERROR("RenderBuffer generation"); + GenerateTexture(&m_SceneTexture, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_RGB16F, GL_RGB, GL_FLOAT); //GenerateTexture(&m_BloomTexture, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewPortSize().Width, m_Renderer->GetViewPortSize().Height), GL_RGB16F, GL_RGB, GL_FLOAT); GenerateTexture(&m_BloomTexture, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_RGB16F, GL_RGB, GL_FLOAT); @@ -173,26 +175,28 @@ void DrawFinalPass::InitializeShaderPrograms() GLERROR("Creating DepthFill program"); } -void DrawFinalPass::Draw(RenderScene& scene) +void DrawFinalPass::Draw(RenderScene& scene, GLuint SSAOTexture) { GLERROR("Pre"); - DrawFinalPassState* state = new DrawFinalPassState(m_FinalPassFrameBuffer.GetHandle()); if (scene.ClearDepth) { - glClear(GL_DEPTH_BUFFER_BIT); + //glClear(GL_DEPTH_BUFFER_BIT); + state->Disable(GL_DEPTH_TEST); + state->DepthMask(GL_FALSE); } //TODO: Do we need check for this or will it be per scene always? glClearStencil(0x00); glClear(GL_STENCIL_BUFFER_BIT); //Fill depth buffer - - + state->StencilMask(0x00); - DrawModelRenderQueues(scene.Jobs.OpaqueObjects, scene); + DrawModelRenderQueues(scene.Jobs.OpaqueObjects, scene, SSAOTexture); GLERROR("OpaqueObjects"); - DrawModelRenderQueues(scene.Jobs.TransparentObjects, scene); + state->BlendFunc(GL_ONE, GL_ONE); + DrawModelRenderQueues(scene.Jobs.TransparentObjects, scene, SSAOTexture); GLERROR("TransparentObjects"); + state->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); DrawSprites(scene.Jobs.SpriteJob, scene); GLERROR("SpriteJobs"); @@ -206,11 +210,11 @@ void DrawFinalPass::Draw(RenderScene& scene) //Draw Opaque shielded objects state->StencilFunc(GL_NOTEQUAL, 1, 0xFF); state->StencilMask(0x00); - DrawModelRenderQueues(scene.Jobs.OpaqueShieldedObjects, scene); //might need changing + DrawModelRenderQueues(scene.Jobs.OpaqueShieldedObjects, scene, SSAOTexture); //might need changing GLERROR("Shielded Opaque object"); //Draw Transparen Shielded objects - DrawModelRenderQueues(scene.Jobs.TransparentShieldedObjects, scene); //might need changing + DrawModelRenderQueues(scene.Jobs.TransparentShieldedObjects, scene, SSAOTexture); //might need changing GLERROR("Shielded Transparent objects"); GLERROR("END"); @@ -241,14 +245,14 @@ void DrawFinalPass::Draw(RenderScene& scene) DrawShieldToStencilBuffer(scene.Jobs.ShieldObjects, scene); GLERROR("StencilPass"); - glClear(GL_DEPTH_BUFFER_BIT); + //glClear(GL_DEPTH_BUFFER_BIT); stateLowRes->Enable(GL_DEPTH_TEST); stateLowRes->StencilFunc(GL_LEQUAL, 1, 0xFF); stateLowRes->StencilMask(0x00); - DrawModelRenderQueues(scene.Jobs.OpaqueObjects, scene); + DrawModelRenderQueues(scene.Jobs.OpaqueObjects, scene, SSAOTexture); GLERROR("OpaqueObjects"); - DrawModelRenderQueues(scene.Jobs.TransparentObjects, scene); + DrawModelRenderQueues(scene.Jobs.TransparentObjects, scene, SSAOTexture); GLERROR("TransparentObjects"); glViewport(0, 0, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height); glScissor(0, 0, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height); @@ -258,20 +262,56 @@ void DrawFinalPass::Draw(RenderScene& scene) void DrawFinalPass::ClearBuffer() { + GLERROR("PRE"); m_FinalPassFrameBufferLowRes.Bind(); + GLERROR("Bind LowRes"); + glViewport(0, 0, m_Renderer->GetViewportSize().Width/m_ShieldPixelRate, m_Renderer->GetViewportSize().Height/m_ShieldPixelRate); glScissor(0, 0, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height); + GLERROR("ViewPort,Scissor LowRes"); + glClearColor(0.f, 0.f, 0.f, 0.f); + GLERROR("1"); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GLERROR("2"); + glDisable(GL_SCISSOR_TEST); + GLERROR("3"); + m_FinalPassFrameBufferLowRes.Unbind(); + GLERROR("prebind HighRes"); m_FinalPassFrameBuffer.Bind(); + GLERROR("Bind HighRes"); glViewport(0, 0, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height); glScissor(0, 0, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height); + GLERROR("ViewPort,Scissor LowRes"); glClearColor(0.f, 0.f, 0.f, 0.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_FinalPassFrameBuffer.Unbind(); + GLERROR("END"); +} + + +void DrawFinalPass::OnWindowResize() +{ + //InitializeFrameBuffers(); + glBindRenderbuffer(GL_RENDERBUFFER, m_DepthBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height); + + GenerateTexture(&m_SceneTexture, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_RGB16F, GL_RGB, GL_FLOAT); + GenerateTexture(&m_BloomTexture, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_RGB16F, GL_RGB, GL_FLOAT); + m_FinalPassFrameBuffer.Generate(); + + + glBindRenderbuffer(GL_RENDERBUFFER, m_DepthBufferLowRes); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, (int)(m_Renderer->GetViewportSize().Width/m_ShieldPixelRate), (int)(m_Renderer->GetViewportSize().Height/m_ShieldPixelRate)); + + GenerateTexture(&m_SceneTextureLowRes, GL_CLAMP_TO_EDGE, GL_NEAREST, glm::vec2((int)(m_Renderer->GetViewportSize().Width/m_ShieldPixelRate), (int)(m_Renderer->GetViewportSize().Height/m_ShieldPixelRate)), GL_RGB16F, GL_RGB, GL_FLOAT); + GenerateTexture(&m_BloomTextureLowRes, GL_CLAMP_TO_EDGE, GL_NEAREST, glm::vec2((int)(m_Renderer->GetViewportSize().Width/m_ShieldPixelRate), (int)(m_Renderer->GetViewportSize().Height/m_ShieldPixelRate)), GL_RGB16F, GL_RGB, GL_FLOAT); + m_FinalPassFrameBufferLowRes.Generate(); + GLERROR("Error changing texture resolutions"); } void DrawFinalPass::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const @@ -300,7 +340,7 @@ void DrawFinalPass::GenerateMipMapTexture(GLuint* texture, GLenum wrapping, glm: GLERROR("MipMap Texture initialization failed"); } -void DrawFinalPass::DrawModelRenderQueues(std::list>& jobs, RenderScene& scene) +void DrawFinalPass::DrawModelRenderQueues(std::list>& jobs, RenderScene& scene, GLuint SSAOTexture) { GLuint forwardHandle = m_ForwardPlusProgram->GetHandle(); GLERROR("forwardHandle"); @@ -323,6 +363,9 @@ void DrawFinalPass::DrawModelRenderQueues(std::list>& glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, m_LightCullingPass->LightGridSSBO()); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, m_LightCullingPass->LightIndexSSBO()); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, SSAOTexture); + for (auto &job : jobs) { auto explosionEffectJob = std::dynamic_pointer_cast(job); if (explosionEffectJob) { @@ -331,13 +374,18 @@ void DrawFinalPass::DrawModelRenderQueues(std::list>& case RawModel::MaterialType::SingleTextures: { if (explosionEffectJob->Model->IsSkinned()) { + m_ExplosionEffectSkinnedProgram->Bind(); GLERROR("Bind ExplosionEffectSkinned program"); //bind uniforms BindExplosionUniforms(explosionSkinnedHandle, explosionEffectJob, scene); //bind textures BindExplosionTextures(explosionSkinnedHandle, explosionEffectJob); - if (explosionEffectJob->BlendTree != nullptr) { + glActiveTexture(GL_TEXTURE5); + glBindTexture(GL_TEXTURE_CUBE_MAP, m_CubeMapPass->m_CubeMapTexture); + glUniform3fv(glGetUniformLocation(forwardHandle, "CameraPosition"), 1, glm::value_ptr(scene.Camera->Position())); + + if (explosionEffectJob->BlendTree != nullptr) { std::vector frameBones; frameBones = explosionEffectJob->BlendTree->GetFinalPose(); glUniformMatrix4fv(glGetUniformLocation(explosionSkinnedHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); @@ -349,6 +397,10 @@ void DrawFinalPass::DrawModelRenderQueues(std::list>& BindExplosionUniforms(explosionHandle, explosionEffectJob, scene); //bind textures BindExplosionTextures(explosionHandle, explosionEffectJob); + glActiveTexture(GL_TEXTURE5); + glBindTexture(GL_TEXTURE_CUBE_MAP, m_CubeMapPass->m_CubeMapTexture); + glUniform3fv(glGetUniformLocation(forwardHandle, "CameraPosition"), 1, glm::value_ptr(scene.Camera->Position())); + } break; } @@ -404,8 +456,11 @@ void DrawFinalPass::DrawModelRenderQueues(std::list>& BindModelUniforms(forwardSkinnedHandle, modelJob, scene); //bind textures BindModelTextures(forwardSkinnedHandle, modelJob); - - if (modelJob->BlendTree != nullptr) { + glActiveTexture(GL_TEXTURE5); + glBindTexture(GL_TEXTURE_CUBE_MAP, m_CubeMapPass->m_CubeMapTexture); + glUniform3fv(glGetUniformLocation(forwardHandle, "CameraPosition"), 1, glm::value_ptr(scene.Camera->Position())); + + if (modelJob->BlendTree != nullptr) { std::vector frameBones; frameBones = modelJob->BlendTree->GetFinalPose(); glUniformMatrix4fv(glGetUniformLocation(forwardSkinnedHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); @@ -417,6 +472,9 @@ void DrawFinalPass::DrawModelRenderQueues(std::list>& BindModelUniforms(forwardHandle, modelJob, scene); //bind textures BindModelTextures(forwardHandle, modelJob); + glActiveTexture(GL_TEXTURE5); + glBindTexture(GL_TEXTURE_CUBE_MAP, m_CubeMapPass->m_CubeMapTexture); + glUniform3fv(glGetUniformLocation(forwardHandle, "CameraPosition"), 1, glm::value_ptr(scene.Camera->Position())); } break; } @@ -648,14 +706,14 @@ void DrawFinalPass::DrawSprites(std::list>&jobs, Rend glUniform4fv(glGetUniformLocation(shaderHandle, "FillColor"), 1, glm::value_ptr(spriteJob->FillColor)); glUniform1f(glGetUniformLocation(shaderHandle, "FillPercentage"), spriteJob->FillPercentage); - glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE1); if (spriteJob->DiffuseTexture != nullptr) { glBindTexture(GL_TEXTURE_2D, spriteJob->DiffuseTexture->m_Texture); } else { glBindTexture(GL_TEXTURE_2D, m_ErrorTexture->m_Texture); } - glActiveTexture(GL_TEXTURE1); + glActiveTexture(GL_TEXTURE2); if (spriteJob->IncandescenceTexture != nullptr) { glBindTexture(GL_TEXTURE_2D, spriteJob->IncandescenceTexture->m_Texture); } else { @@ -668,9 +726,6 @@ void DrawFinalPass::DrawSprites(std::list>&jobs, Rend glDrawElements(GL_TRIANGLES, spriteJob->EndIndex - spriteJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(spriteJob->StartIndex*sizeof(unsigned int))); } } - - - // m_SpriteProgram->Unbind(); } @@ -684,7 +739,7 @@ void DrawFinalPass::BindExplosionUniforms(GLuint shaderHandle, std::shared_ptrProjectionMatrix())); GLERROR("Bind 4 uniform"); - glUniform2f(glGetUniformLocation(shaderHandle, "ScreenDimensions"), m_Renderer->Resolution().Width, m_Renderer->Resolution().Height); + glUniform2f(glGetUniformLocation(shaderHandle, "ScreenDimensions"), m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height); GLERROR("Bind 5 uniform"); glUniform3fv(glGetUniformLocation(shaderHandle, "ExplosionOrigin"), 1, glm::value_ptr(job->ExplosionOrigin)); @@ -717,6 +772,8 @@ void DrawFinalPass::BindExplosionUniforms(GLuint shaderHandle, std::shared_ptrFillPercentage); GLERROR("Bind 19 uniform"); glUniform4fv(glGetUniformLocation(shaderHandle, "AmbientColor"), 1, glm::value_ptr(scene.AmbientColor)); + GLERROR("Bind 20 uniform"); + glUniform1f(glGetUniformLocation(shaderHandle, "GlowIntensity"), job->GlowIntensity); GLERROR("END"); } @@ -734,7 +791,7 @@ void DrawFinalPass::BindModelUniforms(GLuint shaderHandle, std::shared_ptrResolution().Width, m_Renderer->Resolution().Height); + glUniform2f(Location_ScreenDimensions, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height); GLERROR("Bind 5 uniform"); GLint Location_FillPercentage = glGetUniformLocation(shaderHandle, "FillPercentage"); @@ -752,16 +809,23 @@ void DrawFinalPass::BindModelUniforms(GLuint shaderHandle, std::shared_ptrGlowIntensity); + GLERROR("END"); } void DrawFinalPass::BindExplosionTextures(GLuint shaderHandle, std::shared_ptr& job) { + + switch (job->Type) { case RawModel::MaterialType::SingleTextures: case RawModel::MaterialType::Basic: { - glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE1); if (job->DiffuseTexture.size() > 0 && job->DiffuseTexture[0]->Texture != nullptr) { glBindTexture(GL_TEXTURE_2D, job->DiffuseTexture[0]->Texture->m_Texture); glUniform2fv(glGetUniformLocation(shaderHandle, "DiffuseUVRepeat"), 1, glm::value_ptr(job->DiffuseTexture[0]->UVRepeat)); @@ -771,7 +835,7 @@ void DrawFinalPass::BindExplosionTextures(GLuint shaderHandle, std::shared_ptrNormalTexture.size() > 0 && job->NormalTexture[0]->Texture != nullptr) { glBindTexture(GL_TEXTURE_2D, job->NormalTexture[0]->Texture->m_Texture); glUniform2fv(glGetUniformLocation(shaderHandle, "NormalUVRepeat"), 1, glm::value_ptr(job->NormalTexture[0]->UVRepeat)); @@ -781,7 +845,7 @@ void DrawFinalPass::BindExplosionTextures(GLuint shaderHandle, std::shared_ptrSpecularTexture.size() > 0 && job->SpecularTexture[0]->Texture != nullptr) { glBindTexture(GL_TEXTURE_2D, job->SpecularTexture[0]->Texture->m_Texture); glUniform2fv(glGetUniformLocation(shaderHandle, "SpecularUVRepeat"), 1, glm::value_ptr(job->SpecularTexture[0]->UVRepeat)); @@ -791,7 +855,7 @@ void DrawFinalPass::BindExplosionTextures(GLuint shaderHandle, std::shared_ptrIncandescenceTexture.size() > 0 && job->IncandescenceTexture[0]->Texture != nullptr) { glBindTexture(GL_TEXTURE_2D, job->IncandescenceTexture[0]->Texture->m_Texture); glUniform2fv(glGetUniformLocation(shaderHandle, "GlowUVRepeat"), 1, glm::value_ptr(job->IncandescenceTexture[0]->UVRepeat)); @@ -804,7 +868,7 @@ void DrawFinalPass::BindExplosionTextures(GLuint shaderHandle, std::shared_ptrSplatMap->Texture->m_Texture); int texturePosition = GL_TEXTURE1; @@ -879,7 +943,7 @@ void DrawFinalPass::BindModelTextures(GLuint shaderHandle, std::shared_ptrDiffuseTexture.size() > 0 && job->DiffuseTexture[0]->Texture != nullptr) { glBindTexture(GL_TEXTURE_2D, job->DiffuseTexture[0]->Texture->m_Texture); glUniform2fv(glGetUniformLocation(shaderHandle, "DiffuseUVRepeat"), 1, glm::value_ptr(job->DiffuseTexture[0]->UVRepeat)); @@ -889,7 +953,7 @@ void DrawFinalPass::BindModelTextures(GLuint shaderHandle, std::shared_ptrNormalTexture.size() > 0 && job->NormalTexture[0]->Texture != nullptr) { glBindTexture(GL_TEXTURE_2D, job->NormalTexture[0]->Texture->m_Texture); glUniform2fv(glGetUniformLocation(shaderHandle, "NormalUVRepeat"), 1, glm::value_ptr(job->NormalTexture[0]->UVRepeat)); @@ -899,7 +963,7 @@ void DrawFinalPass::BindModelTextures(GLuint shaderHandle, std::shared_ptrSpecularTexture.size() > 0 && job->SpecularTexture[0]->Texture != nullptr) { glBindTexture(GL_TEXTURE_2D, job->SpecularTexture[0]->Texture->m_Texture); glUniform2fv(glGetUniformLocation(shaderHandle, "SpecularUVRepeat"), 1, glm::value_ptr(job->SpecularTexture[0]->UVRepeat)); @@ -909,7 +973,7 @@ void DrawFinalPass::BindModelTextures(GLuint shaderHandle, std::shared_ptrIncandescenceTexture.size() > 0 && job->IncandescenceTexture[0]->Texture != nullptr) { glBindTexture(GL_TEXTURE_2D, job->IncandescenceTexture[0]->Texture->m_Texture); glUniform2fv(glGetUniformLocation(shaderHandle, "GlowUVRepeat"), 1, glm::value_ptr(job->IncandescenceTexture[0]->UVRepeat)); @@ -922,10 +986,10 @@ void DrawFinalPass::BindModelTextures(GLuint shaderHandle, std::shared_ptrSplatMap->Texture->m_Texture); - int texturePosition = GL_TEXTURE1; + int texturePosition = GL_TEXTURE2; //Bind 5 diffuse textures std::string UniformName = "DiffuseUVRepeat"; diff --git a/src/Engine/Rendering/FrameBuffer.cpp b/src/Engine/Rendering/FrameBuffer.cpp index c0be4cb1..794fb84e 100644 --- a/src/Engine/Rendering/FrameBuffer.cpp +++ b/src/Engine/Rendering/FrameBuffer.cpp @@ -72,8 +72,8 @@ void FrameBuffer::Generate() GLenum* bufferTextures = &attachments[0]; glDrawBuffers(attachments.size(), bufferTextures); - if(GLERROR("4")) { - printf("hello"); + if (GLERROR("GLBufferAttachement error")) { + printf(": AttachmentSize %i", attachments.size()); } if (GLenum frameBufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { diff --git a/src/Engine/Rendering/LightCullingPass.cpp b/src/Engine/Rendering/LightCullingPass.cpp index 0ae359db..1ce1f88c 100644 --- a/src/Engine/Rendering/LightCullingPass.cpp +++ b/src/Engine/Rendering/LightCullingPass.cpp @@ -110,6 +110,34 @@ void LightCullingPass::FillLightList(RenderScene& scene) } } + +void LightCullingPass::OnWindowResize() +{ + SetSSBOSizes(); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_FrustumSSBO); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(Frustum)*m_NumberOfTiles, nullptr, GL_DYNAMIC_COPY); + GLERROR("m_FrustumSSBO"); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightSSBO); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(LightSource) * 200, nullptr, GL_DYNAMIC_COPY); + GLERROR("m_LightSSBO"); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightGridSSBO); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(LightGrid)*m_NumberOfTiles, nullptr, GL_DYNAMIC_COPY); + GLERROR("m_LightGridSSBO"); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightOffsetSSBO); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(m_LightOffset), &m_LightOffset, GL_DYNAMIC_COPY); + GLERROR("m_LightOffsetSSBO"); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightIndexSSBO); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(float)*m_NumberOfTiles*MAX_LIGHTS_PER_TILE, m_LightIndex, GL_DYNAMIC_COPY); + GLERROR("m_LightIndexSSBO"); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); +} + void LightCullingPass::InitializeSSBOs() { glGenBuffers(1, &m_FrustumSSBO); diff --git a/src/Engine/Rendering/PickingPass.cpp b/src/Engine/Rendering/PickingPass.cpp index 4d156e46..703a8c00 100644 --- a/src/Engine/Rendering/PickingPass.cpp +++ b/src/Engine/Rendering/PickingPass.cpp @@ -15,19 +15,20 @@ PickingPass::~PickingPass() } + + void PickingPass::InitializeTextures() { GenerateTexture(&m_PickingTexture, GL_CLAMP_TO_BORDER, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_RG8, GL_RG, GL_UNSIGNED_BYTE); + + GenerateTexture(&m_DepthBuffer, GL_CLAMP_TO_BORDER, GL_NEAREST, + glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8); } void PickingPass::InitializeFrameBuffers() { - glGenRenderbuffers(1, &m_DepthBuffer); - glBindRenderbuffer(GL_RENDERBUFFER, m_DepthBuffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height); - - m_PickingBuffer.AddResource(std::shared_ptr(new RenderBuffer(&m_DepthBuffer, GL_DEPTH_ATTACHMENT))); + m_PickingBuffer.AddResource(std::shared_ptr(new Texture2D(&m_DepthBuffer, GL_DEPTH_ATTACHMENT))); m_PickingBuffer.AddResource(std::shared_ptr(new Texture2D(&m_PickingTexture, GL_COLOR_ATTACHMENT0))); m_PickingBuffer.Generate(); } @@ -42,26 +43,29 @@ void PickingPass::InitializeShaderPrograms() m_PickingProgram->BindFragDataLocation(0, "TextureFragment"); m_PickingProgram->Link(); - m_PickingSkinnedProgram = ResourceManager::Load("#PickingSkinnedProgram"); + m_PickingSkinnedProgram = ResourceManager::Load("#PickingSkinnedProgram"); - m_PickingSkinnedProgram->AddShader(std::shared_ptr(new VertexShader("Shaders/PickingSkinned.vert.glsl"))); - m_PickingSkinnedProgram->AddShader(std::shared_ptr(new FragmentShader("Shaders/Picking.frag.glsl"))); - m_PickingSkinnedProgram->Compile(); - m_PickingSkinnedProgram->BindFragDataLocation(0, "TextureFragment"); - m_PickingSkinnedProgram->Link(); + m_PickingSkinnedProgram->AddShader(std::shared_ptr(new VertexShader("Shaders/PickingSkinned.vert.glsl"))); + m_PickingSkinnedProgram->AddShader(std::shared_ptr(new FragmentShader("Shaders/Picking.frag.glsl"))); + m_PickingSkinnedProgram->Compile(); + m_PickingSkinnedProgram->BindFragDataLocation(0, "TextureFragment"); + m_PickingSkinnedProgram->Link(); } void PickingPass::Draw(RenderScene& scene) { + GLERROR("PRE"); PickingPassState* state = new PickingPassState(m_PickingBuffer.GetHandle()); - + //TODO: Render: Add code for more jobs than modeljobs. GLuint shaderHandle = m_PickingProgram->GetHandle(); - GLuint shaderSkinnedHandle = m_PickingSkinnedProgram->GetHandle(); + GLuint shaderSkinnedHandle = m_PickingSkinnedProgram->GetHandle(); m_PickingProgram->Bind(); if (scene.ClearDepth) { - glClear(GL_DEPTH_BUFFER_BIT); + //glClear(GL_DEPTH_BUFFER_BIT); + state->Disable(GL_DEPTH_TEST); + state->DepthMask(GL_FALSE); } m_Camera = scene.Camera; @@ -92,26 +96,25 @@ void PickingPass::Draw(RenderScene& scene) m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo; - if (modelJob->Model->IsSkinned()) - { - m_PickingSkinnedProgram->Bind(); - glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); - glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); - glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); - glUniform2fv(glGetUniformLocation(shaderSkinnedHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); + if (modelJob->Model->IsSkinned()) { + m_PickingSkinnedProgram->Bind(); + glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); + glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); + glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); + glUniform2fv(glGetUniformLocation(shaderSkinnedHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); if (modelJob->BlendTree != nullptr) { std::vector frameBones; frameBones = modelJob->BlendTree->GetFinalPose(); glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); - } - } else { + } + } else { m_PickingProgram->Bind(); - glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); - glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); - glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); - glUniform2fv(glGetUniformLocation(shaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); - } + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); + glUniform2fv(glGetUniformLocation(shaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); + } glBindVertexArray(modelJob->Model->VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer); @@ -119,7 +122,7 @@ void PickingPass::Draw(RenderScene& scene) } } - for (auto &job : scene.Jobs.TransparentObjects) { + /* for (auto &job : scene.Jobs.TransparentObjects) { auto modelJob = std::dynamic_pointer_cast(job); int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] }; @@ -170,7 +173,7 @@ void PickingPass::Draw(RenderScene& scene) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer); glDrawElements(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(modelJob->StartIndex * sizeof(unsigned int))); } - } + }*/ for (auto &job : scene.Jobs.OpaqueShieldedObjects) { auto modelJob = std::dynamic_pointer_cast(job); @@ -199,7 +202,7 @@ void PickingPass::Draw(RenderScene& scene) m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo; - if(modelJob->Model->IsSkinned()) { + if (modelJob->Model->IsSkinned()) { m_PickingSkinnedProgram->Bind(); glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); @@ -226,7 +229,53 @@ void PickingPass::Draw(RenderScene& scene) } } - for (auto &job : scene.Jobs.TransparentShieldedObjects) { + for (auto& job : scene.Jobs.SpriteJob) { + auto spriteJob = std::dynamic_pointer_cast(job); + if (!spriteJob->Pickable) { + continue; + } + RenderState jobState; + + if (spriteJob) { + if (spriteJob->Depth == 0) { + jobState.Disable(GL_DEPTH_TEST); + } + int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] }; + + PickingInfo pickInfo; + pickInfo.Entity = spriteJob->Entity; + pickInfo.World = spriteJob->World; + pickInfo.Camera = scene.Camera; + + auto color = m_EntityColors.find(std::make_tuple(pickInfo.Entity, pickInfo.World, pickInfo.Camera)); + if (color != m_EntityColors.end()) { + pickColor[0] = color->second[0]; + pickColor[1] = color->second[1]; + } else { + m_EntityColors[std::make_tuple(pickInfo.Entity, pickInfo.World, pickInfo.Camera)] = glm::ivec2(pickColor[0], pickColor[1]); + if (m_ColorCounter[0] > 255) { + m_ColorCounter[0] = 0; + m_ColorCounter[1] += 1; + } else { + m_ColorCounter[0] += 1; + } + } + + m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo; + + m_PickingProgram->Bind(); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(spriteJob->Matrix)); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); + glUniform2fv(glGetUniformLocation(shaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); + + glBindVertexArray(spriteJob->Model->VAO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, spriteJob->Model->ElementBuffer); + glDrawElements(GL_TRIANGLES, spriteJob->EndIndex - spriteJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(spriteJob->StartIndex * sizeof(unsigned int))); + } + } + + /* for (auto &job : scene.Jobs.TransparentShieldedObjects) { auto modelJob = std::dynamic_pointer_cast(job); if (modelJob) { @@ -256,7 +305,7 @@ void PickingPass::Draw(RenderScene& scene) if (modelJob->Model->IsSkinned()) { m_PickingSkinnedProgram->Bind(); - + glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); @@ -277,25 +326,24 @@ void PickingPass::Draw(RenderScene& scene) glUniform2fv(glGetUniformLocation(shaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); } - + glBindVertexArray(modelJob->Model->VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer); glDrawElements(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(modelJob->StartIndex * sizeof(unsigned int))); } - } - + }*/ + m_PickingBuffer.Unbind(); GLERROR("PickingPass Error"); delete state; } - - void PickingPass::ClearPicking() { + GLERROR("PRE"); m_PickingColorsToEntity.clear(); m_EntityColors.clear(); m_ColorCounter[0] = 0; @@ -305,6 +353,14 @@ void PickingPass::ClearPicking() glClearColor(0.f, 0.f, 0.f, 0.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_PickingBuffer.Unbind(); + GLERROR("END"); +} + + +void PickingPass::OnWindowResize() +{ + InitializeTextures(); + m_PickingBuffer.Generate(); } PickData PickingPass::Pick(glm::vec2 screenCoord) diff --git a/src/Engine/Rendering/PickingPassState.cpp b/src/Engine/Rendering/PickingPassState.cpp index 0c4f4aca..f2d42bff 100644 --- a/src/Engine/Rendering/PickingPassState.cpp +++ b/src/Engine/Rendering/PickingPassState.cpp @@ -3,9 +3,9 @@ PickingPassState::PickingPassState(GLuint frameBuffer) { - GLERROR("---2"); + GLERROR("PRE"); BindFramebuffer(frameBuffer); - GLERROR("---3"); + GLERROR("Bind Framebuffer"); Enable(GL_DEPTH_TEST); Enable(GL_CULL_FACE); Disable(GL_BLEND); @@ -13,6 +13,7 @@ PickingPassState::PickingPassState(GLuint frameBuffer) glm::vec4 clearColor = glm::vec4(0.f); //ClearColor(clearColor); //Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GLERROR("END"); } PickingPassState::~PickingPassState() diff --git a/src/Engine/Rendering/RenderSystem.cpp b/src/Engine/Rendering/RenderSystem.cpp index 7beb85db..7b0ea84f 100644 --- a/src/Engine/Rendering/RenderSystem.cpp +++ b/src/Engine/Rendering/RenderSystem.cpp @@ -52,6 +52,101 @@ void RenderSystem::fillSprites(std::list>& jobs, Worl continue; } + glm::mat4 modelMatrix; + + // See a sprite is an SpriteIndicator + bool isIndicator = false; + if (world->HasComponent(entity.ID, "SpriteIndicator")) + { + auto indicator = entity["SpriteIndicator"]; + + float minScale = (float)(double)indicator["MinScale"]; + bool hasTeam = indicator["VisibleForSingleTeamOnly"]; + isIndicator = true; + glm::vec3 pos = Transform::AbsolutePosition(entity); + + + EntityWrapper entityTeam; + if (hasTeam && (entity.HasComponent("Team") || entity.FirstParentWithComponent("Team").Valid()) && m_LocalPlayer.World != nullptr) { + if (!entity.HasComponent("Team")) { + entityTeam = entity.FirstParentWithComponent("Team"); + } + else { + entityTeam = entity; + } + + ComponentWrapper& entityTeamComponent = entityTeam["Team"]; + ComponentWrapper& localComponent = m_LocalPlayer["Team"]; + int entityTeamInt = entityTeamComponent["Team"]; + int localComponentInt = localComponent["Team"]; + int SpectatorInt = localComponent["Team"].Enum("Spectator"); + if (entityTeamInt != localComponentInt && localComponentInt != SpectatorInt) { + continue; + } + } + + // Code for check if sprite is inside or outside of screen + //glm::vec4 projectedPos = m_Camera->ProjectionMatrix() * m_Camera->ViewMatrix() * glm::vec4(pos, 1.0f); + //projectedPos /= projectedPos.w; + //// Check if inside of outside of screen. + //if (projectedPos.x < -1.0f || projectedPos.x > 1.0f || projectedPos.y < -1.0f || projectedPos.y > 1.0f) { + // // is outside of screen + //} else { + // // is inside of screen + //} + + + glm::vec3 zAxis = glm::vec3(0.0f, 1.0f, 0.0f); + glm::vec3 normal = pos - m_Camera->Position(); + + //float distance = glm::length(normal); + //if (distance < minDistance) { + // pos = pos - glm::normalize(normal) * (distance - minDistance); + //} else if (distance > maxDistance) { + // pos = pos - glm::normalize(normal) * (distance - maxDistance); + //} + normal.y = 0; + normal = glm::normalize(normal); + glm::vec3 right = glm::cross(normal, zAxis); + glm::vec3 up = glm::cross(right, normal); + + modelMatrix[0][0] = right.x; + modelMatrix[0][1] = right.y; + modelMatrix[0][2] = right.z; + modelMatrix[0][3] = 0.0f; + + modelMatrix[1][0] = zAxis.x; + modelMatrix[1][1] = zAxis.y; + modelMatrix[1][2] = zAxis.z; + modelMatrix[1][3] = 0.0f; + + modelMatrix[2][0] = normal.x; + modelMatrix[2][1] = normal.y; + modelMatrix[2][2] = normal.z; + modelMatrix[2][3] = 0.0f; + + modelMatrix[3][0] = pos.x; + modelMatrix[3][1] = pos.y; + modelMatrix[3][2] = pos.z; + modelMatrix[3][3] = 1.0f; + + glm::mat4 tranformationMatrix = modelMatrix * glm::scale(Transform::AbsoluteScale(entity)); + glm::vec4 tmp = tranformationMatrix * glm::vec4(glm::vec3(0.5, 0.5, 0), 1.0f); + glm::vec2 projectedTopRight = m_Camera->WorldToScreen(glm::vec3(tmp), m_Renderer->GetViewportSize()); + tmp = tranformationMatrix * glm::vec4(glm::vec3(-0.5, -0.5, 0), 1.0f); + glm::vec2 projectedBottomLeft = m_Camera->WorldToScreen(glm::vec3(tmp), m_Renderer->GetViewportSize()); + + float diag = glm::length(projectedBottomLeft - projectedTopRight); + if (diag < minScale) { + tranformationMatrix = tranformationMatrix * glm::scale(glm::vec3(minScale / diag, minScale / diag, minScale / diag)); + } + modelMatrix = tranformationMatrix; + } + else { + modelMatrix = Transform::ModelMatrix(entity.ID, world); + } + + std::string diffuseResource = cSprite["DiffuseTexture"]; std::string glowResource = cSprite["GlowMap"]; bool depthSorted = cSprite["DepthSort"]; @@ -67,11 +162,7 @@ void RenderSystem::fillSprites(std::list>& jobs, Worl fillColor = (glm::vec4)fillComponent["Color"]; } - glm::mat4 modelMatrix = Transform::ModelMatrix(entity.ID, world); - //modelMatrix *= m_Camera->BillboardMatrix(); - - - std::shared_ptr spriteJob = std::shared_ptr(new SpriteJob(cSprite, m_Camera, modelMatrix, world, fillColor, fillPercentage, depthSorted)); + std::shared_ptr spriteJob = std::shared_ptr(new SpriteJob(cSprite, m_Camera, modelMatrix, world, fillColor, fillPercentage, depthSorted, isIndicator)); jobs.push_back(spriteJob); } @@ -79,7 +170,6 @@ void RenderSystem::fillSprites(std::list>& jobs, Worl bool RenderSystem::isEntityVisible(EntityWrapper& entity) { - // Only render children of a camera if that camera is currently active if (isChildOfACamera(entity) && !isChildOfCurrentCamera(entity)) { return false; @@ -87,10 +177,13 @@ bool RenderSystem::isEntityVisible(EntityWrapper& entity) // Hide things parented to local player if they have the HiddenFromLocalPlayer component bool outOfBodyExperience = ResourceManager::Load("Config.ini")->Get("Debug.OutOfBodyExperience", false); - if (entity.HasComponent("HiddenForLocalPlayer") && (entity == m_LocalPlayer || entity.IsChildOf(m_LocalPlayer)) && !outOfBodyExperience) { + if ( + (entity.HasComponent("HiddenForLocalPlayer") || entity.FirstParentWithComponent("HiddenForLocalPlayer").Valid()) + && (entity == m_LocalPlayer || entity.IsChildOf(m_LocalPlayer)) + && !outOfBodyExperience + ) { return false; } - return true; } diff --git a/src/Engine/Rendering/Renderer.cpp b/src/Engine/Rendering/Renderer.cpp index 85741be4..251bba2b 100644 --- a/src/Engine/Rendering/Renderer.cpp +++ b/src/Engine/Rendering/Renderer.cpp @@ -1,5 +1,7 @@ #include "Rendering/Renderer.h" +std::unordered_map Renderer::m_WindowToRenderer; + void Renderer::Initialize() { InitializeWindow(); @@ -12,7 +14,6 @@ void Renderer::Initialize() m_TextPass = new TextPass(); m_TextPass->Initialize(); - /* m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.obj"); m_UnitQuad = ResourceManager::Load("Models/Core/UnitQuad.obj"); m_UnitSphere = ResourceManager::Load("Models/Core/UnitSphere.obj");*/ @@ -20,6 +21,18 @@ void Renderer::Initialize() m_ImGuiRenderPass = new ImGuiRenderPass(this, m_EventBroker); } +void Renderer::glfwFrameBufferCallback(GLFWwindow* window, int width, int height) +{ + glViewport(0, 0, width, height); + Renderer* currentRenderer = m_WindowToRenderer[window]; + currentRenderer->m_ViewportSize = Rectangle(width, height); + currentRenderer->m_DrawFinalPass->OnWindowResize(); + currentRenderer->m_LightCullingPass->OnWindowResize(); + currentRenderer->m_PickingPass->OnWindowResize(); + currentRenderer->m_DrawBloomPass->OnWindowResize(); + currentRenderer->m_SSAOPass->OnWindowResize(); +} + void Renderer::InitializeWindow() { // Initialize GLFW @@ -39,6 +52,7 @@ void Renderer::InitializeWindow() LOG_ERROR("GLFW: Failed to create window"); exit(EXIT_FAILURE); } + glfwSetFramebufferSizeCallback(m_Window, &glfwFrameBufferCallback); glfwMakeContextCurrent(m_Window); // GL version info @@ -59,8 +73,10 @@ void Renderer::InitializeWindow() exit(EXIT_FAILURE); } + m_WindowToRenderer[m_Window] = this; + int windowSize[2]; - glfwGetWindowSize(m_Window, &windowSize[0], &windowSize[1]); + glfwGetFramebufferSize(m_Window, &windowSize[0], &windowSize[1]); m_ViewportSize = Rectangle(windowSize[0], windowSize[1]); } @@ -73,9 +89,6 @@ void Renderer::InitializeShaders() //m_ExplosionEffectProgram->AddShader(std::shared_ptr(new FragmentShader("Shaders/ExplosionEffect.frag.glsl"))); //m_ExplosionEffectProgram->Compile(); //m_ExplosionEffectProgram->Link(); - - - } void Renderer::InputUpdate(double dt) @@ -93,41 +106,81 @@ void Renderer::Update(double dt) void Renderer::Draw(RenderFrame& frame) { - ImGui::Combo("Draw textures", &m_DebugTextureToDraw, "Final\0Scene\0Bloom\0SceneLowRes\0BloomLowRes\0Gaussian\0Picking"); + GLERROR("PRE"); + ImGui::Combo("Draw textures", &m_DebugTextureToDraw, "Final\0Scene\0Bloom\0SceneLowRes\0BloomLowRes\0Gaussian\0Picking\0Ambient Occlusion"); + ImGui::Combo("CubeMap", &m_CubeMapTexture, "Nevada(512)\0Sky(1024)"); + if(m_CubeMapTexture == 0) { + m_CubeMapPass->LoadTextures("Nevada"); + } else if (m_CubeMapTexture == 1) { + m_CubeMapPass->LoadTextures("Sky"); + } + + ImGui::SliderFloat("SSAO sample radius", &m_SSAO_Radius, 0.01f, 5.0f); + ImGui::SliderFloat("SSAO bias", &m_SSAO_Bias, 0.0f, 0.1f); + ImGui::SliderFloat("SSAO contrast", &m_SSAO_Contrast, 0.0f, 10.0f); + ImGui::SliderFloat("SSAO IntensityScale", &m_SSAO_IntensityScale, 0.0f, 10.0f); + ImGui::SliderInt("SSAO Number of Samples", &m_SSAO_NumOfSamples, 2, 100); + ImGui::SliderInt("SSAO Number of Turns", &m_SSAO_NumOfTurns, 0, 50); + m_SSAOPass->Setting(m_SSAO_Radius, m_SSAO_Bias, m_SSAO_Contrast, m_SSAO_IntensityScale, m_SSAO_NumOfSamples, m_SSAO_NumOfTurns); + GLERROR("SSAO Settings"); //clear buffer 0 glClearColor(0.f, 0.f, 0.f, 0.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clear other buffers + PerformanceTimer::StartTimer("Renderer-ClearBuffers"); m_PickingPass->ClearPicking(); m_DrawFinalPass->ClearBuffer(); m_DrawBloomPass->ClearBuffer(); - + m_SSAOPass->ClearBuffer(); + PerformanceTimer::StopTimer("Renderer-ClearBuffers"); + GLERROR("ClearBuffers"); + for (auto scene : frame.RenderScenes) { + PerformanceTimer::StartTimer("Renderer-Depth"); + m_PickingPass->Draw(*scene); + GLERROR("Drawing pickingpass"); + PerformanceTimer::StopTimer("Renderer-Depth"); + } + PerformanceTimer::StartTimer("AO generation"); + m_SSAOPass->Draw(m_PickingPass->DepthBuffer(), frame.RenderScenes.front()->Camera); + GLuint ao = m_SSAOPass->SSAOTexture(); + PerformanceTimer::StopTimer("AO generation"); for (auto scene : frame.RenderScenes){ + PerformanceTimer::StartTimer("Renderer-Drawing PickingPass"); SortRenderJobsByDepth(*scene); GLERROR("SortByDepth"); - m_PickingPass->Draw(*scene); - GLERROR("Drawing pickingpass"); + PerformanceTimer::StartTimerAndStopPrevious("Renderer-Generate Frustrums"); m_LightCullingPass->GenerateNewFrustum(*scene); GLERROR("Generate frustums"); + PerformanceTimer::StartTimerAndStopPrevious("Renderer-Filling Light List"); m_LightCullingPass->FillLightList(*scene); GLERROR("Filling light list"); + PerformanceTimer::StartTimerAndStopPrevious("Renderer-Light Culling"); m_LightCullingPass->CullLights(*scene); GLERROR("LightCulling"); - m_DrawFinalPass->Draw(*scene); + m_DrawFinalPass->Draw(*scene, ao); + PerformanceTimer::StartTimerAndStopPrevious("Renderer-Draw Geometry+Light"); GLERROR("Draw Geometry+Light"); //m_DrawScenePass->Draw(*scene); + PerformanceTimer::StartTimerAndStopPrevious("Renderer-Draw Text"); m_TextPass->Draw(*scene, *m_DrawFinalPass->FinalPassFrameBuffer()); GLERROR("Draw Text"); - + PerformanceTimer::StopTimer("Renderer-Draw Text"); } + + PerformanceTimer::StartTimer("Renderer-Draw Bloom"); m_DrawBloomPass->Draw(m_DrawFinalPass->BloomTexture()); + PerformanceTimer::StopTimer("Renderer-Draw Bloom"); if (m_DebugTextureToDraw == 0) { + PerformanceTimer::StartTimer("Renderer-Color Correction Pass"); m_DrawColorCorrectionPass->Draw(m_DrawFinalPass->SceneTexture(), m_DrawBloomPass->GaussianTexture(), m_DrawFinalPass->SceneTextureLowRes(), m_DrawFinalPass->BloomTextureLowRes(), frame.Gamma, frame.Exposure); + PerformanceTimer::StopTimer("Renderer-Color Correction Pass"); } + + PerformanceTimer::StartTimer("Renderer-Misc Debug Draws"); if (m_DebugTextureToDraw == 1) { m_DrawScreenQuadPass->Draw(m_DrawFinalPass->SceneTexture()); } @@ -146,10 +199,16 @@ void Renderer::Draw(RenderFrame& frame) if (m_DebugTextureToDraw == 6) { m_DrawScreenQuadPass->Draw(m_PickingPass->PickingTexture()); } + if (m_DebugTextureToDraw == 7) { + m_DrawScreenQuadPass->Draw(m_SSAOPass->SSAOTexture()); + } + PerformanceTimer::StopTimer("Renderer-Misc Debug Draws"); + PerformanceTimer::StartTimer("Renderer-ImGuiRenderPass"); m_ImGuiRenderPass->Draw(); GLERROR("Imgui draw"); glfwSwapBuffers(m_Window); + PerformanceTimer::StopTimer("Renderer-ImGuiRenderPass"); } PickData Renderer::Pick(glm::vec2 screenCoord) @@ -169,6 +228,7 @@ void Renderer::SortRenderJobsByDepth(RenderScene &scene) //Sort all forward jobs so transparency is good. scene.Jobs.TransparentObjects.sort(Renderer::DepthSort); scene.Jobs.SpriteJob.sort(Renderer::DepthSort); + scene.Jobs.Text.sort(Renderer::DepthSort); } void Renderer::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) @@ -187,8 +247,10 @@ void Renderer::InitializeRenderPasses() { m_PickingPass = new PickingPass(this, m_EventBroker); m_LightCullingPass = new LightCullingPass(this); - m_DrawFinalPass = new DrawFinalPass(this, m_LightCullingPass); + m_CubeMapPass = new CubeMapPass(this); + m_DrawFinalPass = new DrawFinalPass(this, m_LightCullingPass, m_CubeMapPass); m_DrawScreenQuadPass = new DrawScreenQuadPass(this); m_DrawBloomPass = new DrawBloomPass(this); m_DrawColorCorrectionPass = new DrawColorCorrectionPass(this); + m_SSAOPass = new SSAOPass(this); } diff --git a/src/Engine/Rendering/SSAOPass.cpp b/src/Engine/Rendering/SSAOPass.cpp new file mode 100644 index 00000000..d4cdcb19 --- /dev/null +++ b/src/Engine/Rendering/SSAOPass.cpp @@ -0,0 +1,145 @@ +#include "Rendering/SSAOPass.h" + +SSAOPass::SSAOPass(IRenderer* renderer) +{ + m_Renderer = renderer; + + m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.mesh"); + + InitializeTexture(); + InitializeBuffer(); + InitializeShaderProgram(); + Setting(0.1f, 0.012f, 1.0f, 1.0f, 13, 7); + + m_DrawBloomPass = new DrawBloomPass(renderer); +} + +void SSAOPass::InitializeShaderProgram() +{ + m_SSAOProgram = ResourceManager::Load("##SSAOProgram"); + m_SSAOProgram->AddShader(std::shared_ptr(new VertexShader("Shaders/SSAO.vert.glsl"))); + m_SSAOProgram->AddShader(std::shared_ptr(new FragmentShader("Shaders/SSAO.frag.glsl"))); + m_SSAOProgram->Compile(); + m_SSAOProgram->Link(); + + m_SSAOViewSpaceZProgram = ResourceManager::Load("##SSAOViewSpaceZProgram"); + m_SSAOViewSpaceZProgram->AddShader(std::shared_ptr(new VertexShader("Shaders/SSAO.vert.glsl"))); + m_SSAOViewSpaceZProgram->AddShader(std::shared_ptr(new FragmentShader("Shaders/SSAOViewSpaceZ.frag.glsl"))); + m_SSAOViewSpaceZProgram->Compile(); + m_SSAOViewSpaceZProgram->Link(); +} + +void SSAOPass::InitializeTexture() { + GenerateTexture(&m_SSAOTexture, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_R8, GL_RED, GL_FLOAT); + GenerateTexture(&m_SSAOViewSpaceZTexture, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_R32F, GL_RED, GL_FLOAT); +} + +void SSAOPass::InitializeBuffer() +{ + m_SSAOFramBuffer.AddResource(std::shared_ptr(new Texture2D(&m_SSAOTexture, GL_COLOR_ATTACHMENT0))); + m_SSAOFramBuffer.Generate(); + + m_SSAOViewSpaceZFramBuffer.AddResource(std::shared_ptr(new Texture2D(&m_SSAOViewSpaceZTexture, GL_COLOR_ATTACHMENT0))); + m_SSAOViewSpaceZFramBuffer.Generate(); +} + +void SSAOPass::ClearBuffer() +{ + m_SSAOFramBuffer.Bind(); + glClearColor(1.f, 1.f, 1.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + m_SSAOFramBuffer.Unbind(); + + m_SSAOViewSpaceZFramBuffer.Bind(); + glClearColor(1.f, 1.f, 1.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + m_SSAOViewSpaceZFramBuffer.Unbind(); +} + +void SSAOPass::Setting(float radius, float bias, float contrast, float intensityScale, int numOfSamples, int NumOfTurns) { + m_Radius = radius; + m_Bias = bias; + m_Contrast = contrast; + m_IntensityScale = intensityScale; + m_NumOfSamples = numOfSamples; + m_NumOfTurns = NumOfTurns; +} + +void SSAOPass::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const +{ + glGenTextures(1, texture); + glBindTexture(GL_TEXTURE_2D, *texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapping); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapping); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, dimensions.x, dimensions.y, 0, format, type, nullptr); + GLERROR("Texture initialization failed"); +} + +void SSAOPass::Draw(GLuint depthBuffer, Camera* camera) +{ + SSAOPassState state; + GLuint viewSpaceZPShaderHandle = m_SSAOViewSpaceZProgram->GetHandle(); + GLuint SSAOShaderHandle = m_SSAOProgram->GetHandle(); + + m_SSAOViewSpaceZFramBuffer.Bind(); + m_SSAOViewSpaceZProgram->Bind(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, depthBuffer); + glm::vec3 clipInfo = glm::vec3( + (camera->NearClip() * camera->FarClip()), + (camera->NearClip() - camera->FarClip()), + (camera->FarClip()) + ); + /*glm::vec3 clipInfo = glm::vec3( + (camera->NearClip()), + (-1.0f), + (+1.0f) + );*/ + glUniform3fv(glGetUniformLocation(viewSpaceZPShaderHandle, "ClipInfo"), 1, glm::value_ptr(clipInfo)); + + glBindVertexArray(m_ScreenQuad->VAO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer); + glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex + 1 + , GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex); + + glm::vec4 projInfo = glm::vec4( + ((1.0 - camera->ProjectionMatrix()[0][2]) / camera->ProjectionMatrix()[0][0]), + (-2.0 / (m_Renderer->GetViewportSize().Width * camera->ProjectionMatrix()[0][0])), + ((1.0 + camera->ProjectionMatrix()[1][2]) / camera->ProjectionMatrix()[1][1]), + (-2.0 / (m_Renderer->GetViewportSize().Height * camera->ProjectionMatrix()[1][1])) + ); + + + m_SSAOFramBuffer.Bind(); + m_SSAOProgram->Bind(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_SSAOViewSpaceZTexture); + + // How many pixel there are in a 1m long object 1m away from the camera + glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uProjScale"), m_Renderer->GetViewportSize().Height / (-2.0f * glm::tan(camera->FOV() * 0.5f))); + + glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uRadius"), m_Radius); + glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uBias"), m_Bias); + glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uContrast"), m_Contrast); + glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uIntensityScale"), m_IntensityScale); + glUniform1i(glGetUniformLocation(SSAOShaderHandle, "uNumOfSamples"), m_NumOfSamples); + glUniform1i(glGetUniformLocation(SSAOShaderHandle, "uNumOfTurns"), m_NumOfTurns);; + + glUniform4fv(glGetUniformLocation(SSAOShaderHandle, "uProjInfo"), 1, glm::value_ptr(projInfo)); + glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex + 1 + , GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex); + + m_DrawBloomPass->ClearBuffer(); + m_DrawBloomPass->Draw(m_SSAOTexture); +} + +void SSAOPass::OnWindowResize() { + m_DrawBloomPass->OnWindowResize(); + InitializeTexture(); + m_SSAOFramBuffer.Generate(); + m_SSAOViewSpaceZFramBuffer.Generate(); +} \ No newline at end of file diff --git a/src/Engine/Rendering/SSAOPassState.cpp b/src/Engine/Rendering/SSAOPassState.cpp new file mode 100644 index 00000000..7dd49841 --- /dev/null +++ b/src/Engine/Rendering/SSAOPassState.cpp @@ -0,0 +1,16 @@ +#include "Rendering/SSAOPassState.h" + + +SSAOPassState::SSAOPassState() +{ + //BindFramebuffer(0); + Disable(GL_BLEND); + Disable(GL_DEPTH_TEST); + Disable(GL_CULL_FACE); +} + +SSAOPassState::~SSAOPassState() +{ + +} + diff --git a/src/Engine/Rendering/ShaderProgram.cpp b/src/Engine/Rendering/ShaderProgram.cpp index 9c26c15c..ae536bc0 100644 --- a/src/Engine/Rendering/ShaderProgram.cpp +++ b/src/Engine/Rendering/ShaderProgram.cpp @@ -98,8 +98,7 @@ void ShaderProgram::AddShader(std::shared_ptr shader) void ShaderProgram::Compile() { - if (m_ShaderProgramHandle == 0) - { + if (m_ShaderProgramHandle == 0) { m_ShaderProgramHandle = glCreateProgram(); } diff --git a/src/Engine/Rendering/Texture.cpp b/src/Engine/Rendering/Texture.cpp index 256246a9..03347044 100644 --- a/src/Engine/Rendering/Texture.cpp +++ b/src/Engine/Rendering/Texture.cpp @@ -18,6 +18,7 @@ Texture::Texture(std::string path) this->Width = img->Width; this->Height = img->Height; + this->Data = img->Data; GLint format; switch (img->Format) { @@ -28,6 +29,7 @@ Texture::Texture(std::string path) format = GL_RGBA; break; } + // Construct the OpenGL texture glGenTextures(1, &m_Texture); diff --git a/src/Game/CMakeLists.txt b/src/Game/CMakeLists.txt index f188d7fa..d8ba8599 100644 --- a/src/Game/CMakeLists.txt +++ b/src/Game/CMakeLists.txt @@ -36,6 +36,7 @@ source_group(Network FILES ${SOURCE_FILES_Network}) set(SOURCE_FILES ${SOURCE_FILES} "Game.cpp" + "MiniDump.cpp" ${SOURCE_FILES_Systems} ${SOURCE_FILES_Systems_Weapon} ${SOURCE_FILES_Events} diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 0dc52822..24b7cd1e 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -14,15 +14,21 @@ #include "Game/Systems/CapturePointSystem.h" #include "Game/Systems/CapturePointHUDSystem.h" #include "Game/Systems/PickupSpawnSystem.h" +#include "Game/Systems/AmmoPickupSystem.h" #include "Game/Systems/DamageIndicatorSystem.h" #include "Game/Systems/Weapon/WeaponSystem.h" #include "Rendering/AnimationSystem.h" -#include "Game/Systems/PlayerHUDSystem.h" +#include "Game/Systems/HealthHUDSystem.h" #include "Rendering/BoneAttachmentSystem.h" #include "Game/Systems/LifetimeSystem.h" #include "../Engine/Core/UniformScaleSystem.h" #include "Rendering/AnimationSystem.h" #include "Network/MultiplayerSnapshotFilter.h" +#include "Game/Systems/AmmunitionHUDSystem.h" +#include "Game/Systems/KillFeedSystem.h" +#include "GUI/ButtonSystem.h" +#include "GUI/MainMenuSystem.h" + Game::Game(int argc, char* argv[]) { @@ -42,6 +48,7 @@ Game::Game(int argc, char* argv[]) ResourceManager::UseThreading = m_Config->Get("Multithreading.ResourceLoading", true); DisableMemoryPool::Value = m_Config->Get("Debug.DisableMemoryPool", false); LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get("Debug.LogLevel", 1)); + PlayerSpawnSystem::SetRespawnTime(m_Config->Get("Debug.RespawnTime", 15.0f)); // Create the core event broker m_EventBroker = new EventBroker(); @@ -67,11 +74,6 @@ Game::Game(int argc, char* argv[]) m_InputProxy->AddHandler(); m_InputProxy->LoadBindings("Input.ini"); - // Create the root level GUI frame - m_FrameStack = new GUI::Frame(m_EventBroker); - m_FrameStack->Width = m_Renderer->Resolution().Width; - m_FrameStack->Height = m_Renderer->Resolution().Height; - // Create a world m_World = new World(m_EventBroker); std::string mapToLoad = m_Config->Get("Debug.LoadMap", ""); @@ -118,13 +120,17 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); - m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer, m_OctreeCollision); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer); + m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer); // Populate Octree with collidables ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeCollision, "Collidable"); @@ -132,7 +138,8 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeFrustrumCulling); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); - m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); // Collision and TriggerSystem should update after player. ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel); @@ -160,7 +167,6 @@ Game::~Game() delete m_NetworkServer; } delete m_World; - delete m_FrameStack; delete m_InputProxy; delete m_InputManager; delete m_RenderFrame; @@ -179,16 +185,20 @@ void Game::Tick() // Handle input in a weird looking but responsive way m_EventBroker->Process(); m_EventBroker->Swap(); + PerformanceTimer::StartTimer("InputManager"); m_InputManager->Update(dt); m_EventBroker->Swap(); + PerformanceTimer::StartTimerAndStopPrevious("InputProxy"); m_InputProxy->Update(dt); m_EventBroker->Swap(); m_InputProxy->Process(); m_EventBroker->Swap(); + PerformanceTimer::StartTimerAndStopPrevious("SoundManager"); m_SoundManager->Update(dt); // Update network + PerformanceTimer::StartTimerAndStopPrevious("Network"); m_EventBroker->Process(); if (m_NetworkClient != nullptr) { m_NetworkClient->Update(); @@ -199,10 +209,14 @@ void Game::Tick() //m_SoundManager->Update(dt); // Iterate through systems and update world! + PerformanceTimer::StartTimerAndStopPrevious("SystemPipeline"); m_EventBroker->Process(); m_SystemPipeline->Update(dt); + PerformanceTimer::StartTimerAndStopPrevious("RendererUpdate"); m_Renderer->Update(dt); + PerformanceTimer::StartTimerAndStopPrevious("RendererDraw"); m_Renderer->Draw(*m_RenderFrame); + PerformanceTimer::StopTimer("RendererDraw"); m_RenderFrame->Clear(); m_EventBroker->Swap(); m_EventBroker->Clear(); diff --git a/src/Game/MiniDump.cpp b/src/Game/MiniDump.cpp new file mode 100644 index 00000000..bd8ee2de --- /dev/null +++ b/src/Game/MiniDump.cpp @@ -0,0 +1,110 @@ +/* + Author: Vladimir Sedach. + + Purpose: demo of Call Stack creation by our own means, + and with MiniDumpWriteDump() function of DbgHelp.dll. +*/ + +#include +#include + +#include +#include +//#include "dbghelp.h" + +//#define DEBUG_DPRINTF 1 //allow d() +//#include "wfun.h" + +#pragma optimize("y", off) //generate stack frame pointers for all functions - same as /Oy- in the project +#pragma warning(disable: 4200) //nonstandard extension used : zero-sized array in struct/union +#pragma warning(disable: 4100) //unreferenced formal parameter + +// In case you don't have dbghelp.h. +#ifndef _DBGHELP_ + +typedef struct _MINIDUMP_EXCEPTION_INFORMATION { + DWORD ThreadId; + PEXCEPTION_POINTERS ExceptionPointers; + BOOL ClientPointers; +} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION; + +typedef enum _MINIDUMP_TYPE { + MiniDumpNormal = 0x00000000, + MiniDumpWithDataSegs = 0x00000001, +} MINIDUMP_TYPE; + +typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)( + IN HANDLE hProcess, + IN DWORD ProcessId, + IN HANDLE hFile, + IN MINIDUMP_TYPE DumpType, + IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL + IN PVOID UserStreamParam, OPTIONAL + IN PVOID CallbackParam OPTIONAL + ); + +#else + +typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)( + IN HANDLE hProcess, + IN DWORD ProcessId, + IN HANDLE hFile, + IN MINIDUMP_TYPE DumpType, + IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL + IN PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL + IN PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL + ); +#endif //#ifndef _DBGHELP_ + +HMODULE hDbgHelp; +MINIDUMP_WRITE_DUMP MiniDumpWriteDump_; + +// Tool Help functions. +typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID); + +//************************************************************************************* +void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag) +//************************************************************************************* +// Create dump. +// pException can be either GetExceptionInformation() or NULL. +// If File_Flag = TRUE - write dump files (.dmz and .dmp) with the name of the current process. +// If Show_Flag = TRUE - show message with Get_Exception_Info() dump. +{ + // Try to get MiniDumpWriteDump() address. + hDbgHelp = LoadLibrary("DBGHELP.DLL"); + MiniDumpWriteDump_ = (MINIDUMP_WRITE_DUMP)GetProcAddress(hDbgHelp, "MiniDumpWriteDump"); + + // If MiniDumpWriteDump() of DbgHelp.dll available. + if (MiniDumpWriteDump_) + { + HANDLE hDump_File; + CHAR Dump_Path[MAX_PATH]; + + GetModuleFileName(NULL, Dump_Path, sizeof(Dump_Path)); //path of current process + std::time_t t = std::time(NULL); + char tStr[16]; + std::strftime(tStr, 32, " %a %H-%M-%S", std::localtime(&t)); + std::string time(tStr); + std::string path(Dump_Path); + path = path.substr(0, path.length() - 4); + path += time + ".dmp"; + + MINIDUMP_EXCEPTION_INFORMATION M; + M.ThreadId = GetCurrentThreadId(); + M.ExceptionPointers = pException; + M.ClientPointers = 0; + + hDump_File = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + MiniDumpWriteDump_(GetCurrentProcess(), GetCurrentProcessId(), hDump_File, + MiniDumpNormal, (pException) ? &M : NULL, NULL, NULL); + + CloseHandle(hDump_File); + + std::cout << "Memory dumped to: \"" << path.c_str() << "\""; + MessageBox(NULL, ("Application crashed, memory dumped to: " + path).c_str(), "MiniDump", MB_ICONHAND | MB_OK); + } else { + MessageBox(NULL, "Application crashed, memory dump failed.", "MiniDump", MB_ICONHAND | MB_OK); + } +} + diff --git a/src/Game/Network/MultiplayerSnapshotFilter.cpp b/src/Game/Network/MultiplayerSnapshotFilter.cpp index 65d40189..69b7f282 100644 --- a/src/Game/Network/MultiplayerSnapshotFilter.cpp +++ b/src/Game/Network/MultiplayerSnapshotFilter.cpp @@ -9,7 +9,16 @@ MultiplayerSnapshotFilter::MultiplayerSnapshotFilter(EventBroker* eventBroker) bool MultiplayerSnapshotFilter::FilterComponent(EntityWrapper entity, SharedComponentWrapper& component) { if (entity == m_LocalPlayer || entity.IsChildOf(m_LocalPlayer)) { - return false; + if ( + component.Info.Name == "Transform" + || component.Info.Name == "Physics" + || component.Info.Name == "AssaultWeapon" + || component.Info.Name == "Animation" + || component.Info.Name == "AnimationOffset" + || entity.Name() == "PlayerName" + ) { + return false; + } } if (component.Info.Name == "Physics") { diff --git a/src/Game/Systems/AmmoPickupSystem.cpp b/src/Game/Systems/AmmoPickupSystem.cpp new file mode 100644 index 00000000..250fa494 --- /dev/null +++ b/src/Game/Systems/AmmoPickupSystem.cpp @@ -0,0 +1,79 @@ +#include "Systems/AmmoPickupSystem.h" + +AmmoPickupSystem::AmmoPickupSystem(SystemParams params) + : System(params) +{ + EVENT_SUBSCRIBE_MEMBER(m_ETriggerTouch, &AmmoPickupSystem::OnTriggerTouch); +} + +void AmmoPickupSystem::Update(double dt) +{ + for (auto it = m_ETriggerTouchVector.begin(); it != m_ETriggerTouchVector.end(); ++it) + { + auto& ammoPickupPosition = *it; + //set the double timer value (value 3) + ammoPickupPosition.DecreaseThisRespawnTimer -= dt; + if (ammoPickupPosition.DecreaseThisRespawnTimer < 0.0) { + //spawn and delete the vector item + auto entityFile = ResourceManager::Load("Schema/Entities/AmmoPickup.xml"); + EntityFileParser parser(entityFile); + EntityID ammoPickupID = parser.MergeEntities(m_World); + + //let the world know a pickup has spawned (graphics effects, etc) + Events::PickupSpawned ePickupSpawned; + ePickupSpawned.Pickup = EntityWrapper(m_World, ammoPickupID); + m_EventBroker->Publish(ePickupSpawned); + + //set values from the old entity to the new entity + auto& newAmmoPickupEntity = EntityWrapper(m_World, ammoPickupID); + newAmmoPickupEntity["Transform"]["Position"] = ammoPickupPosition.Pos; + newAmmoPickupEntity["AmmoPickup"]["AmmoGain"] = ammoPickupPosition.AmmoGain; + newAmmoPickupEntity["AmmoPickup"]["RespawnTimer"] = ammoPickupPosition.RespawnTimer; + m_World->SetParent(newAmmoPickupEntity.ID, ammoPickupPosition.parentID); + + //erase the current element (AmmoPickupPosition) + m_ETriggerTouchVector.erase(it); + break; + } + } +} + + +bool AmmoPickupSystem::OnTriggerTouch(Events::TriggerTouch& e) +{ + if (e.Entity != LocalPlayer) { + return false; + } + //TODO: add other weapontypes + if (!e.Entity.HasComponent("AssaultWeapon")) { + return false; + } + if (!e.Trigger.HasComponent("AmmoPickup")) { + return false; + } + int maxWeaponAmmo = (int)e.Entity["AssaultWeapon"]["MaxAmmo"]; + int& currentAmmo = (int)e.Entity["AssaultWeapon"]["Ammo"]; + + int ammoGiven = 0.01*(double)e.Trigger["AmmoPickup"]["AmmoGain"] * maxWeaponAmmo; + //cant pick up ammopacks if you are already at MaxAmmo + if (currentAmmo >= maxWeaponAmmo) { + return false; + } + + //personEntered = e.Entity, thingEntered = e.Trigger + Events::AmmoPickup ePlayerAmmoPickup; + ePlayerAmmoPickup.AmmoGain = ammoGiven; + ePlayerAmmoPickup.Player = e.Entity; + m_EventBroker->Publish(ePlayerAmmoPickup); + //immediately give the player the ammo + currentAmmo = std::min(currentAmmo + ammoGiven, maxWeaponAmmo); + + //copy position, ammogain, respawntimer (twice since one of the values will be counted down to 0, the other will be set in the new object) + //we need to copy all values since each value can be different for each ammoPickup + m_ETriggerTouchVector.push_back({ e.Trigger["Transform"]["Position"], e.Trigger["AmmoPickup"]["AmmoGain"], + e.Trigger["AmmoPickup"]["RespawnTimer"], e.Trigger["AmmoPickup"]["RespawnTimer"], m_World->GetParent(e.Trigger.ID) }); + + //delete the ammopickup + m_World->DeleteEntity(e.Trigger.ID); + return true; +} diff --git a/src/Game/Systems/AmmunitionHUDSystem.cpp b/src/Game/Systems/AmmunitionHUDSystem.cpp new file mode 100644 index 00000000..c9d87072 --- /dev/null +++ b/src/Game/Systems/AmmunitionHUDSystem.cpp @@ -0,0 +1,36 @@ +#include "Game/Systems/AmmunitionHUDSystem.h" + +void AmmunitionHUDSystem::Update(double dt) +{ + //Hud element for tracking ammunition from parent with AssaultWeapon component.Child with the name "MagazineAmmo" tracks clip ammunition.Child with the name "Ammo" tracks ammo. + + auto ammunitionHUDs = m_World->GetComponents("AmmunitionHUD"); + if (ammunitionHUDs == nullptr) { + return; + } + + for (auto& ammunitionHUDComponent : *ammunitionHUDs) { + EntityWrapper entity = EntityWrapper(m_World, ammunitionHUDComponent.EntityID); + + EntityWrapper playerEntity = entity.FirstParentWithComponent("AssaultWeapon"); + + if (!playerEntity.Valid()) { + return; + } + + + EntityWrapper magazineAmmo = entity.FirstChildByName("MagazineAmmo"); + if(magazineAmmo.Valid()) { + if(magazineAmmo.HasComponent("Text")) { + (std::string&)magazineAmmo["Text"]["Content"] = std::to_string((int)playerEntity["AssaultWeapon"]["MagazineAmmo"]); + } + } + + EntityWrapper ammo = entity.FirstChildByName("Ammo"); + if (ammo.Valid()) { + if (ammo.HasComponent("Text")) { + (std::string&)ammo["Text"]["Content"] = std::to_string((int)playerEntity["AssaultWeapon"]["Ammo"]); + } + } + } +} diff --git a/src/Game/Systems/CapturePointHUDSystem.cpp b/src/Game/Systems/CapturePointHUDSystem.cpp index 21737fbe..6f1392e0 100644 --- a/src/Game/Systems/CapturePointHUDSystem.cpp +++ b/src/Game/Systems/CapturePointHUDSystem.cpp @@ -20,6 +20,10 @@ void CapturePointHUDSystem::Update(double dt) return; } + if(!CapturePointHUDElements) { + return; + } + for (auto& cCapturePointHUD : *CapturePointHUDElements) { int HUD_ID = cCapturePointHUD["CapturePointNumber"]; EntityWrapper entityHUD = EntityWrapper(m_World, cCapturePointHUD.EntityID); @@ -39,14 +43,14 @@ void CapturePointHUDSystem::Update(double dt) } //Color hud with team color auto capturePointTeam = (int)teamComponent["Team"]; - entityHUDparent["Sprite"]["Color"] = capturePointTeam == blueTeam ? glm::vec4(0, 0.2f, 1, 0.7) : capturePointTeam == redTeam ? glm::vec4(1, 0.2f, 0, 0.7) : glm::vec4(1, 1, 1, 0.3); + entityHUDparent["Sprite"]["Color"] = capturePointTeam == blueTeam ? glm::vec4(0, 0.2f, 1, 0.7f) : capturePointTeam == redTeam ? glm::vec4(1, 0.0f, 0, 0.7f) : glm::vec4(1, 1, 1, 0.3f); //Progress is scaled with time double currentCaptureTime = (double)entityCP["CapturePoint"]["CaptureTimer"]; double progress = glm::abs(currentCaptureTime)/15.0; int currentCapturingTeam = currentCaptureTime > 0 ? redTeam : currentCaptureTime < 0 ? blueTeam : spectatorTeam; ((glm::vec3&)entityHUD["Transform"]["Orientation"]).z = currentCapturingTeam == redTeam ? glm::half_pi()+glm::pi() : glm::half_pi(); - glm::vec4 fillColor = currentCapturingTeam == redTeam ? glm::vec4(1, 0.2f, 0, 0.7) : glm::vec4(0, 0.2f, 1, 0.7); + glm::vec4 fillColor = currentCapturingTeam == redTeam ? glm::vec4(1, 0.f, 0, 0.7f) : glm::vec4(0, 0.2f, 1, 0.7f); entityHUD["Fill"]["Color"] = fillColor; entityHUD["Fill"]["Percentage"] = progress; } diff --git a/src/Game/Systems/CapturePointSystem.cpp b/src/Game/Systems/CapturePointSystem.cpp index f938ecd0..c99a36a6 100644 --- a/src/Game/Systems/CapturePointSystem.cpp +++ b/src/Game/Systems/CapturePointSystem.cpp @@ -1,26 +1,39 @@ #include "Systems/CapturePointSystem.h" #include -CapturePointSystem::CapturePointSystem(SystemParams params) +CapturePointSystem::CapturePointSystem(SystemParams params) : System(params) , PureSystem("CapturePoint") { //subscribe/listenTo playerdamage,healthpickup events (using the eventBroker) - EVENT_SUBSCRIBE_MEMBER(m_ETriggerTouch, &CapturePointSystem::OnTriggerTouch); - EVENT_SUBSCRIBE_MEMBER(m_ETriggerLeave, &CapturePointSystem::OnTriggerLeave); - EVENT_SUBSCRIBE_MEMBER(m_ECaptured, &CapturePointSystem::OnCaptured); + if (!IsClient) { + EVENT_SUBSCRIBE_MEMBER(m_ETriggerTouch, &CapturePointSystem::OnTriggerTouch); + EVENT_SUBSCRIBE_MEMBER(m_ETriggerLeave, &CapturePointSystem::OnTriggerLeave); + EVENT_SUBSCRIBE_MEMBER(m_ECaptured, &CapturePointSystem::OnCaptured); + } + } //here all capturepoints will update their component //NOTE: needs to run each frame, since we're possibly modifying the captureTimer for the capturePoints by dt void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, ComponentWrapper& cCapturePoint, double dt) { + if (IsClient) { + return; + } if (m_WinnerWasFound) { return; } const int capturePointNumber = cCapturePoint["CapturePointNumber"]; const bool hasTeamComponent = capturePointEntity.HasComponent("Team"); + if (m_NumberOfCapturePoints != 0) { + if (!m_CapturePointNumberToEntityMap[0].HasComponent("CapturePoint")) { + //if map has changed, the capturepoints has changed, now have to redo them + m_NumberOfCapturePoints = 0; + m_CapturePointNumberToEntityMap.clear(); + } + } //if point doesnt have a teamComponent yet, add one. since: //what if capture point has no team -> we cant get/use the team enum from it... if (!hasTeamComponent) { @@ -65,8 +78,7 @@ void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, Comp std::map nextPossibleCapturePoint; nextPossibleCapturePoint["Red"] = -1; nextPossibleCapturePoint["Blue"] = -1; - for (int i = 0; i < m_NumberOfCapturePoints; i++) - { + for (int i = 0; i < m_NumberOfCapturePoints; i++) { if (!m_CapturePointNumberToEntityMap[i].HasComponent("Team")) { continue; } @@ -78,8 +90,7 @@ void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, Comp nextPossibleCapturePoint["Blue"] = i + 1; } } - for (int i = m_NumberOfCapturePoints - 1; i >= 0; i--) - { + for (int i = m_NumberOfCapturePoints - 1; i >= 0; i--) { if (!m_CapturePointNumberToEntityMap[i].HasComponent("Team")) { continue; } @@ -94,8 +105,7 @@ void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, Comp //reset timers and reset the bool that triggers this if (m_ResetTimers) { - for (int i = 0; i < m_NumberOfCapturePoints; i++) - { + for (int i = 0; i < m_NumberOfCapturePoints; i++) { ComponentWrapper& capturePoint = m_CapturePointNumberToEntityMap[i]["CapturePoint"]; if ((int)capturePoint["CapturePointNumber"] != nextPossibleCapturePoint["Red"] && (int)capturePoint["CapturePointNumber"] != nextPossibleCapturePoint["Blue"]) { @@ -110,8 +120,7 @@ void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, Comp } //check how many players are standing inside and are healthy - for (size_t i = m_ETriggerTouchVector.size(); i > 0; i--) - { + for (size_t i = m_ETriggerTouchVector.size(); i > 0; i--) { auto triggerTouched = m_ETriggerTouchVector[i - 1]; if (std::get<1>(triggerTouched) == capturePointEntity) { //some player has touched this - lets figure out: what team, health @@ -170,8 +179,8 @@ void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, Comp cCapturePoint["CaptureTimer"] = (double)cCapturePoint["CaptureTimer"] + timerDeltaChange; } //if capturePoint is owned by the team, and the other team has been trying to take it, then increase/decrease the timer towards 0.0 - if ((ownedBy == currentTeam && currentTeam == redTeam && (double)cCapturePoint["CaptureTimer"] < 0.0) || - (ownedBy == currentTeam && currentTeam == blueTeam && (double)cCapturePoint["CaptureTimer"] > 0.0)) { + if ((ownedBy == currentTeam && currentTeam == redTeam && (double)cCapturePoint["CaptureTimer"] < captureTimeToTakeOver) || + (ownedBy == currentTeam && currentTeam == blueTeam && (double)cCapturePoint["CaptureTimer"] > -captureTimeToTakeOver)) { cCapturePoint["CaptureTimer"] = (double)cCapturePoint["CaptureTimer"] + timerDeltaChange; } //check if captureTimer > captureTimeToTakeOver and if so change owner and publish the eCaptured event @@ -189,17 +198,14 @@ void CapturePointSystem::UpdateComponent(EntityWrapper& capturePointEntity, Comp //check for possible winCondition = check if the homebase is owned by the other team bool checkForWinner = false; - if (capturePointNumber == m_RedTeamHomeCapturePoint && ownedBy != redTeam) - { + if (capturePointNumber == m_RedTeamHomeCapturePoint && ownedBy != redTeam) { checkForWinner = true; } - if (capturePointNumber == m_BlueTeamHomeCapturePoint && ownedBy != blueTeam) - { + if (capturePointNumber == m_BlueTeamHomeCapturePoint && ownedBy != blueTeam) { checkForWinner = true; } - if (checkForWinner && !m_WinnerWasFound) - { + if (checkForWinner && !m_WinnerWasFound) { //publish Win event Events::Win e; e.TeamThatWon = ownedBy; @@ -218,8 +224,7 @@ bool CapturePointSystem::OnTriggerTouch(const Events::TriggerTouch& e) bool CapturePointSystem::OnTriggerLeave(const Events::TriggerLeave& e) { - for (size_t i = 0; i < m_ETriggerTouchVector.size(); i++) - { + for (size_t i = 0; i < m_ETriggerTouchVector.size(); i++) { auto triggerTouched = m_ETriggerTouchVector[i]; if (std::get<0>(triggerTouched) == e.Entity && std::get<1>(triggerTouched) == e.Trigger) { m_ETriggerTouchVector.erase(m_ETriggerTouchVector.begin() + i); diff --git a/src/Game/Systems/DamageIndicatorSystem.cpp b/src/Game/Systems/DamageIndicatorSystem.cpp index a29309b5..92fbe607 100644 --- a/src/Game/Systems/DamageIndicatorSystem.cpp +++ b/src/Game/Systems/DamageIndicatorSystem.cpp @@ -12,45 +12,42 @@ DamageIndicatorSystem::DamageIndicatorSystem(SystemParams params) auto entityFile = ResourceManager::Load("Schema/Entities/DamageIndicator.xml"); } +void DamageIndicatorSystem::Update(double dt) { + if (!IsServer) { + for (auto& iter = updateDamageIndicatorVector.begin(); iter != updateDamageIndicatorVector.end(); iter++) { + if (!iter->spriteEntity.Valid()) { + updateDamageIndicatorVector.erase(iter); + break; + } + auto angleBetweenVectors = CalculateAngle(LocalPlayer, iter->enemyPosition); + //simply set the rotation z-wise to the angleBetweenVectors + iter->spriteEntity["Transform"]["Orientation"] = glm::vec3(0, 0, angleBetweenVectors); + } + } +} + bool DamageIndicatorSystem::OnPlayerDamage(Events::PlayerDamage& e) { if (m_CurrentCamera == EntityID_Invalid) { return false; } - if (e.Victim != LocalPlayer) { + if (e.Victim.Valid() && e.Victim != LocalPlayer && !e.Victim.IsChildOf(LocalPlayer)) { return false; } - //grab players direction - auto playerOrientation = glm::quat((glm::vec3)e.Victim["Transform"]["Orientation"]); - - //get the position vectors, but ignore the y-height - auto enemyPosition = (glm::vec3)e.Inflictor["Transform"]["Position"]; - auto playerPosition = (glm::vec3)e.Victim["Transform"]["Position"]; - enemyPosition.y = 0.0f; - playerPosition.y = 0.0f; - - //calculate the enemy to player vector - auto enemyPlayerVector = glm::normalize(playerPosition - enemyPosition); - - //get angle from players current rotation, this angle is how much you rotate around the y-axis - auto playerAngle = glm::angle(playerOrientation); - auto playerRotationVector = glm::normalize(glm::rotateY(glm::vec3(0, 0, 1), playerAngle)); - - //dot product of players direction-vector and enemys-to-playervector will give the cos of the angle between the vectors - auto playerRotationDot = glm::dot(playerRotationVector, enemyPlayerVector); - //to get the angle between the vectors just do cos-inverse - auto angleBetweenVectors = glm::acos(playerRotationDot); - - //rotate the direction-vector 90 degrees to get the players side-vector - auto playerSideVector = glm::normalize(glm::rotateY(glm::vec3(0, 0, 1), playerAngle + 1.57f)); - //dot of sidevector positive = enemy is on the right side, dot sidevector negative = left side - auto playerSideVectorDot = glm::dot(playerSideVector, enemyPlayerVector); - if (playerSideVectorDot < 0) { - angleBetweenVectors = -angleBetweenVectors; + if (!e.Inflictor.Valid() || !e.Victim.Valid()) { + return false; } + glm::vec3 inflictorPos = e.Inflictor["Transform"]["Position"]; + //if testing +#ifdef INDICATOR_TEST + inflictorPos = DamageIndicatorTest(e.Victim); +#endif + + float angleBetweenVectors = CalculateAngle(e.Victim, inflictorPos); + //load & set the "2d" sprite auto entityFile = ResourceManager::Load("Schema/Entities/DamageIndicator.xml"); EntityFileParser parser(entityFile); @@ -60,6 +57,10 @@ bool DamageIndicatorSystem::OnPlayerDamage(Events::PlayerDamage& e) //simply set the rotation z-wise to the angleBetweenVectors spriteWrapper["Transform"]["Orientation"] = glm::vec3(0, 0, angleBetweenVectors); + if (!IsServer) { + updateDamageIndicatorVector.emplace_back(spriteWrapper, inflictorPos); + } + return true; } @@ -67,3 +68,82 @@ bool DamageIndicatorSystem::OnSetCamera(const Events::SetCamera& e) { m_CurrentCamera = e.CameraEntity.ID; return true; } + +float DamageIndicatorSystem::CalculateAngle(EntityWrapper player, glm::vec3 enemyPos) { + //grab players direction + auto playerOrientation = glm::quat((glm::vec3)player["Transform"]["Orientation"]); + + //get the position vectors, but ignore the y-height + auto enemyPosition = enemyPos; + auto playerPosition = (glm::vec3)player["Transform"]["Position"]; + enemyPosition.y = 0.0f; + playerPosition.y = 0.0f; + + //calculate the enemy to player vector + auto enemyPlayerVector = glm::normalize(playerPosition - enemyPosition); + + //get the rotationvector relative to the z-axis + auto rotationVectorVec3 = glm::vec3(glm::toMat4(Transform::AbsoluteOrientation(player))*glm::vec4(0, 0, 1, 0)); + //rotate the direction-vector 90 degrees to get the players side-vector + auto playerSideVector = glm::vec3(glm::rotateY(rotationVectorVec3, 1.57f)); + + //dot product of players direction-vector and enemys-to-playervector will give the cos of the angle between the vectors + auto playerRotationDot = glm::dot(rotationVectorVec3, enemyPlayerVector); + //to get the angle between the vectors just do cos-inverse + auto angleBetweenVectors = glm::acos(playerRotationDot); + + //dot of sidevector positive = enemy is on the right side, dot sidevector negative = left side + auto playerSideVectorDot = glm::dot(playerSideVector, enemyPlayerVector); + if (playerSideVectorDot < 0) { + angleBetweenVectors = -angleBetweenVectors; + } + + return angleBetweenVectors; +} +#ifdef INDICATOR_TEST +glm::vec3 DamageIndicatorSystem::DamageIndicatorTest(EntityWrapper player) { + auto currentPos = (glm::vec3)player["Transform"]["Position"]; + + auto testVar = 1; + auto testVar2 = 1; + if (m_TestVar % 4 == 0) { + testVar = -1; + testVar2 = 1; + } + if (m_TestVar % 4 == 1) { + testVar = 1; + testVar2 = 1; + } + if (m_TestVar % 4 == 2) { + testVar *= -1; + testVar2 = -1; + } + if (m_TestVar % 4 == 3) { + testVar = 1; + testVar2 = -1; + } + m_TestVar++; + + auto inflictorPos = glm::vec3(currentPos.x + testVar*6.0f, currentPos.y, currentPos.z + testVar2*6.0f); + + //load the explosioneffect XML + auto deathEffect = ResourceManager::Load("Schema/Entities/PlayerDeathExplosionWithCamera.xml"); + EntityFileParser parser(deathEffect); + EntityID deathEffectID = parser.MergeEntities(m_World); + EntityWrapper deathEffectEW = EntityWrapper(m_World, deathEffectID); + + //components that we need from player + auto playerModel = player.FirstChildByName("PlayerModel"); + auto playerEntityModel = playerModel["Model"]; + auto playerEntityAnimation = playerModel["Animation"]; + + //copy the data from player to explosioneffectmodel + playerEntityModel.Copy(deathEffectEW["Model"]); + playerEntityAnimation.Copy(deathEffectEW["Animation"]); + + //copy the models position,orientation + deathEffectEW["Transform"]["Position"] = inflictorPos; + deathEffectEW["Transform"]["Orientation"] = (glm::vec3)player["Transform"]["Orientation"]; + return inflictorPos; +} +#endif \ No newline at end of file diff --git a/src/Game/Systems/PlayerHUDSystem.cpp b/src/Game/Systems/HealthHUDSystem.cpp similarity index 81% rename from src/Game/Systems/PlayerHUDSystem.cpp rename to src/Game/Systems/HealthHUDSystem.cpp index 898d8bea..74258d6e 100644 --- a/src/Game/Systems/PlayerHUDSystem.cpp +++ b/src/Game/Systems/HealthHUDSystem.cpp @@ -1,6 +1,6 @@ -#include "Game/Systems/PlayerHUDSystem.h" +#include "Game/Systems/HealthHUDSystem.h" -void PlayerHUDSystem::Update(double dt) +void HealthHUDSystem::Update(double dt) { auto healthHUDs = m_World->GetComponents("HealthHUD"); if (healthHUDs == nullptr) { @@ -27,13 +27,13 @@ void PlayerHUDSystem::Update(double dt) s = s + "/"; s = s + std::to_string((int)(double)entityIDParent["Health"]["MaxHealth"]); float healthPercentage = (double)entityIDParent["Health"]["Health"]/(double)entityIDParent["Health"]["MaxHealth"]; - (glm::vec4&)entity["Text"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, 1.f); + //(glm::vec4&)entity["Text"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, glm::vec4(entity["Text"]["Color"]).a); entity["Text"]["Content"] = s; } if(entity.HasComponent("Fill")) { float healthPercentage = (double)entityIDParent["Health"]["Health"]/(double)entityIDParent["Health"]["MaxHealth"]; - (glm::vec4&)entity["Fill"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, 0.f); + (glm::vec4&)entity["Fill"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, glm::vec4(entity["Fill"]["Color"]).a); (double&)entity["Fill"]["Percentage"] = healthPercentage; } diff --git a/src/Game/Systems/HealthSystem.cpp b/src/Game/Systems/HealthSystem.cpp index 29d8790d..94f23c67 100644 --- a/src/Game/Systems/HealthSystem.cpp +++ b/src/Game/Systems/HealthSystem.cpp @@ -7,25 +7,43 @@ HealthSystem::HealthSystem(SystemParams params) //subscribe/listenTo playerdamage,healthpickup events (using the eventBroker) EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &HealthSystem::OnPlayerDamaged); EVENT_SUBSCRIBE_MEMBER(m_EPlayerHealthPickup, &HealthSystem::OnPlayerHealthPickup); + EVENT_SUBSCRIBE_MEMBER(m_InputCommand, &HealthSystem::OnInputCommand); + m_NetworkEnabled = ResourceManager::Load("Config.ini")->Get("Networking.StartNetwork", false); } -void HealthSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) +void HealthSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cHealth, double dt) { + double& health = cHealth["Health"]; + if (health <= 0.0) { + Events::PlayerDeath ePlayerDeath; + ePlayerDeath.Player = entity; + m_EventBroker->Publish(ePlayerDeath); + //Note: we will delete the entity in PlayerDeathSystem + } } bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) { + if (!IsServer && m_NetworkEnabled || !e.Victim.Valid()) { + return false; + } + ComponentWrapper cHealth = e.Victim["Health"]; double& health = cHealth["Health"]; health -= e.Damage; - if (health <= 0.0) { - Events::PlayerDeath ePlayerDeath; - ePlayerDeath.Player = e.Victim; - m_EventBroker->Publish(ePlayerDeath); - //Note: we will delete the entity in PlayerDeathSystem - } + return true; +} +bool HealthSystem::OnInputCommand(Events::InputCommand& e) +{ + if (e.Command == "TakeDamage" && e.Value > 0 && LocalPlayer.Valid()) { + Events::PlayerDamage ev; + ev.Inflictor = LocalPlayer; + ev.Victim = LocalPlayer; + ev.Damage = e.Value; + m_EventBroker->Publish(ev); + } return true; } diff --git a/src/Game/Systems/KillFeedSystem.cpp b/src/Game/Systems/KillFeedSystem.cpp new file mode 100644 index 00000000..8a16d708 --- /dev/null +++ b/src/Game/Systems/KillFeedSystem.cpp @@ -0,0 +1,80 @@ +#include "Game/Systems/KillFeedSystem.h" + +void KillFeedSystem::Update(double dt) +{ + auto killFeeds = m_World->GetComponents("KillFeed"); + if (killFeeds == nullptr) { + return; + } + + for (auto& killFeedComponent : *killFeeds) { + EntityWrapper entity = EntityWrapper(m_World, killFeedComponent.EntityID); + + for (int i = 1; i <= 3; i++) { + EntityWrapper child = entity.FirstChildByName("KillFeed" + std::to_string(i)); + if (child.HasComponent("Text")) { + (std::string&)child["Text"]["Content"] = ""; + } + } + + + + + int feedIndex = 1; + for (auto it = m_DeathQueue.begin(); it != m_DeathQueue.end(); ) { + bool remove = false; + + EntityWrapper child = entity.FirstChildByName("KillFeed" + std::to_string(feedIndex)); + + if (child.HasComponent("Text")) { + (std::string&)child["Text"]["Content"] = (*it).Content; + (glm::vec4&)child["Text"]["Color"] = (*it).Color; + + (*it).TimeToLive -= dt; + + if ((*it).TimeToLive <= 0.f) { + (std::string&)child["Text"]["Content"] = ""; + (glm::vec4&)child["Text"]["Color"] = (*it).Color; + remove = true; + } + } + feedIndex++; + if(feedIndex > 3) { + break; + } + + if(remove) { + it = m_DeathQueue.erase(it); + } else { + it++; + } + } + } +} + +bool KillFeedSystem::OnPlayerDeath(Events::PlayerDeath& e) +{ + KillFeedInfo kfInfo; + + if (e.Player.HasComponent("Team")) { + int red = e.Player["Team"].Enum("Team", "Red"); + int blue = e.Player["Team"].Enum("Team", "Blue"); + + if ((int)e.Player["Team"]["Team"] == red) { + kfInfo.Content = "Blue Player killed Red Player"; + kfInfo.Color = glm::vec4(0.f, 0.2f, 1.f, 0.8f); + m_DeathQueue.push_back(kfInfo); + } else if ((int)e.Player["Team"]["Team"] == blue) { + kfInfo.Content = "Red Player killed blue Player"; + kfInfo.Color = glm::vec4(1.f, 0.f, 0.f, 0.8f); + m_DeathQueue.push_back(kfInfo); + } + } + + + if(m_DeathQueue.size() > 3) { + m_DeathQueue.pop_front(); + } + + return true; +} diff --git a/src/Game/Systems/PickupSpawnSystem.cpp b/src/Game/Systems/PickupSpawnSystem.cpp index 159f716b..abf59007 100644 --- a/src/Game/Systems/PickupSpawnSystem.cpp +++ b/src/Game/Systems/PickupSpawnSystem.cpp @@ -29,6 +29,7 @@ void PickupSpawnSystem::Update(double dt) newHealthPickupEntity["Transform"]["Position"] = healthPickupPosition.Pos; newHealthPickupEntity["HealthPickup"]["HealthGain"] = healthPickupPosition.HealthGain; newHealthPickupEntity["HealthPickup"]["RespawnTimer"] = healthPickupPosition.RespawnTimer; + m_World->SetParent(newHealthPickupEntity.ID, healthPickupPosition.parentID); //erase the current element (healthPickupPosition) m_ETriggerTouchVector.erase(it); @@ -58,7 +59,7 @@ bool PickupSpawnSystem::OnTriggerTouch(Events::TriggerTouch& e) //copy position, healthgain, respawntimer (twice since one of the values will be counted down to 0, the other will be set in the new object) //we need to copy all values since each value can be different for each healthPickup m_ETriggerTouchVector.push_back({ (glm::vec3)e.Trigger["Transform"]["Position"] ,e.Trigger["HealthPickup"]["HealthGain"], - e.Trigger["HealthPickup"]["RespawnTimer"],e.Trigger["HealthPickup"]["RespawnTimer"] }); + e.Trigger["HealthPickup"]["RespawnTimer"],e.Trigger["HealthPickup"]["RespawnTimer"], m_World->GetParent(e.Trigger.ID) }); //delete the healthpickup m_World->DeleteEntity(e.Trigger.ID); diff --git a/src/Game/Systems/PlayerDeathSystem.cpp b/src/Game/Systems/PlayerDeathSystem.cpp index 78e20490..844d2ed1 100644 --- a/src/Game/Systems/PlayerDeathSystem.cpp +++ b/src/Game/Systems/PlayerDeathSystem.cpp @@ -33,11 +33,17 @@ void PlayerDeathSystem::createDeathEffect(EntityWrapper player) EntityWrapper deathEffectEW = EntityWrapper(m_World, deathEffectID); //components that we need from player - auto playerCamera = player.FirstChildByName("Camera"); - auto playerEntityModel = player.FirstChildByName("PlayerModel")["Model"]; - auto playerEntityAnimation = player.FirstChildByName("PlayerModel")["Animation"]; + auto playerModel = player.FirstChildByName("PlayerModel"); + if (!playerModel.Valid()) { + return; + } + if (!playerModel.HasComponent("Model") || !playerModel.HasComponent("Animation")) { + return; + } + auto playerEntityModel = playerModel["Model"]; + auto playerEntityAnimation = playerModel["Animation"]; - //copy the data from player to explisioneffectmodel + //copy the data from player to explosioneffectmodel playerEntityModel.Copy(deathEffectEW["Model"]); playerEntityAnimation.Copy(deathEffectEW["Animation"]); //freeze the animation diff --git a/src/Game/Systems/PlayerMovementSystem.cpp b/src/Game/Systems/PlayerMovementSystem.cpp index 461c7098..2e2502ec 100644 --- a/src/Game/Systems/PlayerMovementSystem.cpp +++ b/src/Game/Systems/PlayerMovementSystem.cpp @@ -4,6 +4,7 @@ PlayerMovementSystem::PlayerMovementSystem(SystemParams params) : System(params) { EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &PlayerMovementSystem::OnPlayerSpawned); + EVENT_SUBSCRIBE_MEMBER(m_EDoubleJump, &PlayerMovementSystem::OnDoubleJump); } PlayerMovementSystem::~PlayerMovementSystem() @@ -16,7 +17,15 @@ PlayerMovementSystem::~PlayerMovementSystem() void PlayerMovementSystem::Update(double dt) { updateMovementControllers(dt); - updateVelocity(dt); + if (IsServer) { + for (auto& kv : m_PlayerInputControllers) { + updateVelocity(kv.first, dt); + } + } else { + if (LocalPlayer.Valid()) { + updateVelocity(LocalPlayer, dt); + } + } } void PlayerMovementSystem::updateMovementControllers(double dt) @@ -28,7 +37,6 @@ void PlayerMovementSystem::updateMovementControllers(double dt) if (!player.Valid()) { continue; } - // Aim pitch EntityWrapper cameraEntity = player.FirstChildByName("Camera"); if (cameraEntity.Valid()) { @@ -40,7 +48,8 @@ void PlayerMovementSystem::updateMovementControllers(double dt) EntityWrapper playerModel = player.FirstChildByName("PlayerModel"); if (playerModel.Valid()) { ComponentWrapper cAnimationOffset = playerModel["AnimationOffset"]; - double time = (cameraOrientation.x + glm::half_pi()) / glm::pi(); + float pitch = cameraOrientation.x + 0.2; + double time = (pitch + glm::half_pi()) / glm::pi(); cAnimationOffset["Time"] = time; } } @@ -78,18 +87,18 @@ void PlayerMovementSystem::updateMovementControllers(double dt) } glm::vec3& velocity = cPhysics["Velocity"]; bool isOnGround = (bool)cPhysics["IsOnGround"]; - ImGui::Text(isOnGround ? "On ground" : "In air"); - ImGui::Text("velocity: (%f, %f, %f) |%f|", velocity.x, velocity.y, velocity.z, glm::length(velocity)); + //ImGui::Text(isOnGround ? "On ground" : "In air"); + //ImGui::Text("velocity: (%f, %f, %f) |%f|", velocity.x, velocity.y, velocity.z, glm::length(velocity)); glm::vec3 groundVelocity(0.f, 0.f, 0.f); groundVelocity.x = velocity.x; groundVelocity.z = velocity.z; - ImGui::Text("groundVelocity: (%f, %f, %f) |%f|", groundVelocity.x, groundVelocity.y, groundVelocity.z, glm::length(groundVelocity)); - ImGui::Text("wishDirection: (%f, %f, %f) |%f|", wishDirection.x, wishDirection.y, wishDirection.z, glm::length(wishDirection)); + //ImGui::Text("groundVelocity: (%f, %f, %f) |%f|", groundVelocity.x, groundVelocity.y, groundVelocity.z, glm::length(groundVelocity)); + //ImGui::Text("wishDirection: (%f, %f, %f) |%f|", wishDirection.x, wishDirection.y, wishDirection.z, glm::length(wishDirection)); float currentSpeedProj = glm::dot(groundVelocity, wishDirection); float addSpeed = wishSpeed - currentSpeedProj; - ImGui::Text("currentSpeedProj: %f", currentSpeedProj); - ImGui::Text("wishSpeed: %f", wishSpeed); - ImGui::Text("addSpeed: %f", addSpeed); + //ImGui::Text("currentSpeedProj: %f", currentSpeedProj); + //ImGui::Text("wishSpeed: %f", wishSpeed); + //ImGui::Text("addSpeed: %f", addSpeed); if (addSpeed > 0) { static float accel = 15.f; @@ -113,15 +122,16 @@ void PlayerMovementSystem::updateMovementControllers(double dt) if (isOnGround) { controller->SetDoubleJumping(false); } else { - //put a hexagon at the players feet - auto hexagonEffect = ResourceManager::Load("Schema/Entities/DoubleJumpHexagon.xml"); - EntityFileParser parser(hexagonEffect); - EntityID hexagonEffectID = parser.MergeEntities(m_World); - EntityWrapper hexagonEW = EntityWrapper(m_World, hexagonEffectID); - hexagonEW["Transform"]["Position"] = (glm::vec3)player["Transform"]["Position"]; - controller->SetDoubleJumping(true); - Events::DoubleJump e; - m_EventBroker->Publish(e); + // If IsServer and network is off this will not work + if (IsClient) { + //put a hexagon at the players feet + spawnHexagon(player); + controller->SetDoubleJumping(true); + // Publish event for client to listen to + Events::DoubleJump e; + e.entityID = player.ID; + m_EventBroker->Publish(e); + } } velocity.y = 4.f; } @@ -218,15 +228,11 @@ void PlayerMovementSystem::updateMovementControllers(double dt) } -void PlayerMovementSystem::updateVelocity(double dt) +void PlayerMovementSystem::updateVelocity(EntityWrapper player, double dt) { // Only apply velocity to local player - if (!LocalPlayer.Valid()) { - return; - } - - ComponentWrapper& cTransform = LocalPlayer["Transform"]; - ComponentWrapper& cPhysics = LocalPlayer["Physics"]; + ComponentWrapper& cTransform = player["Transform"]; + ComponentWrapper& cPhysics = player["Physics"]; glm::vec3& velocity = cPhysics["Velocity"]; bool isOnGround = (bool)cPhysics["IsOnGround"]; @@ -288,3 +294,26 @@ bool PlayerMovementSystem::OnPlayerSpawned(Events::PlayerSpawned& e) } return true; } + +bool PlayerMovementSystem::OnDoubleJump(Events::DoubleJump & e) +{ + // If entity does not exist, exit + if (!EntityWrapper(m_World, e.entityID).Valid()) { + return false; + } + // If entity IsLocalPlayer, exit + if (e.entityID == m_LocalPlayer.ID) { + return false; + } + spawnHexagon(EntityWrapper(m_World, e.entityID)); +} + +void PlayerMovementSystem::spawnHexagon(EntityWrapper target) +{ + //put a hexagon at the entitys... feet? + auto hexagonEffect = ResourceManager::Load("Schema/Entities/DoubleJumpHexagon.xml"); + EntityFileParser parser(hexagonEffect); + EntityID hexagonEffectID = parser.MergeEntities(m_World); + EntityWrapper hexagonEW = EntityWrapper(m_World, hexagonEffectID); + hexagonEW["Transform"]["Position"] = (glm::vec3)target["Transform"]["Position"]; +} \ No newline at end of file diff --git a/src/Game/Systems/PlayerSpawnSystem.cpp b/src/Game/Systems/PlayerSpawnSystem.cpp index 444fc08f..6254c674 100644 --- a/src/Game/Systems/PlayerSpawnSystem.cpp +++ b/src/Game/Systems/PlayerSpawnSystem.cpp @@ -1,20 +1,39 @@ #include "Systems/PlayerSpawnSystem.h" -PlayerSpawnSystem::PlayerSpawnSystem(SystemParams params) +//This should be set by the config anyway. +float PlayerSpawnSystem::m_RespawnTime = 15.0f; + +PlayerSpawnSystem::PlayerSpawnSystem(SystemParams params) : System(params) + , m_Timer(0.f) { EVENT_SUBSCRIBE_MEMBER(m_OnInputCommand, &PlayerSpawnSystem::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_OnPlayerSpawnerd, &PlayerSpawnSystem::OnPlayerSpawned); + EVENT_SUBSCRIBE_MEMBER(m_OnPlayerDeath, &PlayerSpawnSystem::OnPlayerDeath); m_NetworkEnabled = ResourceManager::Load("Config.ini")->Get("Networking.StartNetwork", false); } void PlayerSpawnSystem::Update(double dt) { + //Increase timer. + m_Timer += dt; + if (m_Timer < m_RespawnTime) { + return; + } + //If respawn time has passed, we spawn all players that have requested to be spawned. + m_Timer = 0.f; + + //If there are no spawn requests, return immediately, if we are client the SpawnRequests should always be empty. + if (m_SpawnRequests.size() == 0) { + return; + } + auto playerSpawns = m_World->GetComponents("PlayerSpawn"); if (playerSpawns == nullptr) { return; } + int numSpawnedPlayers = 0; for (auto& req : m_SpawnRequests) { for (auto& cPlayerSpawn : *playerSpawns) { EntityWrapper spawner(m_World, cPlayerSpawn.EntityID); @@ -30,7 +49,7 @@ void PlayerSpawnSystem::Update(double dt) } // Spawn the player! - EntityWrapper player = SpawnerSystem::Spawn(spawner); + EntityWrapper player = SpawnerSystem::Spawn(spawner, EntityWrapper::Invalid, "Player"); // Set the player team affiliation player["Team"]["Team"] = req.Team; @@ -40,29 +59,61 @@ void PlayerSpawnSystem::Update(double dt) e.Player = player; e.Spawner = spawner; m_EventBroker->Publish(e); - + ++numSpawnedPlayers; + break; } } + if (numSpawnedPlayers != (int)m_SpawnRequests.size()) { + LOG_DEBUG("%i players were supposed to be spawned, but %i was spawned.", (int)m_SpawnRequests.size(), numSpawnedPlayers); + } else { + LOG_DEBUG("%i players were spawned.", numSpawnedPlayers); + } m_SpawnRequests.clear(); } -bool PlayerSpawnSystem::OnInputCommand(const Events::InputCommand& e) +bool PlayerSpawnSystem::OnInputCommand(Events::InputCommand& e) { if (e.Command != "PickTeam") { return false; } // Team picks should be processed ONLY server-side! - // Don't make a spawn request if PlayerID is -1, i.e. we're the client. - if (e.PlayerID == -1 && m_NetworkEnabled) { + // Don't make a spawn request if we're the client. + if (!IsServer && m_NetworkEnabled) { return false; } - if (e.Value != 0) { + if (e.Value == 0) { + return false; + } + + //TODO: Spectating? + //Right now, return if someone picks spectator. + //1 signifies spectator here, could not get Playerteam component since it may be invalid or without team comp. + if ((ComponentInfo::EnumType)e.Value == 1) { + return false; + } + + //Check if the player already requested spawn. + auto iter = m_SpawnRequests.begin(); + for (; iter != m_SpawnRequests.end(); ++iter) { + if (iter->PlayerID == e.PlayerID) { + break; + } + } + + if (iter != m_SpawnRequests.end()) { + //If player is in queue to spawn, then change their team affiliation in the request. + iter->Team = (ComponentInfo::EnumType)e.Value; + } else if (m_PlayerEntities.count(e.PlayerID) == 0 || !m_PlayerEntities[e.PlayerID].Valid()) { + //If player is not in queue to spawn, then create a spawn request, + //but only if they are spectating and/or just connected. SpawnRequest req; req.PlayerID = e.PlayerID; req.Team = (ComponentInfo::EnumType)e.Value; m_SpawnRequests.push_back(req); + } else { + return false; } return true; @@ -70,22 +121,23 @@ bool PlayerSpawnSystem::OnInputCommand(const Events::InputCommand& e) bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) { + // Store the player for future reference + m_PlayerEntities[e.PlayerID] = e.Player; + m_PlayerIDs[e.Player.ID] = e.PlayerID; + // When a player is actually spawned (since the actual spawning is handled on the server) + // Hack should be moved. + + // TODO: Set the player name to whatever + EntityWrapper playerName = e.Player.FirstChildByName("PlayerName"); + if (playerName.Valid()) { + playerName["Text"]["Content"] = e.PlayerName; + } + if (!IsClient) { return false; } - // Check if a player already exists - if (m_PlayerEntities.count(e.PlayerID) != 0) { - // TODO: Disallow infinite respawning here - if (m_PlayerEntities[e.PlayerID].Valid()) { - m_World->DeleteEntity(m_PlayerEntities[e.PlayerID].ID); - } - } - - // Store the player for future reference - m_PlayerEntities[e.PlayerID] = e.Player; - // Set the camera to the correct entity EntityWrapper cameraEntity = e.Player.FirstChildByName("Camera"); bool outOfBodyExperience = ResourceManager::Load("Config.ini")->Get("Debug.OutOfBodyExperience", false); @@ -107,11 +159,32 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) } } - // TODO: Set the player name to whatever - EntityWrapper playerName = e.Player.FirstChildByName("PlayerName"); - if (playerName.Valid()) { - playerName["Text"]["Content"] = e.PlayerName; + return true; +} + +bool PlayerSpawnSystem::OnPlayerDeath(Events::PlayerDeath& e) +{ + //Only spawn request if network is disabled or we are server. + if (!IsServer && m_NetworkEnabled) { + return false; + } + if (!e.Player.HasComponent("Team")) { + return false; + } + ComponentWrapper cTeam = e.Player["Team"]; + //A spectator can't die anyway + if ((ComponentInfo::EnumType)cTeam["Team"] == cTeam["Team"].Enum("Spectator")) { + return false; } + if (m_PlayerIDs.count(e.Player.ID) == 0) { + return false; + } + + SpawnRequest req; + req.PlayerID = m_PlayerIDs.at(e.Player.ID); + req.Team = cTeam["Team"]; + m_SpawnRequests.push_back(req); + return true; -} \ No newline at end of file +} diff --git a/src/Game/Systems/SoundSystem.cpp b/src/Game/Systems/SoundSystem.cpp index d6ac7dab..b4a37ad0 100644 --- a/src/Game/Systems/SoundSystem.cpp +++ b/src/Game/Systems/SoundSystem.cpp @@ -6,13 +6,16 @@ SoundSystem::SoundSystem(SystemParams params) { ConfigFile* config = ResourceManager::Load("Config.ini"); m_Announcer = ResourceManager::Load("Config.ini")->Get("Sound.Announcer", "female"); - EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &SoundSystem::OnPlayerSpawned); - EVENT_SUBSCRIBE_MEMBER(m_InputCommand, &SoundSystem::OnInputCommand); - EVENT_SUBSCRIBE_MEMBER(m_EDoubleJump, &SoundSystem::OnDoubleJump); - EVENT_SUBSCRIBE_MEMBER(m_EDashAbility, &SoundSystem::OnDashAbility); - EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &SoundSystem::OnPlayerDamage); - EVENT_SUBSCRIBE_MEMBER(m_ECaptured, &SoundSystem::OnCaptured); - EVENT_SUBSCRIBE_MEMBER(m_ETriggerTouch, &SoundSystem::OnTriggerTouch); + if (IsClient) { + EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &SoundSystem::OnPlayerSpawned); + EVENT_SUBSCRIBE_MEMBER(m_InputCommand, &SoundSystem::OnInputCommand); + EVENT_SUBSCRIBE_MEMBER(m_EDoubleJump, &SoundSystem::OnDoubleJump); + EVENT_SUBSCRIBE_MEMBER(m_EDashAbility, &SoundSystem::OnDashAbility); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &SoundSystem::OnPlayerDamage); + EVENT_SUBSCRIBE_MEMBER(m_ECaptured, &SoundSystem::OnCaptured); + EVENT_SUBSCRIBE_MEMBER(m_ETriggerTouch, &SoundSystem::OnTriggerTouch); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerDeath, &SoundSystem::OnPlayerDeath); + } } void SoundSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cComponent, double dt) @@ -20,6 +23,10 @@ void SoundSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cComp void SoundSystem::Update(double dt) { + if (!IsClient) { + return; + } + // Temp for play test. if (m_DrumsIsPlaying) { m_DrumsIsPlaying = !drumTimer(dt); @@ -52,12 +59,6 @@ bool SoundSystem::OnInputCommand(const Events::InputCommand & e) return true; } } - if (e.Command == "TakeDamage" && e.Value > 0) { - Events::PlayerDamage ev; - ev.Victim = LocalPlayer; - ev.Damage = 1.0; - m_EventBroker->Publish(ev); - } return false; } @@ -90,6 +91,9 @@ bool SoundSystem::drumTimer(double dt) bool SoundSystem::OnCaptured(const Events::Captured & e) { + if (!LocalPlayer.Valid()) { + return false; + } int homeTeam = (int)m_World->GetComponent(e.CapturePointID, "Team")["Team"]; int team = (int)m_World->GetComponent(LocalPlayer.ID, "Team")["Team"]; Events::PlaySoundOnEntity ev; @@ -108,7 +112,12 @@ bool SoundSystem::OnCaptured(const Events::Captured & e) // Testing purposes atm... bool SoundSystem::OnPlayerDamage(const Events::PlayerDamage & e) { - // Should check for only local players here... + if (!IsClient) { // Only play for clients + return false; + } + if (LocalPlayer.ID = e.Victim.ID) { // You're local player was the one who took dmg + return false; + } std::uniform_int_distribution dist(1, 12); int rand = dist(generator); std::vector paths; @@ -128,8 +137,16 @@ bool SoundSystem::OnPlayerDamage(const Events::PlayerDamage & e) bool SoundSystem::OnPlayerDeath(const Events::PlayerDeath & e) { - Events::PlaySoundOnEntity ev; - ev.EmitterID = LocalPlayer.ID; + if (e.Player.ID != LocalPlayer.ID) { + return false; + } + if (!IsClient) { + return false; + } + // The local player is dead. The local player might be invalid? + // Play the sound from the listener. + // TODO: We might want to hear other players die. + Events::PlayBackgroundMusic ev; ev.FilePath = "Audio/die/die2.wav"; m_EventBroker->Publish(ev); return false; diff --git a/src/Game/Systems/SpawnerSystem.cpp b/src/Game/Systems/SpawnerSystem.cpp index b3c556f1..90f677fe 100644 --- a/src/Game/Systems/SpawnerSystem.cpp +++ b/src/Game/Systems/SpawnerSystem.cpp @@ -1,12 +1,13 @@ #include "Systems/SpawnerSystem.h" +#include "Collision/Collision.h" -SpawnerSystem::SpawnerSystem(SystemParams params) +SpawnerSystem::SpawnerSystem(SystemParams params) : System(params) { EVENT_SUBSCRIBE_MEMBER(m_OnSpawnerSpawn, &SpawnerSystem::OnSpawnerSpawn); } -EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent /*= EntityWrapper::Invalid*/) +EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent /*= EntityWrapper::Invalid*/, const std::string& dontCollideComponent) { // Spawn the entity in the parent's world if it exists, otherwise in the spawner's world World* world = parent.World; @@ -14,17 +15,41 @@ EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent / world = spawner.World; } + // Load the entity file and parse it + const std::string& entityFilePath = spawner["Spawner"]["EntityFile"]; + auto entityFile = ResourceManager::Load(entityFilePath); + if (entityFile == nullptr) { + return EntityWrapper::Invalid; + } + EntityFileParser parser(entityFile); + EntityWrapper spawnedEntity(world, parser.MergeEntities(world, parent.ID)); + + //If the spawned entity is collideable, then we must not spawn it where it collides with something that + //has a dontCollideComponent attached. + bool spawnOnCollidable = dontCollideComponent.empty() || !spawnedEntity.HasComponent("Collidable"); + if (!spawnOnCollidable) { + boost::optional optBox = Collision::EntityAbsoluteAABB(spawnedEntity); + //If we can't calculate the box for some reason, then just spawn somewhere anyway. + if (!optBox) { + spawnOnCollidable = true; + } + } + // Find any SpawnPoints existing as children of spawner auto children = spawner.World->GetChildren(spawner.ID); std::vector spawnPoints; for (auto kv = children.first; kv != children.second; ++kv) { const EntityID& child = kv->second; if (spawner.World->HasComponent(child, "SpawnPoint")) { - spawnPoints.push_back(EntityWrapper(spawner.World, child)); + EntityWrapper spawnPoint = EntityWrapper(spawner.World, child); + if (spawnOnCollidable || !spawnedEntityIsColliding(spawnedEntity, spawnPoint, dontCollideComponent)) { + spawnPoints.push_back(spawnPoint); + } } } // Choose a random SpawnPoint + // If there are no children, or if they are all blocked, then the entity will be spawned at the spawner itself. EntityWrapper spawnPoint = spawner; if (!spawnPoints.empty()) { if (spawnPoints.size() > 1) { @@ -39,25 +64,61 @@ EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent / } } - // Load the entity file and parse it - const std::string& entityFilePath = spawner["Spawner"]["EntityFile"]; - auto entityFile = ResourceManager::Load(entityFilePath); - if (entityFile == nullptr) { - return EntityWrapper::Invalid; - } - EntityFileParser parser(entityFile); - EntityWrapper spawnedEntity(world, parser.MergeEntities(world, parent.ID)); - if (spawnPoint != parent) { - // Set its position and orientation to that of the SpawnPoint - spawnedEntity["Transform"]["Position"] = Transform::AbsolutePosition(spawnPoint.World, spawnPoint.ID); - // TODO: Quaternions, bitch - spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(spawnPoint)); + transformEntityToSpawnPoint(spawnedEntity, spawnPoint); } return spawnedEntity; } +void SpawnerSystem::transformEntityToSpawnPoint(EntityWrapper spawnedEntity, EntityWrapper spawnPoint) +{ + // Set its position and orientation to that of the SpawnPoint + spawnedEntity["Transform"]["Position"] = Transform::AbsolutePosition(spawnPoint.World, spawnPoint.ID); + // TODO: Quaternions, bitch + spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(spawnPoint)); +} + +bool SpawnerSystem::spawnedEntityIsColliding(EntityWrapper spawnedEntity, EntityWrapper spawnPoint, const std::string& dontCollideComponent) +{ + transformEntityToSpawnPoint(spawnedEntity, spawnPoint); + //Check if the spawned entity collides with anything, and if so, continue to the next spawnpoint. + EntityAABB spawnedBox = *Collision::EntityAbsoluteAABB(spawnedEntity); + const ComponentPool* otherSpawnedEntities = spawnPoint.World->GetComponents(dontCollideComponent); + for (const auto& obj : *otherSpawnedEntities) { + if (spawnedEntity.ID == obj.EntityID) { + continue; + } + EntityWrapper otherEntity = EntityWrapper(spawnPoint.World, obj.EntityID); + if (!otherEntity.HasComponent("Collidable")) { + continue; + } + auto otherBox = Collision::EntityAbsoluteAABB(otherEntity); + if (!otherBox) { + continue; + } + if (Collision::AABBVsAABB(spawnedBox, *otherBox)) { + if (!spawnedBox.Entity.HasComponent("Model")) { + return true; + } + RawModel* model = nullptr; + try { + model = ResourceManager::Load(otherEntity["Model"]["Resource"]); + } catch (const std::exception&) { + } + + if (model != nullptr && Collision::AABBvsTriangles( + spawnedBox, + model->Vertices(), + model->m_Indices, + Transform::ModelMatrix(otherEntity))) { + return true; + } + } + } + return false; +} + bool SpawnerSystem::OnSpawnerSpawn(Events::SpawnerSpawn& e) { EntityWrapper spawnedEntity = Spawn(e.Spawner, e.Parent); diff --git a/src/Game/Systems/Weapon/AssaultWeaponBehaviour.cpp b/src/Game/Systems/Weapon/AssaultWeaponBehaviour.cpp index 54c3a590..84d3ccd4 100644 --- a/src/Game/Systems/Weapon/AssaultWeaponBehaviour.cpp +++ b/src/Game/Systems/Weapon/AssaultWeaponBehaviour.cpp @@ -4,6 +4,7 @@ AssaultWeaponBehaviour::AssaultWeaponBehaviour(SystemParams systemParams, IRende : WeaponBehaviour(systemParams, renderer, collisionOctree, player) { m_FirstPersonModel = m_Player.FirstChildByName("Hands"); + m_ThirdPersonModel = m_Player.FirstChildByName("PlayerModel"); EVENT_SUBSCRIBE_MEMBER(m_EAnimationComplete, &AssaultWeaponBehaviour::OnAnimationComplete); } @@ -37,12 +38,18 @@ void AssaultWeaponBehaviour::Reload() // Don't reload if we're completly out of ammo if (ammo == 0) { + playEmptySound(); + m_TimeSinceLastFire = -0.0f; // HACK: To make empty sound play with interval return; } m_Reloading = true; m_ReloadTimer = cAssaultWeapon["ReloadTime"]; playReloadAnimation(); + Events::PlaySoundOnEntity e; + e.EmitterID = cAssaultWeapon.EntityID; + e.FilePath = "Audio/weapon/reload.wav"; + m_EventBroker->Publish(e); } void AssaultWeaponBehaviour::Update(double dt) @@ -50,9 +57,14 @@ void AssaultWeaponBehaviour::Update(double dt) if (m_Reloading) { m_ReloadTimer -= dt; // Re-enable glow on reload impersonator half-way through the animation - if (m_ReloadTimer <= (double)m_Player["AssaultWeapon"]["ReloadTime"] / 2.0) { - if (m_ReloadImpersonator.Valid()) { - m_ReloadImpersonator["Model"]["GlowMap"] = true; + if (IsClient) { + if (m_ReloadTimer <= (double)m_Player["AssaultWeapon"]["ReloadTime"] / 2.0) { + if (m_FirstPersonReloadImpersonator.Valid()) { + m_FirstPersonReloadImpersonator["Model"]["GlowMap"] = true; + } + if (m_ThirdPersonReloadImpersonator.Valid()) { + m_ThirdPersonReloadImpersonator["Model"]["GlowMap"] = true; + } } } if (m_ReloadTimer <= 0) { @@ -69,14 +81,18 @@ void AssaultWeaponBehaviour::Update(double dt) } if (!m_Firing && !m_Reloading) { - playIdleAnimation(); + if (IsClient) { + playIdleAnimation(); + } } // Disable glow map on weapon if it's out of ammo // Make real first person weapon model visible again - EntityWrapper firstPersonWeaponModel = m_Player.FirstChildByName("WeaponModel"); - if (firstPersonWeaponModel.Valid()) { - firstPersonWeaponModel["Model"]["GlowMap"] = hasAmmo(); + if (IsClient) { + EntityWrapper firstPersonWeaponModel = m_Player.FirstChildByName("WeaponModel"); + if (firstPersonWeaponModel.Valid()) { + firstPersonWeaponModel["Model"]["GlowMap"] = hasAmmo(); + } } } @@ -117,20 +133,23 @@ void AssaultWeaponBehaviour::fireRound() if (magAmmo <= 0) { Reload(); return; - } + } // Fire magAmmo -= 1; - spawnTracer(); - playSound(); - viewPunch(); - playShootAnimation(); - bool hit = shoot(cAssaultWeapon["BaseDamage"]); - if (hit) { - showHitMarker(); - } - m_TimeSinceLastFire = 0.0; + + // Effects + if (IsClient) { + spawnTracer(); + playFireSound(); + viewPunch(); + playShootAnimation(); + bool hit = shoot(cAssaultWeapon["BaseDamage"]); + if (hit) { + showHitMarker(); + } + } } void AssaultWeaponBehaviour::spawnTracer() @@ -140,7 +159,8 @@ void AssaultWeaponBehaviour::spawnTracer() } EntityWrapper spawner; - if (m_Player == LocalPlayer) { + bool outOfBodyExperience = ResourceManager::Load("Config.ini")->Get("Debug.OutOfBodyExperience", false); + if (m_Player == LocalPlayer && !outOfBodyExperience) { spawner = m_Player.FirstChildByName("WeaponMuzzle"); } else { spawner = m_Player.FirstChildByName("ThirdPersonWeaponMuzzle"); @@ -168,7 +188,7 @@ float AssaultWeaponBehaviour::traceRayDistance(glm::vec3 origin, glm::vec3 direc } } -void AssaultWeaponBehaviour::playSound() +void AssaultWeaponBehaviour::playFireSound() { if (!IsClient) { return; @@ -180,6 +200,19 @@ void AssaultWeaponBehaviour::playSound() m_EventBroker->Publish(e); } + +void AssaultWeaponBehaviour::playEmptySound() +{ + if (!IsClient) { + return; + } + + Events::PlaySoundOnEntity e; + e.EmitterID = m_Player.ID; + e.FilePath = "Audio/weapon/zeroAmmo.wav"; + m_EventBroker->Publish(e); +} + void AssaultWeaponBehaviour::viewPunch() { EntityWrapper playerCamera = m_Player.FirstChildByName("Camera"); @@ -206,7 +239,13 @@ void AssaultWeaponBehaviour::finishReload() // Make real first person weapon model visible again EntityWrapper firstPersonWeaponModel = m_Player.FirstChildByName("WeaponModel"); - firstPersonWeaponModel["Model"]["Visible"] = true; + if (firstPersonWeaponModel.Valid()) { + firstPersonWeaponModel["Model"]["Visible"] = true; + } + EntityWrapper thirdPersonWeaponModel = m_Player.FirstChildByName("ThirdPersonWeaponModel"); + if (thirdPersonWeaponModel.Valid()) { + thirdPersonWeaponModel["Model"]["Visible"] = true; + } m_Reloading = false; } @@ -259,19 +298,45 @@ void AssaultWeaponBehaviour::playIdleAnimation() void AssaultWeaponBehaviour::playReloadAnimation() { // Play animation - ComponentWrapper cAnimation = m_FirstPersonModel["Animation"]; - cAnimation["AnimationName1"] = "ReloadSwitch"; - cAnimation["Weight1"] = 1.0; - cAnimation["Time1"] = 0.0; - cAnimation["Speed1"] = 0.5; - cAnimation["Loop1"] = true; + // First person + if (IsClient) + { + ComponentWrapper cAnimation = m_FirstPersonModel["Animation"]; + cAnimation["AnimationName1"] = "ReloadSwitch"; + cAnimation["Weight1"] = 1.0; + cAnimation["Time1"] = 0.0; + cAnimation["Speed1"] = 0.5; + cAnimation["Loop1"] = true; + } + // TODO: Third person + //{ + // ComponentWrapper cAnimation = m_ThirdPersonModel["Animation"]; + // cAnimation["AnimationName1"] = "ReloadSwitch"; + // cAnimation["Weight1"] = 1.0; + // cAnimation["Time1"] = 0.0; + // cAnimation["Speed1"] = 0.5; + // cAnimation["Loop1"] = true; + //} // Hide weapon model and spawn the exploding version - EntityWrapper firstPersonWeaponModel = m_Player.FirstChildByName("WeaponModel"); - EntityWrapper reloadSpawner = m_Player.FirstChildByName("FirstPersonReloadSpawner"); - m_ReloadImpersonator = SpawnerSystem::Spawn(reloadSpawner, reloadSpawner); - firstPersonWeaponModel["Model"].Copy(m_ReloadImpersonator["Model"]); - firstPersonWeaponModel["Model"]["Visible"] = false; + { + EntityWrapper firstPersonWeaponModel = m_Player.FirstChildByName("WeaponModel"); + EntityWrapper reloadSpawner = m_Player.FirstChildByName("FirstPersonReloadSpawner"); + if (IsClient) { + m_FirstPersonReloadImpersonator = SpawnerSystem::Spawn(reloadSpawner, reloadSpawner); + firstPersonWeaponModel["Model"].Copy(m_FirstPersonReloadImpersonator["Model"]); + } + firstPersonWeaponModel["Model"]["Visible"] = false; + } + { + EntityWrapper thirdPersonWeaponModel = m_Player.FirstChildByName("ThirdPersonWeaponModel"); + EntityWrapper reloadSpawner = m_Player.FirstChildByName("ThirdPersonReloadSpawner"); + if (IsClient) { + m_ThirdPersonReloadImpersonator = SpawnerSystem::Spawn(reloadSpawner, reloadSpawner); + thirdPersonWeaponModel["Model"].Copy(m_ThirdPersonReloadImpersonator["Model"]); + } + thirdPersonWeaponModel["Model"]["Visible"] = false; + } } bool AssaultWeaponBehaviour::shoot(double damage) @@ -302,6 +367,9 @@ bool AssaultWeaponBehaviour::shoot(double damage) } EntityWrapper victim(m_World, pickData.Entity); + if (!victim.Valid()) { + return false; + } // Don't let us shoot ourselves in the foot if (victim == LocalPlayer) { @@ -337,5 +405,9 @@ void AssaultWeaponBehaviour::showHitMarker() EntityWrapper hitMarkerSpawner = m_Player.FirstChildByName("HitMarkerSpawner"); if (hitMarkerSpawner.Valid()) { SpawnerSystem::Spawn(hitMarkerSpawner, hitMarkerSpawner); + Events::PlaySoundOnEntity e; + e.EmitterID = m_Player.ID; + e.FilePath = "Audio/weapon/hitclick.wav"; + m_EventBroker->Publish(e); } } diff --git a/src/Game/main.cpp b/src/Game/main.cpp index 613165dd..dd2a5a84 100644 --- a/src/Game/main.cpp +++ b/src/Game/main.cpp @@ -1,11 +1,25 @@ #include "Game.h" +#include "MiniDump.h" + +LONG WINAPI CrashHandler(EXCEPTION_POINTERS* pException); int main(int argc, char* argv[]) { - Game game(argc, argv); - while (game.Running()) { - game.Tick(); - } + ::SetUnhandledExceptionFilter(CrashHandler); + + Game game(argc, argv); + while (game.Running()) { + game.Tick(); + } return 0; +} + +LONG WINAPI CrashHandler(EXCEPTION_POINTERS* pException) +{ + //Take minidump. path should be bin/TacticalZ.dmp + //Then show MessageBox, and exit application. + Create_Dump(pException, 1, 1); + + return EXCEPTION_EXECUTE_HANDLER;// EXCEPTION_CONTINUE_SEARCH } \ No newline at end of file diff --git a/src/Tests/CapturePointTest.cpp b/src/Tests/CapturePointTest.cpp index 2c7bf430..8a9baf40 100644 --- a/src/Tests/CapturePointTest.cpp +++ b/src/Tests/CapturePointTest.cpp @@ -94,7 +94,7 @@ CapturePointTest::CapturePointTest(int runTestNumber) m_World = new World(); // Create system pipeline - m_SystemPipeline = new SystemPipeline(m_World,m_EventBroker); + m_SystemPipeline = new SystemPipeline(m_World,m_EventBroker, true, false); m_SystemPipeline->AddSystem(0); m_SystemPipeline->AddSystem(1); diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 6cb6c88b..b5ba8245 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -33,10 +33,10 @@ void RayTest(std::string fileName) { ResourceManager::RegisterType("RawModel"); auto unitBox = ResourceManager::Load(fileName); BOOST_REQUIRE(unitBox != nullptr); - bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + bool hit = Collision::RayVsModel(ray, unitBox->Vertices(), unitBox->m_Indices, glm::mat4(1)); BOOST_CHECK(hit); ray.SetDirection(glm::vec3(-1, 0, 0)); - hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + hit = Collision::RayVsModel(ray, unitBox->Vertices(), unitBox->m_Indices, glm::mat4(1)); BOOST_CHECK(!hit); } @@ -146,12 +146,12 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) z = Collision::RayVsAABB(ray, someAABB); if (z) { //hit - bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + bool hit = Collision::RayVsModel(ray, unitBox->Vertices(), unitBox->m_Indices,glm::mat4(1)); if (!hit) { //if rayvsaabb hit but rayvvmodel didnt hit, we get to here - glm::vec3 outtttttttt; - hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices, outtttttttt); - hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + glm::mat4 outtttttttt; + hit = Collision::RayVsModel(ray, unitBox->Vertices(), unitBox->m_Indices, glm::mat4(1)); + hit = Collision::RayVsModel(ray, unitBox->Vertices(), unitBox->m_Indices, glm::mat4(1)); } else { hit = hit; @@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) // z = z; //} // - bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + bool hit = Collision::RayVsModel(ray, unitBox->Vertices(), unitBox->m_Indices, glm::mat4(1)); ////breakpoint test //if (!hit) { // hit = hit; @@ -175,8 +175,8 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) //if rayvsmodel hit but rayvsaabb didnt hit then we get to here z = Collision::RayVsAABB(ray, someAABB); glm::vec3 outtttttttt; - hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices, outtttttttt); - hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices); + hit = Collision::RayVsModel(ray, unitBox->Vertices(), unitBox->m_Indices, glm::mat4(1)); + hit = Collision::RayVsModel(ray, unitBox->Vertices(), unitBox->m_Indices, glm::mat4(1)); } else { z = z; diff --git a/src/Tests/HealthSystemTest.cpp b/src/Tests/HealthSystemTest.cpp index 8bf16024..bf2650a3 100644 --- a/src/Tests/HealthSystemTest.cpp +++ b/src/Tests/HealthSystemTest.cpp @@ -48,26 +48,21 @@ GameHealthSystemTest::GameHealthSystemTest() fp.MergeEntities(m_World); // Create system pipeline - m_SystemPipeline = new SystemPipeline(m_World, m_EventBroker); + m_SystemPipeline = new SystemPipeline(m_World, m_EventBroker, false, false); m_SystemPipeline->AddSystem(0); //The Test //create entity which has transform,player,model,health in it. i.e. is a player EntityID playerID = m_World->CreateEntity(); ComponentWrapper player = m_World->AttachComponent(playerID, "Player"); - ComponentWrapper health = m_World->AttachComponent(playerID, "Health"); - healthsID = playerID; + ComponentWrapper& health = m_World->AttachComponent(playerID, "Health"); + health["Health"] = 100.0; + m_PlayersID = playerID; EntityID playerID2 = m_World->CreateEntity(); ComponentWrapper player2 = m_World->AttachComponent(playerID2, "Player"); ComponentWrapper health2 = m_World->AttachComponent(playerID2, "Health"); - //heal player with 40 - Events::PlayerHealthPickup e3; - e3.HealthAmount = 40.0f; - e3.Player = EntityWrapper(m_World, player.EntityID); - m_EventBroker->Publish(e3); - //damage player with 50 Events::PlayerDamage e; e.Damage = 50.0f; @@ -103,9 +98,18 @@ void GameHealthSystemTest::Tick() m_EventBroker->Swap(); m_EventBroker->Clear(); - - //if health reaches 90 then we know the test has succeeded (start with 100hp, remove 50hp, add 40hp) - double currentHealth = (double)m_World->GetComponent(healthsID, "Health")["Health"]; - if (currentHealth == 90) + + double currentHealth = (double)m_World->GetComponent(m_PlayersID, "Health")["Health"]; + //if players health reach 50 means he got damaged by 50 + if (currentHealth == 50.0) { + m_TestStage1Success = true; + //heal player with 40 + Events::PlayerHealthPickup e3; + e3.HealthAmount = 40.0f; + e3.Player = EntityWrapper(m_World, m_PlayersID); + m_EventBroker->Publish(e3); + } + if (m_TestStage1Success && currentHealth == 90.0f) { TestSucceeded = true; + } } diff --git a/src/Tests/HealthSystemTest.h b/src/Tests/HealthSystemTest.h index 62c5f55b..275558d2 100644 --- a/src/Tests/HealthSystemTest.h +++ b/src/Tests/HealthSystemTest.h @@ -6,7 +6,6 @@ #include "Core/EventBroker.h" #include "Rendering/Renderer.h" #include "Core/InputManager.h" -#include "GUI/Frame.h" #include "Core/World.h" #include "Input/InputProxy.h" #include "Input/KeyboardInputHandler.h" @@ -31,7 +30,9 @@ private: EventBroker* m_EventBroker; World* m_World; SystemPipeline* m_SystemPipeline; - int healthsID; + int m_PlayersID; + bool m_TestStage1Success = false; + }; #endif diff --git a/src/Tests/PickupSpawnTest.cpp b/src/Tests/PickupSpawnTest.cpp new file mode 100644 index 00000000..4acd897e --- /dev/null +++ b/src/Tests/PickupSpawnTest.cpp @@ -0,0 +1,214 @@ +#include +using boost::unit_test_framework::test_suite; +using boost::unit_test_framework::test_case; + +#include "PickupSpawnTest.h" + +BOOST_AUTO_TEST_SUITE(PickupSpawnTestSuite) + +//dont use the same name as the classname in test cases... +BOOST_AUTO_TEST_CASE(PickupSpawnTest_HealthPickupRespawns_PlayerHealthPickupEventTriggers) +{ + PickupSpawnTest game(1); + bool success = game.Game_Loop_OneHundredTimes(); + BOOST_TEST(success); +} +BOOST_AUTO_TEST_CASE(PickupSpawnTest_APlayerAtMaxHealth_CantTakeHealthPickup) +{ + PickupSpawnTest game(2); + bool success = game.Game_Loop_OneHundredTimes(); + BOOST_TEST(success); +} +BOOST_AUTO_TEST_CASE(PickupSpawnTest_APickupCanRespawnSlowly) +{ + PickupSpawnTest game(3); + bool success = game.Game_Loop_OneHundredTimes(); + BOOST_TEST(success); +} +BOOST_AUTO_TEST_SUITE_END() + +PickupSpawnTest::PickupSpawnTest(int runTestNumber) +{ + ResourceManager::RegisterType("ConfigFile"); + ResourceManager::RegisterType("EntityFile"); + + m_Config = ResourceManager::Load("Config.ini"); + LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get("Debug.LogLevel", 1)); + + m_EventBroker = new EventBroker(); + m_World = new World(); + + // Create system pipeline + m_SystemPipeline = new SystemPipeline(m_World, m_EventBroker, false, false); + m_SystemPipeline->AddSystem(0); + m_SystemPipeline->AddSystem(1); + + //must register components (Components.xsd), else you cant create entities. Easiest done by loading a test xsd file + auto file = ResourceManager::Load("Schema/Entities/HealthPickup.xml"); + EntityFilePreprocessor fpp(file); + fpp.RegisterComponents(m_World); + EntityFileParser fp(file); + //connect the healthpickup to the world + m_HealthPickupID = fp.MergeEntities(m_World); + + //create a player + m_PlayerID = m_World->CreateEntity(); + auto& player = m_World->AttachComponent(m_PlayerID, "Player"); + + m_RunTestNumber = runTestNumber; + + //further testsetups + TestSetup(m_RunTestNumber); + + //init glfw so dt works + glfwInit(); + + //listen to the 2 events that are related to PickupSpawn + EVENT_SUBSCRIBE_MEMBER(m_HP, &PickupSpawnTest::OnHealthPickup); + EVENT_SUBSCRIBE_MEMBER(m_PS, &PickupSpawnTest::OnPickupSpawned); +} + +bool PickupSpawnTest::OnHealthPickup(Events::PlayerHealthPickup& e) { + switch (m_RunTestNumber) + { + case 1: + //verify that the event has the correct healthgain number and playerid + if (e.HealthAmount == 22.0 && e.Player.ID == m_PlayerID) { + m_TestStage1Success = true; + } + break; + case 2: + m_TestStage1Success = false; + break; + case 3: + //verify that the event has the correct healthgain number and playerid + if (e.HealthAmount == 50.0 && e.Player.ID == m_PlayerID) { + m_TestStage1Success = true; + } + break; + } + return true; +} +bool PickupSpawnTest::OnPickupSpawned(Events::PickupSpawned& e) { + switch (m_RunTestNumber) + { + case 1: + //verify that the newly spawned pickup has the same variable values as the original one + if ((double)e.Pickup["HealthPickup"]["HealthGain"] == 22.0 && (double)e.Pickup["HealthPickup"]["RespawnTimer"] == 2.0) { + m_TestStage2Success = true; + } + break; + case 2: + m_TestStage2Success = false; + break; + case 3: + m_TestStage2Success = false; + break; + } + return true; +} + +void PickupSpawnTest::TestSetup(int testNumber) +{ + switch (m_RunTestNumber) + { + case 1: + { + //PickupSpawnTest_HealthPickupRespawns_PlayerHealthPickupEventTriggers + auto& healthPickupEW = EntityWrapper(m_World, m_HealthPickupID); + healthPickupEW["HealthPickup"]["RespawnTimer"] = 2.0; + healthPickupEW["HealthPickup"]["HealthGain"] = 22.0; + + //create a player + auto& health = m_World->AttachComponent(m_PlayerID, "Health"); + health["Health"] = 20.0; + health["MaxHealth"] = 100.0; + } + break; + case 2: + { + //PickupSpawnTest_APlayerAtMaxHealth_CantTakeHealthPickup + auto& healthPickupEW = EntityWrapper(m_World, m_HealthPickupID); + healthPickupEW["HealthPickup"]["RespawnTimer"] = 1.0; + healthPickupEW["HealthPickup"]["HealthGain"] = 50.0; + + //create a player at max health + auto& health = m_World->AttachComponent(m_PlayerID, "Health"); + health["Health"] = 100.0; + health["MaxHealth"] = 100.0; + } + break; + case 3: + { + //PickupSpawnTest_APickupCanRespawnSlowly + auto& healthPickupEW = EntityWrapper(m_World, m_HealthPickupID); + healthPickupEW["HealthPickup"]["RespawnTimer"] = 100.0; + healthPickupEW["HealthPickup"]["HealthGain"] = 50.0; + + //create a player + auto& health = m_World->AttachComponent(m_PlayerID, "Health"); + health["Health"] = 1.0; + health["MaxHealth"] = 100.0; + } + break; + default: + break; + } + //do the triggerTouch event to get the pickupSpawnTest started + Events::TriggerTouch eTriggerTouch; + DoTouchEvent(m_PlayerID, m_HealthPickupID); +} + +//generic stuff +void PickupSpawnTest::Tick() +{ + glfwPollEvents(); + + //just set dt to 1.0 since we want fast testing + double dt = 1.0; + + // Iterate through systems and update world! + m_SystemPipeline->Update(dt); + + m_EventBroker->Swap(); + m_EventBroker->Clear(); + + //verify that healthgain event has been published and pickup has respawned + if (m_RunTestNumber == 1 && m_TestStage1Success && m_TestStage2Success) { + m_TestSucceeded = true; + } + //verify that no healthgain event has been published and that no pickup has respawned + if (m_NumLoops > 90 && m_RunTestNumber == 2 && !m_TestStage1Success && !m_TestStage2Success) { + m_TestSucceeded = true; + } + //3: verify that the pickup hasnt spawned + if (m_NumLoops > 90 && m_RunTestNumber == 3 && m_TestStage1Success && !m_TestStage2Success) { + m_TestSucceeded = true; + } +} +bool PickupSpawnTest::Game_Loop_OneHundredTimes() { + //100 loops will be more than enough to do the test + int loops = 100; + bool success = false; + while (loops > 0) { + Tick(); + m_NumLoops++; + if (m_TestSucceeded) { + success = true; + break; + } + loops--; + } + return success; +} +PickupSpawnTest::~PickupSpawnTest() +{ + delete m_SystemPipeline; + delete m_World; +} +void PickupSpawnTest::DoTouchEvent(EntityID whoDidSomething, EntityID onWhatObject) { + Events::TriggerTouch touchEvent; + touchEvent.Entity = EntityWrapper(m_World, whoDidSomething); + touchEvent.Trigger = EntityWrapper(m_World, onWhatObject); + m_EventBroker->Publish(touchEvent); +} diff --git a/src/Tests/PickupSpawnTest.h b/src/Tests/PickupSpawnTest.h new file mode 100644 index 00000000..b9d5483a --- /dev/null +++ b/src/Tests/PickupSpawnTest.h @@ -0,0 +1,72 @@ +#ifndef PickupSpawnTest_h__ +#define PickupSpawnTest_h__ + +#include "Core/ResourceManager.h" +#include "Core/ConfigFile.h" +#include "Core/EventBroker.h" +#include "Core/World.h" +#include "Input/InputProxy.h" +#include "Input/KeyboardInputHandler.h" +#include "Input/MouseInputHandler.h" +#include "Core/EKeyDown.h" +#include "Core/EntityFile.h" +#include "Core/SystemPipeline.h" + +#include "Core/EntityFilePreprocessor.h" +#include "Core/EntityFileParser.h" +#include "Core/EntityFileWriter.h" + +#include "Engine/Collision/ETrigger.h" + +//#include "Core/System.h" +//#include "Core/Transform.h" +//#include "Core/ResourceManager.h" +//#include "Core/EntityFileParser.h" +//#include "Core/EPickupSpawned.h" +//#include "Core/EPlayerHealthPickup.h" +#include "Engine/Collision/ETrigger.h" +//#include "Common.h" +//#include +#include "Collision/TriggerSystem.h" +#include "Collision/CollisionSystem.h" +#include "Core/EntityFileWriter.h" +#include "Game/Systems/HealthSystem.h" +#include "Game/Systems/PickupSpawnSystem.h" + +#include "Core/ResourceManager.h" + +class PickupSpawnTest +{ +public: + PickupSpawnTest(int runTestNumber); + ~PickupSpawnTest(); + + void Tick(); + + bool Game_Loop_OneHundredTimes(); + void TestSetup(int testNumber); + void DoTouchEvent(EntityID whoDidSomething, EntityID onWhatObject); + +private: + double m_LastTime; + ConfigFile* m_Config = nullptr; + EventBroker* m_EventBroker; + World* m_World; + SystemPipeline* m_SystemPipeline; + EntityID m_PlayerID, m_HealthPickupID; + int m_RunTestNumber; + + EventRelay m_HP; + bool OnHealthPickup(Events::PlayerHealthPickup& e); + EventRelay m_PS; + bool OnPickupSpawned(Events::PickupSpawned& e); + + bool m_TestStage1Success = false; + bool m_TestStage2Success = false; + + bool m_TestSucceeded = false; + int m_NumLoops = 0; + +}; + +#endif diff --git a/src/Tests/WorldTest.cpp b/src/Tests/WorldTest.cpp index 03008562..633e617a 100644 --- a/src/Tests/WorldTest.cpp +++ b/src/Tests/WorldTest.cpp @@ -69,3 +69,47 @@ BOOST_AUTO_TEST_CASE(WorldTestMultipleAllocations, * utf::tolerance(0.00001)) i++; } } + +BOOST_AUTO_TEST_CASE(WorldCopy, *utf::tolerance(0.00001)) +{ + World w1; + + // Create a test component + auto testComponent = ComponentWrapperFactory("Test", 2); + testComponent.AddProperty("TestInteger", 1337); + testComponent.AddProperty("TestDouble", 13.37); + testComponent.AddProperty("TestString", std::string("DefaultString")); + testComponent.AddProperty("TestVec3", glm::vec3(1.f, 2.f, 3.f)); + w1.RegisterComponent(testComponent); + + // Create a test entity + EntityID w1_e1 = w1.CreateEntity(); + auto w1_c1 = w1.AttachComponent(w1_e1, "Test"); + + // Create a child + EntityID w1_e2 = w1.CreateEntity(w1_e1); + auto w1_c2 = w1.AttachComponent(w1_e2, "Test"); + w1_c2["TestString"] = "NonDefaultString"; + + // Copy the world! + World w2 = w1; + + // Fetch the components + auto w2_c1 = w2.GetComponent(w1_e1, "Test"); + auto w2_c2 = w2.GetComponent(w1_e2, "Test"); + + // Check that built-in types are copied but don't reside in the same memory + BOOST_CHECK((int)w1_c1["TestInteger"] == (int)w2_c1["TestInteger"]); + BOOST_CHECK(&(int&)w1_c1["TestInteger"] != &(int&)w2_c1["TestInteger"]); + BOOST_CHECK((double)w1_c1["TestDouble"] == (double)w2_c1["TestDouble"]); + BOOST_CHECK(&(int&)w1_c1["TestDouble"] != &(int&)w2_c1["TestDouble"]); + BOOST_CHECK((int)w1_c2["TestInteger"] == (int)w2_c2["TestInteger"]); + BOOST_CHECK(&(int&)w1_c2["TestInteger"] != &(int&)w2_c2["TestInteger"]); + BOOST_CHECK((double)w1_c2["TestDouble"] == (double)w2_c2["TestDouble"]); + BOOST_CHECK(&(int&)w1_c2["TestDouble"] != &(int&)w2_c2["TestDouble"]); + // Check that specially handled strings are fine + BOOST_CHECK((std::string)w1_c1["TestString"] == (std::string)w2_c1["TestString"]); + BOOST_CHECK(&(std::string&)w1_c1["TestString"] != &(std::string&)w2_c1["TestString"]); + BOOST_CHECK((std::string)w1_c2["TestString"] == (std::string)w2_c2["TestString"]); + BOOST_CHECK(&(std::string&)w1_c2["TestString"] != &(std::string&)w2_c2["TestString"]); +}