diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 624fbca0..c4292907 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -78,8 +78,6 @@ bool AABBVsAABB(const AABB& a, const AABB& b); //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); -//Attaches an AABB which contains all vertices in the entitys Model. -bool AttachAABBComponentFromModel(EntityWrapper entity); // Calculates an absolute AABB from an entity AABB component boost::optional EntityAbsoluteAABB(EntityWrapper& entity); diff --git a/include/Engine/Collision/FillFrustumOctreeSystem.h b/include/Engine/Collision/FillFrustumOctreeSystem.h new file mode 100644 index 00000000..f7f1413c --- /dev/null +++ b/include/Engine/Collision/FillFrustumOctreeSystem.h @@ -0,0 +1,25 @@ +#ifndef FillFrustumOctreeSystem_h__ +#define FillFrustumOctreeSystem_h__ + +#include "../Core/System.h" +#include "../Core/Octree.h" +#include "Collision.h" +#include "EntityAABB.h" + +class FillFrustumOctreeSystem : public ImpureSystem, public PureSystem +{ +public: + FillFrustumOctreeSystem(World* world, EventBroker* eventBroker, Octree* octree) + : System(world, eventBroker) + , PureSystem("Model") + , m_Octree(octree) + { } + + virtual void Update(double dt) override; + virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) override; + +private: + Octree* m_Octree; +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Collision/FillOctreeSystem.h b/include/Engine/Collision/FillOctreeSystem.h index 9fd87b94..3bacccac 100644 --- a/include/Engine/Collision/FillOctreeSystem.h +++ b/include/Engine/Collision/FillOctreeSystem.h @@ -1,5 +1,5 @@ -#ifndef CollidableOctreeSystem_h__ -#define CollidableOctreeSystem_h__ +#ifndef FillOctreeSystem_h__ +#define FillOctreeSystem_h__ #include "../Core/System.h" #include "../Core/Octree.h" diff --git a/include/Engine/Core/Frustum.h b/include/Engine/Core/Frustum.h new file mode 100644 index 00000000..c2d83b23 --- /dev/null +++ b/include/Engine/Core/Frustum.h @@ -0,0 +1,77 @@ +#ifndef Frustum_h__ +#define Frustum_h__ + +#include "../GLM.h" +#include "AABB.h" +#include + +//A frustum defined by 6 planes. +struct Frustum +{ + //Contains points P in: dot(normal, P) + d = 0 + struct Plane + { + glm::vec3 Normal; + float Distance; + }; + + enum class Output + { + Inside, + Outside, + Intersects + }; + Plane Planes[6]; + + Frustum() = default; + Frustum(glm::mat4x4 viewProjMatrix) + { + //Order: Right, left, top, bottom, far, near. + int sign = 1; + for (int i = 0; i < 6; ++i) { + sign = -sign; + int index = i / 2; + Plane& plane = Planes[i]; + plane.Normal.x = viewProjMatrix[0].w + sign * viewProjMatrix[0][index]; + plane.Normal.y = viewProjMatrix[1].w + sign * viewProjMatrix[1][index]; + plane.Normal.z = viewProjMatrix[2].w + sign * viewProjMatrix[2][index]; + plane.Distance = viewProjMatrix[3].w + sign * viewProjMatrix[3][index]; + float divByNormalLength = 1.0f / glm::length(plane.Normal); + plane.Normal *= divByNormalLength; + plane.Distance *= divByNormalLength; + } + } + + Output VsAABB(const AABB& box) const + { + const glm::vec3& maxCorner = box.MaxCorner(); + const glm::vec3& minCorner = box.MinCorner(); + bool completelyInside = true; + for (const Plane& p : Planes) { + bool anyWasInside = false; + bool anyWasOutside = false; + //If points are on both sides of the plane, we can stop. + for (int i = 0; i < 8 && (!anyWasInside || !anyWasOutside); ++i) { + std::bitset<3> bits(i); + glm::vec3 corner; + corner.x = bits.test(0) ? maxCorner.x : minCorner.x; + corner.y = bits.test(1) ? maxCorner.y : minCorner.y; + corner.z = bits.test(2) ? maxCorner.z : minCorner.z; + if (glm::dot(p.Normal, corner) > -p.Distance) { + anyWasInside = true; + } else { + anyWasOutside = true; + } + } + if (!anyWasInside) { + return Output::Outside; + } + if (anyWasOutside) { + completelyInside = false; + } + } + return completelyInside ? Output::Inside : Output::Intersects; + } +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Core/Octree.h b/include/Engine/Core/Octree.h index 907d5736..6bdea4d3 100644 --- a/include/Engine/Core/Octree.h +++ b/include/Engine/Core/Octree.h @@ -5,6 +5,7 @@ #include "../Common.h" #include "AABB.h" +#include "Frustum.h" //Fwd declarations. class Ray; @@ -40,6 +41,8 @@ public: //The type Box must be AABB, or inherit from AABB. template void ObjectsInSameRegion(const Box& box, std::vector& outObjects); + //Get the objects that are inside the frustum, the objects are put in outObjects. + void ObjectsInFrustum(const Frustum& frustum, std::vector& outObjects); //Empty the tree of all objects, static and dynamic. void ClearObjects(); //Empty the tree of all dynamic objects. Static objects remain in the tree. @@ -97,6 +100,8 @@ struct Child void AddStaticObject(const AABB& box); template void ObjectsInSameRegion(const Box& box, std::vector& outObjects) const; + template + void ObjectsInFrustum(const Frustum& frustum, std::vector& outObjects, bool takeAllDontTest) const; void ClearObjects(); void ClearDynamicObjects(); bool RayCollides(const Ray& ray, Output& data) const; @@ -154,6 +159,13 @@ void Octree::ObjectsInSameRegion(const Box& box, std::vector& outObjects) m_Root->ObjectsInSameRegion(box, outObjects); } +template +void Octree::ObjectsInFrustum(const Frustum& frustum, std::vector& outObjects) +{ + falsifyObjectChecks(); + m_Root->ObjectsInFrustum(frustum, outObjects, false); +} + template void Octree::ClearObjects() { @@ -230,4 +242,46 @@ void OctSpace::Child::ObjectsInSameRegion(const Box& box, std::vector& outObj } } +template +void OctSpace::Child::ObjectsInFrustum(const Frustum& frustum, std::vector& outObjects, bool takeAllDontTest) const +{ + if (hasChildren()) { + for (const Child* c : m_Children) { + Frustum::Output out = Frustum::Output::Inside; + if (!takeAllDontTest) { + out = frustum.VsAABB(c->m_Box); + if (out == Frustum::Output::Outside) { + continue; + } + } + c->ObjectsInFrustum(frustum, outObjects, out == Frustum::Output::Inside); + } + } else { + size_t startIndex = outObjects.size(); + int numDuplicates = 0; + outObjects.resize(outObjects.size() + m_StaticObjIndices.size() + m_DynamicObjIndices.size()); + for (size_t i = 0; i < m_StaticObjIndices.size(); ++i) { + ContainedObject& obj = m_StaticObjectsRef[m_StaticObjIndices[i]]; + if (obj.Checked || frustum.VsAABB(*obj.Box) == Frustum::Output::Outside) { + ++numDuplicates; + } else { + obj.Checked = true; + outObjects[startIndex + i - numDuplicates] = *static_cast(obj.Box.get()); + } + } + for (size_t i = 0; i < m_DynamicObjIndices.size(); ++i) { + ContainedObject& obj = m_DynamicObjectsRef[m_DynamicObjIndices[i]]; + if (obj.Checked || frustum.VsAABB(*obj.Box) == Frustum::Output::Outside) { + ++numDuplicates; + } else { + obj.Checked = true; + outObjects[startIndex + i - numDuplicates] = *static_cast(obj.Box.get()); + } + } + for (size_t i = 0; i < numDuplicates; ++i) { + outObjects.pop_back(); + } + } +} + #endif \ No newline at end of file diff --git a/include/Engine/Input/FirstPersonInputController.h b/include/Engine/Input/FirstPersonInputController.h index 2bbd768d..f3b0a17a 100644 --- a/include/Engine/Input/FirstPersonInputController.h +++ b/include/Engine/Input/FirstPersonInputController.h @@ -4,6 +4,7 @@ #include "../GLM.h" #include "../Core/InputController.h" #include "../Core/ELockMouse.h" +#include "../Game/Events/EDashAbility.h" #include "InputHandler.h" template @@ -230,6 +231,9 @@ void FirstPersonInputController::AssaultDashCheck(double dt, bool m_AssaultDashDoubleTapped = true; m_AssaultDashDoubleTapDeltaTime = 0.f; m_AssaultDashCoolDownTimer = assaultDashCoolDownMaxTimer; + + Events::DashAbility e; + m_EventBroker->Publish(e); } #endif \ No newline at end of file diff --git a/include/Engine/Rendering/Model.h b/include/Engine/Rendering/Model.h index 024c97c8..30706cc5 100644 --- a/include/Engine/Rendering/Model.h +++ b/include/Engine/Rendering/Model.h @@ -4,6 +4,7 @@ #include "Rendering/RawModelCustom.h" //#include "Rendering/RawModelAssimp.h" #include "../OpenGL.h" +#include "Core/AABB.h" class Model : public ThreadUnsafeResource { @@ -18,13 +19,15 @@ public: const glm::mat4& Matrix() const { return m_RawModel->m_Matrix; } const RawModel::Vertex* Vertices() const { return m_RawModel->Vertices(); } unsigned int NumberOfVertices() const { return m_RawModel->NumVertices(); } + const AABB& Box() const { return m_Box; } bool isSkined() const { return m_RawModel->isSkined(); } GLuint VAO; GLuint ElementBuffer; RawModel* m_RawModel; private: - + AABB m_Box; + GLuint VertexBuffer; GLuint NormalBuffer; GLuint TangentNormalsBuffer; diff --git a/include/Engine/Rendering/RenderSystem.h b/include/Engine/Rendering/RenderSystem.h index d12ca647..4a313087 100644 --- a/include/Engine/Rendering/RenderSystem.h +++ b/include/Engine/Rendering/RenderSystem.h @@ -16,11 +16,13 @@ #include "PointLightJob.h" #include "../Core/Transform.h" #include "../Core/EPlayerSpawned.h" +#include "../Core/Octree.h" +#include "../Collision/EntityAABB.h" class RenderSystem : public ImpureSystem { public: - RenderSystem(World* world, EventBroker* eventBrokerer, const IRenderer* renderer, RenderFrame* renderFrame); + RenderSystem(World* world, EventBroker* eventBrokerer, const IRenderer* renderer, RenderFrame* renderFrame, Octree* frustumCullOctree); ~RenderSystem(); virtual void Update(double dt) override; @@ -32,6 +34,7 @@ private: World* m_World; EntityWrapper m_CurrentCamera = EntityWrapper::Invalid; EntityWrapper m_LocalPlayer = EntityWrapper::Invalid; + Octree* m_Octree; EventRelay m_ESetCamera; bool OnSetCamera(Events::SetCamera &event); diff --git a/include/Engine/Sound/EPlayQueueOnEntity.h b/include/Engine/Sound/EPlayQueueOnEntity.h new file mode 100644 index 00000000..e880819a --- /dev/null +++ b/include/Engine/Sound/EPlayQueueOnEntity.h @@ -0,0 +1,18 @@ +#ifndef Events_PlayQueueOnEntity_h__ +#define Events_PlayQueueOnEntity_h__ + +#include "../Core/Event.h" +#include "../Core/EntityWrapper.h" + +namespace Events +{ + +struct PlayQueueOnEntity : public Event +{ + EntityWrapper Emitter; + std::vector FilePaths; +}; + +} + +#endif diff --git a/include/Engine/Sound/Sound.h b/include/Engine/Sound/Sound.h index 6b2aac04..cccbdf07 100644 --- a/include/Engine/Sound/Sound.h +++ b/include/Engine/Sound/Sound.h @@ -1,6 +1,9 @@ #ifndef Sound_h__ #define Sound_h__ +#include +#include + #include "Core/ResourceManager.h" class Sound : public Resource diff --git a/include/Engine/Sound/SoundSystem.h b/include/Engine/Sound/SoundManager.h similarity index 57% rename from include/Engine/Sound/SoundSystem.h rename to include/Engine/Sound/SoundManager.h index b9cc2589..5b027c62 100644 --- a/include/Engine/Sound/SoundSystem.h +++ b/include/Engine/Sound/SoundManager.h @@ -1,17 +1,23 @@ -#ifndef SoundSystem_h__ -#define SoundSystem_h__ +#ifndef SoundManager_h__ +#define SoundManager_h__ #include +#include #include "glm/common.hpp" #include "glm/gtx/rotate_vector.hpp" // Calculate Up vector #include "OpenAL/al.h" #include "OpenAL/alc.h" +#include "imgui/imgui.h" + #include "Core/World.h" #include "Core/EventBroker.h" +#include "../Engine/Core/ResourceManager.h" +#include "../Engine/Core/ConfigFile.h" #include "Core/Transform.h" // Absolute transform #include "Sound/Sound.h" +#include "../Engine/Sound/EPlayQueueOnEntity.h" #include "Sound/EPlaySoundOnEntity.h" #include "Sound/EPlaySoundOnPosition.h" #include "Sound/EPlayBackgroundMusic.h" @@ -20,6 +26,11 @@ #include "Sound/EStopSound.h" #include "Sound/ESetBGMGain.h" #include "Sound/ESetSFXGain.h" +#include "Core/EPause.h" +#include "Core/EComponentAttached.h" +#include "../Core/EPlayerSpawned.h" + +typedef std::pair> QueuedBuffers; enum class SoundType { SFX, @@ -34,14 +45,15 @@ struct Source SoundType Type; }; -class SoundSystem +class SoundManager { public: - SoundSystem() { } - SoundSystem(World* world, EventBroker* eventBroker, bool editorMode); - ~SoundSystem(); + SoundManager() { } + SoundManager(World* world, EventBroker* eventBroker); + ~SoundManager(); // Update emitters / listener void Update(double dt); + private: // Help functions for working with OpenaAL void setListenerPos(glm::vec3 pos) { alListener3f(AL_POSITION, pos.x, pos.y, pos.z); }; @@ -56,46 +68,62 @@ private: // Logic void initOpenAL(); void updateEmitters(double dt); - void updateListener(double dt); void deleteInactiveEmitters(); - void addNewEmitters(double dt); - Source* createSource(std::string filePath); - void playSound(Source* source); - void stopSound(Source* source); void stopEmitters(); + void updateListener(double dt); ALenum getSourceState(ALuint source); void setGain(Source* source, float gain); - void setSoundProperties(ALuint source, ComponentWrapper* soundComponent); + void setSoundProperties(Source* source, ComponentWrapper* soundComponent); + + // Specific logic + void playSound(Source* source); + // Need to be the same format (sample rate etc) + void playQueue(QueuedBuffers qb); + void stopSound(Source* source); + Source* createSource(std::string filePath); + std::unordered_map m_Sources; + + // Logic + World* m_World = nullptr; + EventBroker* m_EventBroker = nullptr; // OpenAL system variables ALCdevice* m_ALCdevice = nullptr; ALCcontext* m_ALCcontext = nullptr; - // Logic - World* m_World = nullptr; - EventBroker* m_EventBroker = nullptr; - std::unordered_map m_Sources; float m_BGMVolumeChannel = 1.0f; - float m_SFXVolumeChannel = 1.f; - bool m_EditorEnabled = false; - + float m_SFXVolumeChannel = 1.0f; + EntityWrapper m_LocalPlayer = EntityWrapper(); + // Events - EventRelay m_EPlaySoundOnEntity; + EventRelay m_EPlaySoundOnEntity; bool OnPlaySoundOnEntity(const Events::PlaySoundOnEntity &e); - EventRelay m_EPlaySoundOnPosition; + EventRelay m_EPlaySoundOnPosition; bool OnPlaySoundOnPosition(const Events::PlaySoundOnPosition &e); - EventRelay m_EPlayBackgroundMusic; + EventRelay m_EPlayBackgroundMusic; bool OnPlayBackgroundMusic(const Events::PlayBackgroundMusic &e); - EventRelay m_EPauseSound; + EventRelay m_EPauseSound; bool OnPauseSound(const Events::PauseSound &e); - EventRelay m_EStopSound; + EventRelay m_EStopSound; bool OnStopSound(const Events::StopSound &e); - EventRelay m_EContinueSound; + EventRelay m_EContinueSound; bool OnContinueSound(const Events::ContinueSound &e); - EventRelay m_ESetBGMGain; - bool OnSetBGMGain(const Events::SetBGMGain &e); // Not tested - EventRelay m_ESetSFXGain; - bool OnSetSFXGain(const Events::SetSFXGain &e); // Not tested + EventRelay m_ESetBGMGain; + bool OnSetBGMGain(const Events::SetBGMGain &e); + EventRelay m_ESetSFXGain; + bool OnSetSFXGain(const Events::SetSFXGain &e); + EventRelay m_EComponentAttached; + bool OnComponentAttached(const Events::ComponentAttached &e); + EventRelay m_EPause; + bool OnPause(const Events::Pause &e); + EventRelay m_EResume; + bool OnResume(const Events::Resume &e); + EventRelay m_EPlayerSpawned; + bool OnPlayerSpawned(const Events::PlayerSpawned &e); + EventRelay m_EPlayQueueOnEntity; + bool OnPlayQueueOnEntity(const Events::PlayQueueOnEntity &e); + + }; #endif \ No newline at end of file diff --git a/include/Game/Events/EDashAbility.h b/include/Game/Events/EDashAbility.h new file mode 100644 index 00000000..62a2b935 --- /dev/null +++ b/include/Game/Events/EDashAbility.h @@ -0,0 +1,13 @@ +#ifndef Events_DashAbility_h__ +#define Events_DashAbility_h__ + +#include "Core/Event.h" + +namespace Events +{ + +struct DashAbility : public Event { }; + +} + +#endif \ No newline at end of file diff --git a/include/Game/Events/EDoubleJump.h b/include/Game/Events/EDoubleJump.h new file mode 100644 index 00000000..767d5b39 --- /dev/null +++ b/include/Game/Events/EDoubleJump.h @@ -0,0 +1,16 @@ +#ifndef Events_DoubleJump_h__ +#define Events_DoubleJump_h__ + +#include "Core/Event.h" + +namespace Events +{ + +struct DoubleJump : public Event +{ + +}; + +} + +#endif diff --git a/include/Game/Game.h b/include/Game/Game.h index 37267a68..6177a818 100644 --- a/include/Game/Game.h +++ b/include/Game/Game.h @@ -30,7 +30,8 @@ #include "Network/Client.h" // Sound -#include "Sound/SoundSystem.h" +#include "Sound/SoundManager.h" +#include "Systems/SoundSystem.h" class Game { @@ -64,7 +65,7 @@ private: bool m_IsClientOrServer = false; // Sound - SoundSystem* m_SoundSystem; + SoundManager* m_SoundManager; //EventRelay m_EInputCommand; //bool debugOnInputCommand(const Events::InputCommand& e); diff --git a/include/Game/Systems/LifetimeSystem.h b/include/Game/Systems/LifetimeSystem.h index da88cfa2..99cafd89 100644 --- a/include/Game/Systems/LifetimeSystem.h +++ b/include/Game/Systems/LifetimeSystem.h @@ -9,7 +9,9 @@ public: LifetimeSystem(World* world, EventBroker* eventBroker) : System(world, eventBroker) , PureSystem("Lifetime") - { } + { + LOG_INFO("ASDASDASSA"); + } virtual void Update(double dt) override; virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cLifetime, double dt) override; diff --git a/include/Game/Systems/PlayerMovementSystem.h b/include/Game/Systems/PlayerMovementSystem.h index 34862e90..defaa8ae 100644 --- a/include/Game/Systems/PlayerMovementSystem.h +++ b/include/Game/Systems/PlayerMovementSystem.h @@ -4,6 +4,8 @@ #include "Core/EPlayerSpawned.h" #include "Input/FirstPersonInputController.h" #include +#include "Events/EDoubleJump.h" +#include "../Engine/Sound/EPlaySoundOnEntity.h" class PlayerMovementSystem : public ImpureSystem, PureSystem { @@ -18,6 +20,19 @@ private: // State std::unordered_map*> m_PlayerInputControllers; + EntityWrapper m_LocalPlayer = EntityWrapper::Invalid; + // Walking logic + // Keeps track of how far the player has walked within this "key press session". + float m_DistanceMoved = 0.0f; + // How far a step is (How often the step sound will be played). + const float m_PlayerStepLength = 1.75f; + // Determine what sound file to play. + bool m_LeftFoot = false; + // To get a difference when calculating the walking state. + glm::vec3 m_LastPosition = glm::vec3(); + // The logic for making the sound play when player is moving + void playerStep(double dt); + EventRelay m_EPlayerSpawned; bool OnPlayerSpawned(Events::PlayerSpawned& e); diff --git a/include/Game/Systems/SoundSystem.h b/include/Game/Systems/SoundSystem.h new file mode 100644 index 00000000..09582826 --- /dev/null +++ b/include/Game/Systems/SoundSystem.h @@ -0,0 +1,71 @@ +#ifndef Systems_SoundSystem_h__ +#define Systems_SoundSystem_h__ + +#include + +#include "../Engine/Core/System.h" +#include "../Engine/Core/ResourceManager.h" +#include "../Engine/Core/ConfigFile.h" +#include "../Engine/Sound/Sound.h" +#include "../Engine/Sound/EPlayQueueOnEntity.h" +#include "../Engine/Core/EPlayerSpawned.h" +#include "../Engine/Input/EInputCommand.h" +#include "../Engine/Core/EShoot.h" +#include "../Engine/Core/EPlayerSpawned.h" +#include "../Engine/Input/EInputCommand.h" +#include "../Engine/Core/ECaptured.h" +#include "../Engine/Core/EPlayerDamage.h" +#include "../Engine/Core/EPlayerDeath.h" +#include "../Engine/Core/EPlayerHealthPickup.h" +#include "../Engine/Collision/ETrigger.h" +#include "../Engine/Sound/EPlaySoundOnEntity.h" +#include "../Engine/Sound/EPlayBackgroundMusic.h" +#include "../Game/Events/EDoubleJump.h" +#include "../Game/Events/EDashAbility.h" + + +class SoundSystem : public PureSystem, ImpureSystem +{ +public: + SoundSystem(World* world, EventBroker* eventbroker); + virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cComponent, double dt) override; + virtual void Update(double dt) override; +private: + EntityWrapper m_LocalPlayer = EntityWrapper(); + + World* m_World = nullptr; + EventBroker* m_EventBroker = nullptr; + std::string m_Announcer = ""; + // Logic for playing a sound when a player jumps + void playerJumps(); + + // Temporary solution for play test. + bool m_DrumsIsPlaying = false; + double m_DrumTimer = 0.0; + bool drumTimer(double dt); + + std::default_random_engine generator; + + EventRelay m_EPlayerSpawned; + bool OnPlayerSpawned(const Events::PlayerSpawned &e); + EventRelay m_InputCommand; + bool OnInputCommand(const Events::InputCommand &e); + EventRelay m_EDoubleJump; + bool OnDoubleJump(const Events::DoubleJump &e); + EventRelay m_EDashAbility; + bool OnDashAbility(const Events::DashAbility &e); + EventRelay m_ETriggerTouch; + bool OnTriggerTouch(const Events::TriggerTouch &e); + EventRelay m_EShoot; + bool OnShoot(const Events::Shoot &e); + EventRelay m_ECaptured; + bool OnCaptured(const Events::Captured &e); + EventRelay m_EPlayerDamage; + bool OnPlayerDamage(const Events::PlayerDamage &e); + EventRelay m_EPlayerDeath; + bool OnPlayerDeath(const Events::PlayerDeath &e); + EventRelay m_EPlayerHealthPickup; + bool OnPlayerHealthPickup(const Events::PlayerHealthPickup &e); +}; + +#endif diff --git a/resources/DefaultConfig.ini b/resources/DefaultConfig.ini index 86dc735c..12ec06c8 100644 --- a/resources/DefaultConfig.ini +++ b/resources/DefaultConfig.ini @@ -29,3 +29,8 @@ TimeoutMs=15000 [Multithreading] ResourceLoading=true + +[Sound] +BGMVolume=1.0 +SFXVolume=1.0 +Announcer=female \ No newline at end of file diff --git a/resources/Schema/Entities/aim_rays.xml b/resources/Schema/Entities/aim_rays.xml new file mode 100644 index 00000000..c8dac9a3 --- /dev/null +++ b/resources/Schema/Entities/aim_rays.xml @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + models/core/unitcube.mesh + + + + + + + + + + + + + + + models/core/unitcube.mesh + + + + + + + + + + + + + + + + + + + + + + + + models/core/unitcube.mesh + + + + + + + + + + + + + + + models/core/unitcube.mesh + + + + + + + + + + + + + + + models/core/unitcube.mesh + + + + + + + + + + + + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + models/core/unitcube.mesh + + + + + + + + + + + + + + + + + + Models\Core\UnitCube.mesh + + + + + + + + + + + + + + + + + + + + + + + 1 + + + Models\Core\UnitCube.mesh + + + + + + + + + + + + + + + + + + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 34b7e553..249669b9 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -1,4 +1,5 @@ #include +#include #include "Collision/Collision.h" #include "Engine/GLM.h" @@ -564,47 +565,50 @@ bool AABBvsTriangles(const AABB& box, return hit; } -bool AttachAABBComponentFromModel(EntityWrapper entity) -{ - if (!entity.HasComponent("Model")) { - return false; - } - //Derive AABB from model - RawModel* model; - try { - model = ResourceManager::Load(entity["Model"]["Resource"]); - } catch (const std::exception&) { - return false; - } - - glm::vec3 mini(INFINITY); - glm::vec3 maxi(-INFINITY); - for (unsigned int i = 0; i < model->NumVertices(); i++) { - const auto& v = model->Vertices()[i]; - mini = glm::min(mini, v.Position); - maxi = glm::max(maxi, v.Position); - } - entity.AttachComponent("AABB"); - entity["AABB"]["Origin"] = 0.5f * (maxi + mini); - entity["AABB"]["Size"] = maxi - mini; - return true; -} - boost::optional EntityAbsoluteAABB(EntityWrapper& entity) { - if (!entity.HasComponent("AABB")) { + AABB modelSpaceBox; + if (entity.HasComponent("AABB")) { + ComponentWrapper& cAABB = entity["AABB"]; + modelSpaceBox = EntityAABB::FromOriginSize((glm::vec3)cAABB["Origin"], (glm::vec3)cAABB["Size"]); + } else if (entity.HasComponent("Model")) { + Model* model; + std::string res = entity["Model"]["Resource"]; + if (res.empty()) { + return boost::none; + } + try { + model = ResourceManager::Load<::Model, true>(res); + } catch (const Resource::StillLoadingException&) { + return boost::none; + } catch (const std::exception&) { + return boost::none; + } + modelSpaceBox = model->Box(); + } else { return boost::none; } - ComponentWrapper& cAABB = entity["AABB"]; - glm::vec3 absPosition = Transform::AbsolutePosition(entity.World, entity.ID); - glm::vec3 absScale = Transform::AbsoluteScale(entity.World, entity.ID); - glm::vec3 origin = absPosition + (glm::vec3)cAABB["Origin"]; - glm::vec3 size = (glm::vec3)cAABB["Size"] * absScale; + glm::mat4 modelMat = Transform::AbsoluteTransformation(entity); + glm::vec3 mini(INFINITY); + glm::vec3 maxi(-INFINITY); + glm::vec3 maxCorner = modelSpaceBox.MaxCorner(); + glm::vec3 minCorner = modelSpaceBox.MinCorner(); + for (int i = 0; i < 8; ++i) { + std::bitset<3> bits(i); + glm::vec3 corner; + corner.x = bits.test(0) ? maxCorner.x : minCorner.x; + corner.y = bits.test(1) ? maxCorner.y : minCorner.y; + corner.z = bits.test(2) ? maxCorner.z : minCorner.z; + corner = Transform::TransformPoint(corner, modelMat); + mini = glm::min(mini, corner); + maxi = glm::max(maxi, corner); + } + + EntityAABB aabb; + aabb = AABB(mini, maxi); - EntityAABB aabb = EntityAABB::FromOriginSize(origin, size); aabb.Entity = entity; - return aabb; } diff --git a/src/Engine/Collision/FillFrustumOctreeSystem.cpp b/src/Engine/Collision/FillFrustumOctreeSystem.cpp new file mode 100644 index 00000000..f02a30f1 --- /dev/null +++ b/src/Engine/Collision/FillFrustumOctreeSystem.cpp @@ -0,0 +1,21 @@ +#include "Collision/FillFrustumOctreeSystem.h" + +void FillFrustumOctreeSystem::Update(double dt) +{ + m_Octree->ClearDynamicObjects(); +} + +void FillFrustumOctreeSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) +{ + if (entity.HasComponent("ExplosionEffect")) { + //TODO: Fix hack, get real box by using shader equation. + EntityAABB aabb = AABB(glm::vec3(-300), glm::vec3(300)); + aabb.Entity = entity; + m_Octree->AddDynamicObject(aabb); + } else { + boost::optional absoluteAABB = Collision::EntityAbsoluteAABB(entity); + if (absoluteAABB) { + m_Octree->AddDynamicObject(*absoluteAABB); + } + } +} \ No newline at end of file diff --git a/src/Engine/Collision/FillOctreeSystem.cpp b/src/Engine/Collision/FillOctreeSystem.cpp index 8c03b5c2..a727eb86 100644 --- a/src/Engine/Collision/FillOctreeSystem.cpp +++ b/src/Engine/Collision/FillOctreeSystem.cpp @@ -7,12 +7,6 @@ void FillOctreeSystem::Update(double dt) void FillOctreeSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) { - if (!entity.HasComponent("AABB")) { - //Derive AABB from model. - if (!Collision::AttachAABBComponentFromModel(entity)) { - return; - } - } boost::optional absoluteAABB = Collision::EntityAbsoluteAABB(entity); if (absoluteAABB) { m_Octree->AddDynamicObject(*absoluteAABB); diff --git a/src/Engine/Collision/TriggerSystem.cpp b/src/Engine/Collision/TriggerSystem.cpp index adc778e5..21a47721 100644 --- a/src/Engine/Collision/TriggerSystem.cpp +++ b/src/Engine/Collision/TriggerSystem.cpp @@ -5,11 +5,6 @@ void TriggerSystem::UpdateComponent(EntityWrapper& triggerEntity, ComponentWrapper& cTrigger, double dt) { - // The trigger *should* have a bounding box, or something, to test against so it can be triggered. - // If it doesn't, add one as big as the model for now, then size can be modified in editor if necessary. - if (!triggerEntity.HasComponent("AABB")) { - Collision::AttachAABBComponentFromModel(triggerEntity); - } boost::optional triggerBox = Collision::EntityAbsoluteAABB(triggerEntity); if (!triggerBox) { return; diff --git a/src/Engine/Rendering/Model.cpp b/src/Engine/Rendering/Model.cpp index 7b880208..9ce99564 100644 --- a/src/Engine/Rendering/Model.cpp +++ b/src/Engine/Rendering/Model.cpp @@ -125,6 +125,17 @@ Model::Model(std::string fileName) GLERROR("GLEW: BufferFail5"); //CreateBuffers(); + + glm::vec3 mini(INFINITY); + glm::vec3 maxi(-INFINITY); + + for (unsigned int i = 0; i < m_RawModel->NumVertices(); i++) { + const auto& v = m_RawModel->Vertices()[i]; + mini = glm::min(mini, v.Position); + maxi = glm::max(maxi, v.Position); + } + + m_Box = AABB(maxi, mini); } Model::~Model() diff --git a/src/Engine/Rendering/PickingPass.cpp b/src/Engine/Rendering/PickingPass.cpp index 80f0efb7..bef0e117 100644 --- a/src/Engine/Rendering/PickingPass.cpp +++ b/src/Engine/Rendering/PickingPass.cpp @@ -156,41 +156,72 @@ void PickingPass::Draw(RenderScene& scene) auto modelJob = std::dynamic_pointer_cast(job); if (modelJob) { - int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] }; + if (modelJob->Model->isSkined()) { + int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] }; - PickingInfo pickInfo; - pickInfo.Entity = modelJob->Entity; - pickInfo.World = modelJob->World; - pickInfo.Camera = scene.Camera; + PickingInfo pickInfo; + pickInfo.Entity = modelJob->Entity; + pickInfo.World = modelJob->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; + 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_ColorCounter[0] += 1; + 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_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo; - 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(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]))); - std::vector frameBones; - if (modelJob->AnimationOffset.animation != nullptr) { - frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations, modelJob->AnimationOffset); + std::vector frameBones; + if (modelJob->AnimationOffset.animation != nullptr) { + frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations, modelJob->AnimationOffset); + } else { + frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations); + } + glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); } else { - frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations); + int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] }; + + PickingInfo pickInfo; + pickInfo.Entity = modelJob->Entity; + pickInfo.World = modelJob->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; + + 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, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); + glBindVertexArray(modelJob->Model->VAO); @@ -226,19 +257,28 @@ void PickingPass::Draw(RenderScene& scene) m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo; - 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]))); + if(modelJob->Model->isSkined()) { + 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]))); + + std::vector frameBones; + if (modelJob->AnimationOffset.animation != nullptr) { + frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations, modelJob->AnimationOffset); + } else { + frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations); + } + glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); - std::vector frameBones; - if (modelJob->AnimationOffset.animation != nullptr) { - frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations, modelJob->AnimationOffset); } else { - frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations); + 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, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); - glBindVertexArray(modelJob->Model->VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer); @@ -251,30 +291,32 @@ void PickingPass::Draw(RenderScene& scene) if (modelJob) { + int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] }; + + PickingInfo pickInfo; + pickInfo.Entity = modelJob->Entity; + pickInfo.World = modelJob->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; + if (modelJob->Model->isSkined()) { m_PickingSkinnedProgram->Bind(); - int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] }; - - PickingInfo pickInfo; - pickInfo.Entity = modelJob->Entity; - pickInfo.World = modelJob->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; + 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())); @@ -293,28 +335,7 @@ void PickingPass::Draw(RenderScene& scene) } } else { - int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] }; - - PickingInfo pickInfo; - pickInfo.Entity = modelJob->Entity; - pickInfo.World = modelJob->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(modelJob->Matrix)); glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); diff --git a/src/Engine/Rendering/RenderSystem.cpp b/src/Engine/Rendering/RenderSystem.cpp index 67b0cff5..ab2b6dc2 100644 --- a/src/Engine/Rendering/RenderSystem.cpp +++ b/src/Engine/Rendering/RenderSystem.cpp @@ -1,10 +1,13 @@ #include "Rendering/RenderSystem.h" +#include "Collision/Collision.h" +#include "Core/Frustum.h" -RenderSystem::RenderSystem(World* world, EventBroker* eventBroker, const IRenderer* renderer, RenderFrame* renderFrame) +RenderSystem::RenderSystem(World* world, EventBroker* eventBroker, const IRenderer* renderer, RenderFrame* renderFrame, Octree* frustumCullOctree) : System(world, eventBroker) , m_Renderer(renderer) , m_RenderFrame(renderFrame) , m_World(world) + , m_Octree(frustumCullOctree) { EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &RenderSystem::OnSetCamera); EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &RenderSystem::OnInputCommand); @@ -42,12 +45,13 @@ bool RenderSystem::isChildOfCurrentCamera(EntityWrapper entity) void RenderSystem::fillModels(RenderScene::Queues &Jobs) { - auto models = m_World->GetComponents("Model"); - if (models == nullptr) { - return; - } + Frustum frustum(m_Camera->ProjectionMatrix() * m_Camera->ViewMatrix()); + std::vector seenEntities; + m_Octree->ObjectsInFrustum(frustum, seenEntities); - for (auto& cModel : *models) { + for (auto& seenEntity : seenEntities) { + EntityWrapper entity = seenEntity.Entity; + ComponentWrapper cModel = entity["Model"]; bool visible = cModel["Visible"]; if (!visible) { continue; @@ -57,8 +61,6 @@ void RenderSystem::fillModels(RenderScene::Queues &Jobs) continue; } - EntityWrapper entity(m_World, cModel.EntityID); - // Only render children of a camera if that camera is currently active // if (isChildOfACamera(entity) && !isChildOfCurrentCamera(entity)) { // continue; diff --git a/src/Engine/Sound/SoundManager.cpp b/src/Engine/Sound/SoundManager.cpp new file mode 100644 index 00000000..8e103d5c --- /dev/null +++ b/src/Engine/Sound/SoundManager.cpp @@ -0,0 +1,374 @@ +#include "Sound/SoundManager.h" + +SoundManager::SoundManager(World* world, EventBroker* eventBroker) +{ + ConfigFile* config = ResourceManager::Load("Config.ini"); + m_EventBroker = eventBroker; + m_World = world; + m_BGMVolumeChannel = config->Get("Sound.BGMVolume", 1.f); + m_SFXVolumeChannel = config->Get("Sound.SFXVolume", 1.f); + + initOpenAL(); + alSpeedOfSound(340.29f); + alDistanceModel(AL_LINEAR_DISTANCE); + alDopplerFactor(1); + + EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnEntity, &SoundManager::OnPlaySoundOnEntity); + EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnPosition, &SoundManager::OnPlaySoundOnPosition); + EVENT_SUBSCRIBE_MEMBER(m_EPlayBackgroundMusic, &SoundManager::OnPlayBackgroundMusic); + EVENT_SUBSCRIBE_MEMBER(m_EStopSound, &SoundManager::OnStopSound); + EVENT_SUBSCRIBE_MEMBER(m_EPauseSound, &SoundManager::OnPauseSound); + EVENT_SUBSCRIBE_MEMBER(m_EContinueSound, &SoundManager::OnContinueSound); + EVENT_SUBSCRIBE_MEMBER(m_ESetBGMGain, &SoundManager::OnSetBGMGain); + EVENT_SUBSCRIBE_MEMBER(m_ESetSFXGain, &SoundManager::OnSetSFXGain); + EVENT_SUBSCRIBE_MEMBER(m_EPause, &SoundManager::OnPause); + EVENT_SUBSCRIBE_MEMBER(m_EResume, &SoundManager::OnResume); + EVENT_SUBSCRIBE_MEMBER(m_EComponentAttached, &SoundManager::OnComponentAttached); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &SoundManager::OnPlayerSpawned); + EVENT_SUBSCRIBE_MEMBER(m_EPlayQueueOnEntity, &SoundManager::OnPlayQueueOnEntity); +} + +SoundManager::~SoundManager() +{ + stopEmitters(); // Stopps emitters + deleteInactiveEmitters(); // Deletes stopped emitters + // Delete entities + std::unordered_map::iterator it; + for (it = m_Sources.begin(); it != m_Sources.end(); it++) { + m_World->DeleteEntity((*it).first); + } + m_Sources.clear(); + + alcDestroyContext(m_ALCcontext); + alcCloseDevice(m_ALCdevice); +} + +void SoundManager::stopEmitters() +{ + std::unordered_map::iterator it; + for (it = m_Sources.begin(); it != m_Sources.end(); it++) { + if (getSourceState(it->second->ALsource) == AL_PLAYING) { + stopSound(it->second); + } + } +} + +void SoundManager::Update(double dt) +{ + m_EventBroker->Process(); + deleteInactiveEmitters(); // can be optimized with "EEntityDeleted" + updateEmitters(dt); + updateListener(dt); + + // Editor debug info + ImGui::SliderFloat("BGM", &m_BGMVolumeChannel, 0.0f, 1.0f, "%.3f", 1.0f); + ImGui::SliderFloat("SFX", &m_SFXVolumeChannel, 0.0f, 1.0f, "%.3f", 1.0f); +} + +void SoundManager::deleteInactiveEmitters() +{ + std::unordered_map::iterator it; + for (it = m_Sources.begin(); it != m_Sources.end();) { + if (m_World->ValidEntity(it->first) + && m_World->HasComponent(it->first, "SoundEmitter")) { + if (getSourceState(it->second->ALsource) != AL_STOPPED) { + // Nothing to see here, move along + it++; + continue; + } else { + // Sound has been stopped / finished playing. + alDeleteBuffers(1, &it->second->ALsource); + alDeleteSources(1, &it->second->ALsource); + m_World->DeleteEntity(it->first); + delete it->second; + it = m_Sources.erase(it); + } + } else { + // Entity / Component has been removed + stopSound(it->second); + alDeleteBuffers(1, &it->second->ALsource); + alDeleteSources(1, &it->second->ALsource); + delete it->second; + it = m_Sources.erase(it); + } + } +} + +void SoundManager::updateEmitters(double dt) +{ + std::unordered_map::iterator it; + for (it = m_Sources.begin(); it != m_Sources.end(); it++) { + // Get previous pos + if (!m_World->ValidEntity(it->first)) { + return; + } + if (!m_World->HasComponent(it->first, "SoundEmitter")) + return; + + glm::vec3 previousPos; + alGetSource3f(it->second->ALsource, AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z); + // Get next pos + if (!m_World->HasComponent(it->first, "Transform")) + return; + if (!m_World->ValidEntity(m_World->GetParent(it->first))) { + return; + } + glm::vec3 nextPos = Transform::AbsolutePosition(m_World, it->first); + // Calculate velocity + glm::vec3 velocity = glm::vec3(nextPos - previousPos) / (float)dt; + setSourcePos(it->second->ALsource, nextPos); + setSourceVel(it->second->ALsource, velocity); + + auto emitter = m_World->GetComponent(it->first, "SoundEmitter"); + setSoundProperties(it->second, &emitter); + + // Path changed + if (it->second->SoundResource->Path() != (std::string)emitter["FilePath"]) { + it->second->SoundResource = ResourceManager::Load((std::string)emitter["FilePath"]); + if (it->second->SoundResource->Buffer() != 0) { + playSound(it->second); + } + } + } +} + +void SoundManager::updateListener(double dt) +{ + // Should only be one listener. + auto listenerComponents = m_World->GetComponents("Listener"); + if (listenerComponents == nullptr || !m_LocalPlayer.Valid()) { + return; + } + for (auto it = listenerComponents->begin(); it != listenerComponents->end(); it++) { + EntityWrapper listener(m_World, (*it).EntityID); + if (!listener.Valid()) { + break; + } + if (listener.IsChildOf(m_LocalPlayer) || listener == m_LocalPlayer) { + glm::vec3 previousPos; + alGetListener3f(AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z); // Get previous pos + glm::vec3 nextPos = Transform::AbsolutePosition(listener); // Get next (current) pos + glm::vec3 velocity = glm::vec3(nextPos - previousPos) / (float)dt; // Calculate velocity + setListenerPos(nextPos); + setListenerVel(velocity); + setListenerOri(glm::eulerAngles(Transform::AbsoluteOrientation(listener))); + break; + } + } +} + +Source* SoundManager::createSource(std::string filePath) +{ + ALuint alSource; + alGenSources((ALuint)1, &alSource); + alSourcef(alSource, AL_REFERENCE_DISTANCE, 1.0); + alSourcef(alSource, AL_MAX_DISTANCE, FLT_MAX); + Source* source = new Source(); + source->ALsource = alSource; + source->SoundResource = ResourceManager::Load(filePath); + return source; +} + +void SoundManager::playSound(Source* source) +{ + alSourcei(source->ALsource, AL_BUFFER, source->SoundResource->Buffer()); + alSourcePlay(source->ALsource); +} + +void SoundManager::playQueue(QueuedBuffers qb) +{ + for (int i = 0; i < qb.second.size(); i++) { + alSourceQueueBuffers(qb.first, 1, &qb.second[i]); + } + alSourcePlay(qb.first); +} + +void SoundManager::stopSound(Source* source) +{ + alSourceStop(source->ALsource); +} + +bool SoundManager::OnPlaySoundOnEntity(const Events::PlaySoundOnEntity & e) +{ + Source* source = createSource(e.FilePath); + source->Type = SoundType::SFX; + EntityID child = m_World->CreateEntity(e.EmitterID); + m_World->AttachComponent(child, "Transform"); + m_World->AttachComponent(child, "SoundEmitter"); + m_Sources[child] = source; + playSound(source); + return false; +} + +bool SoundManager::OnPlaySoundOnPosition(const Events::PlaySoundOnPosition & e) +{ + Source* source = createSource(e.FilePath); + auto emitterID = m_World->CreateEntity(); + auto transform = m_World->AttachComponent(emitterID, "Transform"); + (glm::vec3&)transform["Position"] = e.Position; + auto emitter = m_World->AttachComponent(emitterID, "SoundEmitter"); + (float&)(double)emitter["Gain"] = e.Gain; + (float&)(double)emitter["Pitch"] = e.Pitch; + (bool&)emitter["Loop"] = e.Loop; + (float&)(double)emitter["MaxDistance"] = e.MaxDistance; + (float&)(double)emitter["RollOffFactor"] = e.RollOffFactor; + (float&)(double)emitter["ReferenceDistance"] = e.ReferenceDistance; + source->Type = SoundType::SFX; + m_Sources[emitterID] = source; + playSound(source); + return true; +} + +bool SoundManager::OnPauseSound(const Events::PauseSound & e) +{ + alSourcePause(m_Sources[e.EmitterID]->ALsource); + return true; +} + +bool SoundManager::OnStopSound(const Events::StopSound & e) +{ + alSourceStop(m_Sources[e.EmitterID]->ALsource); + return true; +} + +bool SoundManager::OnContinueSound(const Events::ContinueSound & e) +{ + alSourcePlay(m_Sources[e.EmitterID]->ALsource); + return true; +} + +bool SoundManager::OnPlayBackgroundMusic(const Events::PlayBackgroundMusic & e) +{ + auto listenerComponents = m_World->GetComponents("Listener"); + for (auto it = listenerComponents->begin(); it != listenerComponents->end(); it++) { + if ((*it).EntityID != m_LocalPlayer.ID) { + break; + } + auto emitterChild = m_World->CreateEntity((*it).EntityID); + auto emitter = m_World->AttachComponent(emitterChild, "SoundEmitter"); + (bool&)emitter["Loop"] = true; + (std::string&)emitter["FilePath"] = e.FilePath; + m_World->AttachComponent(emitterChild, "Transform"); + Source* source = createSource(e.FilePath); + source->Type = SoundType::BGM; + setSoundProperties(source, &emitter); + m_Sources[emitterChild] = source; + playSound(source); + } + return true; +} + +bool SoundManager::OnSetBGMGain(const Events::SetBGMGain & e) +{ + m_BGMVolumeChannel = e.Gain; + return true; +} + +bool SoundManager::OnSetSFXGain(const Events::SetSFXGain & e) +{ + m_SFXVolumeChannel = e.Gain; + return true; +} + +bool SoundManager::OnComponentAttached(const Events::ComponentAttached & e) +{ + if (e.Component.Info.Name == "SoundEmitter") { + auto component = m_World->GetComponent(e.Entity.ID, "SoundEmitter"); + Source* source = createSource(component["FilePath"]); + m_Sources[e.Entity.ID] = source; + } + return false; +} + +bool SoundManager::OnPause(const Events::Pause & e) +{ + for (auto it = m_Sources.begin(); it != m_Sources.end(); it++) { + alSourcePause(it->second->ALsource); + } + return false; +} + +bool SoundManager::OnResume(const Events::Resume &e) +{ + for (auto it = m_Sources.begin(); it != m_Sources.end(); it++) { + alSourcePlay(it->second->ALsource); + } + return false; +} + + +bool SoundManager::OnPlayerSpawned(const Events::PlayerSpawned &e) +{ + if (e.PlayerID == -1) { // Local player + m_LocalPlayer = e.Player; + return true; + } + return false; +} + + +bool SoundManager::OnPlayQueueOnEntity(const Events::PlayQueueOnEntity &e) +{ + Source* source = createSource(*e.FilePaths.begin()); + std::vector buffers; + buffers.push_back(source->SoundResource->Buffer()); + source->Type = SoundType::BGM; + std::vector::const_iterator it; + for (it = e.FilePaths.begin() + 1; it != e.FilePaths.end(); it++) { + buffers.push_back(ResourceManager::Load(*it)->Buffer()); + } + playQueue(QueuedBuffers(source->ALsource, buffers)); + return true; +} + +ALenum SoundManager::getSourceState(ALuint source) +{ + ALenum state; + alGetSourcei(source, AL_SOURCE_STATE, &state); + return state; +} + +void SoundManager::setGain(Source * source, float gain) +{ + alSourcef(source->ALsource, AL_GAIN, gain); +} + +void SoundManager::setSoundProperties(Source* source, ComponentWrapper* soundComponent) +{ + float gain = (source->Type == SoundType::SFX) ? m_SFXVolumeChannel : m_BGMVolumeChannel; + alSourcef(source->ALsource, AL_GAIN, (float)(double)(*soundComponent)["Gain"] * gain); + alSourcef(source->ALsource, AL_PITCH, (float)(double)(*soundComponent)["Pitch"]); + alSourcei(source->ALsource, AL_LOOPING, (int)(bool)(*soundComponent)["Loop"]); // YOLO + alSourcef(source->ALsource, AL_MAX_DISTANCE, (float)(double)(*soundComponent)["MaxDistance"]); + alSourcef(source->ALsource, AL_ROLLOFF_FACTOR, (float)(double)(*soundComponent)["RollOffFactor"]); + alSourcef(source->ALsource, AL_REFERENCE_DISTANCE, (float)(double)(*soundComponent)["ReferenceDistance"]); +} + +void SoundManager::initOpenAL() +{ + // Initialize OpenAL + m_ALCdevice = alcOpenDevice(nullptr); + if (m_ALCdevice != nullptr) { + m_ALCcontext = alcCreateContext(m_ALCdevice, nullptr); + alcMakeContextCurrent(m_ALCcontext); + } else { + LOG_ERROR("OpenAL failed to initialize."); + } +} + +void SoundManager::setListenerOri(glm::vec3 ori) +{ + // Calculate forward and up vector. + glm::vec3 forward = glm::vec3(0.0, 0.0, -1.0); + forward = glm::rotateX(forward, ori.x); + forward = glm::rotateY(forward, ori.y); + forward = glm::rotateZ(forward, ori.z); + glm::normalize(forward); + glm::vec3 up = glm::vec3(0.0, 1.0, 0.0); + up = glm::rotateX(up, ori.x); + up = glm::rotateY(up, ori.y); + up = glm::rotateZ(up, ori.z); + glm::normalize(up); + ALfloat lOri[6] = { forward.x, forward.y, forward.z, up.x, up.y, up.z }; + alListenerfv(AL_ORIENTATION, lOri); +} \ No newline at end of file diff --git a/src/Engine/Sound/SoundSystem.cpp b/src/Engine/Sound/SoundSystem.cpp deleted file mode 100644 index cd83d9c6..00000000 --- a/src/Engine/Sound/SoundSystem.cpp +++ /dev/null @@ -1,308 +0,0 @@ -#include "Sound/SoundSystem.h" - -SoundSystem::SoundSystem(World* world, EventBroker* eventBroker, bool editorMode) -{ - m_EventBroker = eventBroker; - m_World = world; - m_EditorEnabled = editorMode; - - initOpenAL(); - - alSpeedOfSound(340.29f); - alDistanceModel(AL_LINEAR_DISTANCE); - alDopplerFactor(1); - - EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnEntity, &SoundSystem::OnPlaySoundOnEntity); - EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnPosition, &SoundSystem::OnPlaySoundOnPosition); - EVENT_SUBSCRIBE_MEMBER(m_EPlayBackgroundMusic, &SoundSystem::OnPlayBackgroundMusic); - EVENT_SUBSCRIBE_MEMBER(m_EStopSound, &SoundSystem::OnStopSound); - EVENT_SUBSCRIBE_MEMBER(m_EPauseSound, &SoundSystem::OnPauseSound); - EVENT_SUBSCRIBE_MEMBER(m_EContinueSound, &SoundSystem::OnContinueSound); - EVENT_SUBSCRIBE_MEMBER(m_ESetBGMGain, &SoundSystem::OnSetBGMGain); - EVENT_SUBSCRIBE_MEMBER(m_ESetSFXGain, &SoundSystem::OnSetSFXGain); -} - -SoundSystem::~SoundSystem() -{ - stopEmitters(); // Stopps emitters - deleteInactiveEmitters(); // Deletes stopped emitters - // Delete entities - std::unordered_map::iterator it; - for (it = m_Sources.begin(); it != m_Sources.end(); it++) { - m_World->DeleteEntity((*it).first); - } - m_Sources.clear(); - - alcDestroyContext(m_ALCcontext); - alcCloseDevice(m_ALCdevice); -} - -void SoundSystem::stopEmitters() -{ - std::unordered_map::iterator it; - for (it = m_Sources.begin(); it != m_Sources.end(); it++) { - if (getSourceState(it->second->ALsource) == AL_PLAYING) { - stopSound(it->second); - } - } -} - -void SoundSystem::Update(double dt) -{ - m_EventBroker->Process(); - addNewEmitters(dt); // can be optimized with "EEntityCreated" - deleteInactiveEmitters(); // can be optimized with "EEntityDeleted" - updateEmitters( dt); - updateListener( dt); -} - -void SoundSystem::deleteInactiveEmitters() -{ - std::unordered_map::iterator it; - for (it = m_Sources.begin(); it != m_Sources.end();) { - if (m_World->ValidEntity(it->first) - && m_World->HasComponent(it->first, "SoundEmitter")) { - if (getSourceState(it->second->ALsource) != AL_STOPPED) { - // Nothing to see here, move along - it++; - continue; - } else { - // Sound has been stopped / finished playing. - alDeleteBuffers(1, &it->second->ALsource); - alDeleteSources(1, &it->second->ALsource); - m_World->DeleteEntity(it->first); - delete it->second; - it = m_Sources.erase(it); - } - } else { - // Entity / Component has been removed - stopSound((*it).second); - alDeleteBuffers(1, &it->second->ALsource); - alDeleteSources(1, &it->second->ALsource); - delete it->second; - it = m_Sources.erase(it); - } - } -} - -void SoundSystem::addNewEmitters(double dt) -{ - auto emitterComponents = m_World->GetComponents("SoundEmitter"); - if (emitterComponents == nullptr) { - return; - } - for (auto it = emitterComponents->begin(); it != emitterComponents->end(); it++) { - EntityID emitter = (*it).EntityID; - std::unordered_map::iterator source; - source = m_Sources.find(emitter); - if (source == m_Sources.end()) { // Did not exist, add it - Source* source = createSource((std::string)(*it)["FilePath"]); - m_Sources[emitter] = source; - } - } -} - -void SoundSystem::updateEmitters(double dt) -{ - std::unordered_map::iterator it; - for (it = m_Sources.begin(); it != m_Sources.end(); it++) { - // Get previous pos - glm::vec3 previousPos; - alGetSource3f(it->second->ALsource, AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z); - // Get next pos - glm::vec3 nextPos = Transform::AbsolutePosition(m_World, it->first); - // Calculate velocity - glm::vec3 velocity = glm::vec3(nextPos - previousPos) / (float)dt; - setSourcePos(it->second->ALsource, nextPos); - setSourceVel(it->second->ALsource, velocity); - float gain; - if (it->second->Type == SoundType::SFX) { - gain = m_SFXVolumeChannel; - } else if (it->second->Type == SoundType::BGM) { - gain = m_BGMVolumeChannel; - } - auto emitter = m_World->GetComponent(it->first, "SoundEmitter"); - setSoundProperties(it->second->ALsource, &emitter); - - // To make an emitter play when spawned in editor mode - if (m_EditorEnabled) { - // Path changed - if (it->second->SoundResource->Path() != (std::string)emitter["FilePath"]) { - it->second->SoundResource = ResourceManager::Load((std::string)emitter["FilePath"]); - if (it->second->SoundResource->Buffer() != 0) { - playSound(it->second); - } - } - } - } -} - -void SoundSystem::updateListener(double dt) -{ - // Should only be one listener. - auto listenerComponents = m_World->GetComponents("Listener"); - if (listenerComponents == nullptr) { - return; - } - for (auto it = listenerComponents->begin(); it != listenerComponents->end(); it++) { - EntityID listener = (*it).EntityID; - glm::vec3 previousPos; - alGetListener3f(AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z); // Get previous pos - glm::vec3 nextPos = Transform::AbsolutePosition(m_World, listener); // Get next (current) pos - glm::vec3 velocity = glm::vec3(nextPos - previousPos) / (float)dt; // Calculate velocity - setListenerPos(nextPos); - setListenerVel(velocity); - setListenerOri(glm::eulerAngles(Transform::AbsoluteOrientation(m_World, listener))); - } -} - -Source* SoundSystem::createSource(std::string filePath) -{ - ALuint alSource; - alGenSources((ALuint)1, &alSource); - alSourcef(alSource, AL_REFERENCE_DISTANCE, 1.0); - alSourcef(alSource, AL_MAX_DISTANCE, FLT_MAX); - Source* source = new Source(); - source->ALsource = alSource; - source->SoundResource = ResourceManager::Load(filePath); - return source; -} - -void SoundSystem::playSound(Source* source) -{ - alSourcei(source->ALsource, AL_BUFFER, source->SoundResource->Buffer()); - alSourcePlay(source->ALsource); -} - -void SoundSystem::stopSound(Source* source) -{ - alSourceStop(source->ALsource); -} - -bool SoundSystem::OnPlaySoundOnEntity(const Events::PlaySoundOnEntity & e) -{ - Source* source = createSource(e.FilePath); - source->Type = SoundType::SFX; - m_Sources[e.EmitterID] = source; - playSound(source); - return false; -} - -bool SoundSystem::OnPlaySoundOnPosition(const Events::PlaySoundOnPosition & e) -{ - Source* source = createSource(e.FilePath); - auto emitterID = m_World->CreateEntity(); - auto transform = m_World->AttachComponent(emitterID, "Transform"); - (glm::vec3&)transform["Position"] = e.Position; - auto emitter = m_World->AttachComponent(emitterID, "SoundEmitter"); - (float&)(double)emitter["Gain"] = e.Gain; - (float&)(double)emitter["Pitch"] = e.Pitch; - (bool&)emitter["Loop"] = e.Loop; - (float&)(double)emitter["MaxDistance"] = e.MaxDistance; - (float&)(double)emitter["RollOffFactor"] = e.RollOffFactor; - (float&)(double)emitter["ReferenceDistance"] = e.ReferenceDistance; - auto model = m_World->AttachComponent(emitterID, "Model"); - (std::string&)model["Resource"] = "Models/Core/UnitCube.mesh"; // 360NoScope UnitCube - source->Type = SoundType::SFX; - m_Sources[emitterID] = source; - playSound(source); - return true; -} - -bool SoundSystem::OnPauseSound(const Events::PauseSound & e) -{ - alSourcePause(m_Sources[e.EmitterID]->ALsource); - return true; -} - -bool SoundSystem::OnStopSound(const Events::StopSound & e) -{ - alSourceStop(m_Sources[e.EmitterID]->ALsource); - return true; -} - -bool SoundSystem::OnContinueSound(const Events::ContinueSound & e) -{ - alSourcePlay(m_Sources[e.EmitterID]->ALsource); - return true; -} - -bool SoundSystem::OnPlayBackgroundMusic(const Events::PlayBackgroundMusic & e) -{ - auto listenerComponents = m_World->GetComponents("Listener"); - for (auto it = listenerComponents->begin(); it != listenerComponents->end(); it++) { - auto emitterChild = m_World->CreateEntity((*it).EntityID); - auto emitter = m_World->AttachComponent(emitterChild, "SoundEmitter"); - (bool&)emitter["Loop"] = true; - (std::string&)emitter["FilePath"] = e.FilePath; - m_World->AttachComponent(emitterChild, "Transform"); - Source* source = createSource(e.FilePath); - source->Type = SoundType::BGM; - m_Sources[emitterChild] = source; - playSound(source); - } - return true; -} - -bool SoundSystem::OnSetBGMGain(const Events::SetBGMGain & e) -{ - m_BGMVolumeChannel = e.Gain; - return true; -} - -bool SoundSystem::OnSetSFXGain(const Events::SetSFXGain & e) -{ - m_SFXVolumeChannel = e.Gain; - return true; -} - -void SoundSystem::setListenerOri(glm::vec3 ori) -{ - // Calculate forward and up vector. - glm::vec3 forward = glm::vec3(0.0, 0.0, -1.0); - forward = glm::rotateX(forward, ori.x); - forward = glm::rotateY(forward, ori.y); - forward = glm::rotateZ(forward, ori.z); - glm::normalize(forward); - glm::vec3 up = glm::vec3(0.0, 1.0, 0.0); - up = glm::rotateX(up, ori.x); - up = glm::rotateY(up, ori.y); - up = glm::rotateZ(up, ori.z); - glm::normalize(up); - ALfloat lOri[6] = { forward.x, forward.y, forward.z, up.x, up.y, up.z }; - alListenerfv(AL_ORIENTATION, lOri); -} - -ALenum SoundSystem::getSourceState(ALuint source) -{ - ALenum state; - alGetSourcei(source, AL_SOURCE_STATE, &state); - return state; -} - -void SoundSystem::setGain(Source * source, float gain) -{ - alSourcef(source->ALsource, AL_GAIN, gain); -} - -void SoundSystem::setSoundProperties(ALuint source, ComponentWrapper* soundComponent) -{ - alSourcef(source, AL_GAIN, (float)(double)(*soundComponent)["Gain"]); - alSourcef(source, AL_PITCH, (float)(double)(*soundComponent)["Pitch"]); - alSourcei(source, AL_LOOPING, (int)(bool)(*soundComponent)["Loop"]); // YOLO - alSourcef(source, AL_MAX_DISTANCE, (float)(double)(*soundComponent)["MaxDistance"]); - alSourcef(source, AL_ROLLOFF_FACTOR, (float)(double)(*soundComponent)["RollOffFactor"]); - alSourcef(source, AL_REFERENCE_DISTANCE, (float)(double)(*soundComponent)["ReferenceDistance"]); -} - -void SoundSystem::initOpenAL() -{ - // Initialize OpenAL - m_ALCdevice = alcOpenDevice(nullptr); - if (m_ALCdevice != nullptr) { - m_ALCcontext = alcCreateContext(m_ALCdevice, nullptr); - alcMakeContextCurrent(m_ALCcontext); - } else { - LOG_ERROR("OpenAL failed to initialize."); - } -} \ No newline at end of file diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 8272519a..95310952 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -1,5 +1,6 @@ #include "Game.h" #include "Collision/FillOctreeSystem.h" +#include "Collision/FillFrustumOctreeSystem.h" #include "Collision/EntityAABB.h" #include "Collision/TriggerSystem.h" #include "Collision/CollisionSystem.h" @@ -74,16 +75,21 @@ Game::Game(int argc, char* argv[]) fp.MergeEntities(m_World); } + // Create the sound manager + m_SoundManager = new SoundManager(m_World, m_EventBroker); // Create Octrees - m_OctreeCollision = new Octree(AABB(glm::vec3(-100), glm::vec3(100)), 4); - m_OctreeTrigger = new Octree(AABB(glm::vec3(-100), glm::vec3(100)), 4); - m_OctreeFrustrumCulling = new Octree(AABB(glm::vec3(-100), glm::vec3(100)), 4); + // TODO: Perhaps the world bounds should be set in some non-arbitrary way instead of this. + AABB boxContainingTheWorld(glm::vec3(-300), glm::vec3(300)); + m_OctreeCollision = new Octree(boxContainingTheWorld, 4); + m_OctreeTrigger = new Octree(boxContainingTheWorld, 4); + m_OctreeFrustrumCulling = new Octree(boxContainingTheWorld, 4); // Create system pipeline m_SystemPipeline = new SystemPipeline(m_World, m_EventBroker); // All systems with orderlevel 0 will be updated first. unsigned int updateOrderLevel = 0; + m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); @@ -100,6 +106,7 @@ Game::Game(int argc, char* argv[]) ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeCollision, "Collidable"); m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeTrigger, "Player"); + m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeFrustrumCulling); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); @@ -109,7 +116,7 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeCollision); m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeTrigger); ++updateOrderLevel; - m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer, m_RenderFrame); + m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer, m_RenderFrame, m_OctreeFrustrumCulling); ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer, m_RenderFrame); @@ -119,19 +126,16 @@ Game::Game(int argc, char* argv[]) networkFunction(); } - // Invoke sound system - m_SoundSystem = new SoundSystem(m_World, m_EventBroker, m_Config->Get("Debug.EditorEnabled", false)); - m_LastTime = glfwGetTime(); } Game::~Game() { delete m_SystemPipeline; - delete m_SoundSystem; delete m_OctreeFrustrumCulling; delete m_OctreeCollision; delete m_OctreeTrigger; + delete m_SoundManager; delete m_World; delete m_FrameStack; delete m_InputProxy; @@ -159,16 +163,19 @@ void Game::Tick() m_InputProxy->Process(); m_EventBroker->Swap(); + m_SoundManager->Update(dt); + // Update network if (m_IsClientOrServer) { m_ClientOrServer->Update(); } + //m_SoundManager->Update(dt); + // Iterate through systems and update world! m_EventBroker->Process(); m_SystemPipeline->Update(dt); debugTick(dt); m_Renderer->Update(dt); - m_SoundSystem->Update(dt); GLERROR("Game::Tick m_RenderQueueFactory->Update"); m_Renderer->Draw(*m_RenderFrame); m_RenderFrame->Clear(); diff --git a/src/Game/Systems/PlayerMovementSystem.cpp b/src/Game/Systems/PlayerMovementSystem.cpp index f022cb26..99a2def5 100644 --- a/src/Game/Systems/PlayerMovementSystem.cpp +++ b/src/Game/Systems/PlayerMovementSystem.cpp @@ -56,6 +56,12 @@ void PlayerMovementSystem::Update(double dt) } else { wishSpeed = playerMovementSpeed; } + if (player.ID == m_LocalPlayer.ID) { + if (glm::length(wishDirection) == 0) { + // If no key is pressed, reset the distance moved since last step. + m_DistanceMoved = 0; + } + } glm::vec3& velocity = cPhysics["Velocity"]; bool isOnGround = (bool)cPhysics["IsOnGround"]; ImGui::Text(isOnGround ? "On ground" : "In air"); @@ -94,6 +100,8 @@ void PlayerMovementSystem::Update(double dt) controller->SetDoubleJumping(false); } else { controller->SetDoubleJumping(true); + Events::DoubleJump e; + m_EventBroker->Publish(e); } velocity.y = 4.f; } @@ -136,6 +144,7 @@ void PlayerMovementSystem::Update(double dt) controller->Reset(); } + playerStep(dt); } void PlayerMovementSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) @@ -171,10 +180,38 @@ void PlayerMovementSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapp position += velocity * (float)dt; } +void PlayerMovementSystem::playerStep(double dt) +{ + if (!m_LocalPlayer.Valid()) { + return; + } + // Position of the local player, used see how far a player has moved. + glm::vec3 pos = (glm::vec3)m_World->GetComponent(m_LocalPlayer.ID, "Transform")["Position"]; + // Velocity of the local player, used to see if a player is airborne. + glm::vec3 vel = (glm::vec3)m_World->GetComponent(m_LocalPlayer.ID, "Physics")["Velocity"]; + m_DistanceMoved += glm::length(pos - m_LastPosition); + // Set the last position for next iteration + m_LastPosition = pos; + bool isAirborne = vel.y != 0; + if (m_DistanceMoved > m_PlayerStepLength && !isAirborne) { + // Player moved a step's distance + // Create footstep sound + Events::PlaySoundOnEntity e; + e.EmitterID = m_LocalPlayer.ID; + e.FilePath = m_LeftFoot ? "Audio/footstep/footstep2.wav" : "Audio/footstep/footstep3.wav"; + m_LeftFoot = !m_LeftFoot; + m_EventBroker->Publish(e); + m_DistanceMoved = 0.f; + } +} + bool PlayerMovementSystem::OnPlayerSpawned(Events::PlayerSpawned& e) { // When a player spawns, create an input controller for them m_PlayerInputControllers[e.Player] = new FirstPersonInputController(m_EventBroker, e.PlayerID); - + if (e.PlayerID == -1) { + // Keep track of the local player + m_LocalPlayer = e.Player; + } return true; } diff --git a/src/Game/Systems/SoundSystem.cpp b/src/Game/Systems/SoundSystem.cpp new file mode 100644 index 00000000..df14cd86 --- /dev/null +++ b/src/Game/Systems/SoundSystem.cpp @@ -0,0 +1,190 @@ +#include "Game/Systems/SoundSystem.h" + +SoundSystem::SoundSystem(World* world, EventBroker* eventbroker) + : System(world, eventbroker) + , PureSystem("SoundEmitter") + //, ImpureSystem() +{ + ConfigFile* config = ResourceManager::Load("Config.ini"); + m_Announcer = ResourceManager::Load("Config.ini")->Get("Sound.Announcer", "female"); + m_World = world; + m_EventBroker = eventbroker; + 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_EShoot, &SoundSystem::OnShoot); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &SoundSystem::OnPlayerDamage); + EVENT_SUBSCRIBE_MEMBER(m_ECaptured, &SoundSystem::OnCaptured); + EVENT_SUBSCRIBE_MEMBER(m_ETriggerTouch, &SoundSystem::OnTriggerTouch); +} + +void SoundSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cComponent, double dt) +{ } + +void SoundSystem::Update(double dt) +{ + // Temp for play test. + if(m_DrumsIsPlaying) { + m_DrumsIsPlaying = !drumTimer(dt); + } +} + +bool SoundSystem::OnPlayerSpawned(const Events::PlayerSpawned &e) +{ + if (e.PlayerID == -1) { // Local player + m_World->AttachComponent(e.Player.ID, "Listener"); + m_LocalPlayer = e.Player; + Events::PlaySoundOnEntity go; + go.EmitterID = m_LocalPlayer.ID; + go.FilePath = "Audio/announcer/" + m_Announcer + "/go.wav"; + m_EventBroker->Publish(go); + // TEMP: starts bgm + { + Events::PlayBackgroundMusic ev; + ev.FilePath = "Audio/bgm/ambient.wav"; + m_EventBroker->Publish(ev); + } + } + return true; +} + +bool SoundSystem::OnInputCommand(const Events::InputCommand & e) +{ + if (e.Command == "Jump" && e.Value > 0) { + if (e.PlayerID == -1) { // local player + playerJumps(); + return true; + } + } + if (e.Command == "TakeDamage" && e.Value > 0) { + Events::PlayerDamage ev; + ev.Player = m_LocalPlayer; + ev.Damage = 1.0; + m_EventBroker->Publish(ev); + } + + return false; +} + +void SoundSystem::playerJumps() +{ + glm::vec3 vel = (glm::vec3)m_World->GetComponent(m_LocalPlayer.ID, "Physics")["Velocity"]; + if (vel.y == 0) { + Events::PlaySoundOnEntity e; + e.EmitterID = m_LocalPlayer.ID; + e.FilePath = "Audio/jump/jump1.wav"; + m_EventBroker->Publish(e); + } +} + +bool SoundSystem::drumTimer(double dt) +{ + m_DrumTimer += dt; + if (m_DrumTimer > 15) { + m_DrumTimer = 0.0; + return true; + } else { + return false; + } +} + +bool SoundSystem::OnShoot(const Events::Shoot & e) +{ + Events::PlaySoundOnEntity ev; + ev.EmitterID = m_LocalPlayer.ID; + ev.FilePath = "Audio/laser/laser1.wav"; + m_EventBroker->Publish(ev); + return true; +} + +bool SoundSystem::OnCaptured(const Events::Captured & e) +{ + int homeTeam = (int)m_World->GetComponent(e.CapturePointID, "Team")["Team"]; + int team = (int)m_World->GetComponent(m_LocalPlayer.ID, "Team")["Team"]; + Events::PlaySoundOnEntity ev; + if (team == homeTeam) { + ev.FilePath = "Audio/announcer/" + m_Announcer + "/objective_achieved.wav"; + } else { + ev.FilePath = "Audio/announcer/" + m_Announcer + "/objective_failed.wav"; // have not been tested + } + ev.EmitterID = m_LocalPlayer.ID; + m_EventBroker->Publish(ev); + // Temp for play test. + m_DrumsIsPlaying = false; + return false; +} + +// Testing purposes atm... +bool SoundSystem::OnPlayerDamage(const Events::PlayerDamage & e) +{ + // Should check for only local players here... + std::uniform_int_distribution dist(1, 12); + int rand = dist(generator); + std::vector paths; + paths.push_back("Audio/hurt/hurt" + std::to_string(rand) + ".wav"); + +// // Breathe +// int ammountOfbreaths = (static_cast(e.Damage) / 10) + 2; // TEMP: Idk something stupid like this shit +// for (int i = 0; i < ammountOfbreaths; i++) { +// paths.push_back("Audio/exhausted/breath.wav"); +// } + Events::PlayQueueOnEntity ev; + ev.Emitter = m_LocalPlayer; + ev.FilePaths = paths; + m_EventBroker->Publish(ev); + return false; +} + +bool SoundSystem::OnPlayerDeath(const Events::PlayerDeath & e) +{ + Events::PlaySoundOnEntity ev; + ev.EmitterID = m_LocalPlayer.ID; + ev.FilePath = "Audio/die/die2.wav"; + m_EventBroker->Publish(ev); + return false; +} + +bool SoundSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup & e) +{ + Events::PlaySoundOnEntity ev; + ev.EmitterID = m_LocalPlayer.ID; + ev.FilePath = "Audio/pickup/pickup2.wav"; + m_EventBroker->Publish(ev); + return false; +} + +bool SoundSystem::OnTriggerTouch(const Events::TriggerTouch & e) +{ + // Temp for play test. + if (m_DrumsIsPlaying) { + return false; + } + if (m_World->HasComponent(e.Trigger.ID, "CapturePoint")) { + Events::PlaySoundOnEntity ev; // should be BGM + ev.EmitterID = m_LocalPlayer.ID; + ev.FilePath = "Audio/bgm/drumstest.wav"; + m_EventBroker->Publish(ev); + // Temp for play test. + m_DrumsIsPlaying = true; + } + return false; +} + +bool SoundSystem::OnDoubleJump(const Events::DoubleJump & e) +{ + Events::PlaySoundOnEntity ev; + ev.EmitterID = m_LocalPlayer.ID; + ev.FilePath = "Audio/jump/jump2.wav"; + m_EventBroker->Publish(ev); + return false; +} + +bool SoundSystem::OnDashAbility(const Events::DashAbility &e) +{ + Events::PlaySoundOnEntity ev; + ev.EmitterID = m_LocalPlayer.ID; + ev.FilePath = "Audio/jump/dash1.wav"; + m_EventBroker->Publish(ev); + return false; +} \ No newline at end of file