@@ -12,6 +12,7 @@ Libraries bundled along with binaries for Windows (MSVC14), available as a submo
|
||||
| **[Xerces-C++](https://xerces.apache.org/xerces-c)** | 3.1.2 | [Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) |
|
||||
| **[ImGui](https://github.com/ocornut/imgui)** | 2015-12-12 | [MIT License](https://github.com/ocornut/imgui/blob/de3a154f3801de22c8e0bd2aeabf663a70c05972/LICENSE) |
|
||||
| **[nativefiledialog](https://github.com/mlabbe/nativefiledialog) | 2016-01-08 | https://github.com/mlabbe/nativefiledialog/blob/master/LICENSE |
|
||||
| **[OpenAL](https://www.openal.org/)** | 1.1 | [OpenAL License]() |
|
||||
|
||||
#### External libraries
|
||||
Libraries that are too big to be bundled with the project.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef Events_ContinueSound_h__
|
||||
#define Events_ContinueSound_h__
|
||||
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Core/Entity.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
// Continues to play a sound from where it was paused.
|
||||
struct ContinueSound : Event
|
||||
{
|
||||
EntityID EmitterID;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef Events_PauseSound_h__
|
||||
#define Events_PauseSound_h__
|
||||
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Core/Entity.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
// Pauses a playing sound
|
||||
struct PauseSound : Event
|
||||
{
|
||||
EntityID EmitterID;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef Events_PlayBackgroundMusic_h__
|
||||
#define Events_PlayBackgroundMusic_h__
|
||||
|
||||
#include <string>
|
||||
#include "Core/Entity.h"
|
||||
#include "Core/Event.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
// Play a sound that will be heared the same anywhere
|
||||
struct PlayBackgroundMusic : public Event
|
||||
{
|
||||
std::string FilePath = "";
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef Events_PlaySoundOnEntity_h__
|
||||
#define Events_PlaySoundOnEntity_h__
|
||||
|
||||
#include <string>
|
||||
#include "Core/Entity.h"
|
||||
#include "Core/Event.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
|
||||
// Plays a sound on an entity with a SoundEmitter component attached.
|
||||
// Sound behavior is thereby specified in the SoundEmitter component.
|
||||
struct PlaySoundOnEntity : public Event
|
||||
{
|
||||
EntityID EmitterID = 0;
|
||||
std::string FilePath = "";
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef Events_PlaySoundOnPosition_h__
|
||||
#define Events_PlaySoundOnPosition_h__
|
||||
|
||||
#include <string>
|
||||
#include <glm/common.hpp>
|
||||
#include "Core/Event.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
|
||||
// Plays a sound on a given position. Idk if this would be useful.
|
||||
struct PlaySoundOnPosition : public Event
|
||||
{
|
||||
glm::vec3 Position = glm::vec3(0);
|
||||
std::string FilePath = "";
|
||||
float Gain = 1;
|
||||
float Pitch = 1;
|
||||
bool Loop = false;
|
||||
float MaxDistance = 20;
|
||||
float RollOffFactor = 1;
|
||||
float ReferenceDistance = 1;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef Events_SetBGMGain_h__
|
||||
#define Events_SetBGMGain_h__
|
||||
|
||||
#include "Core/Event.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
// Set the "volume" for all background sounds
|
||||
struct SetBGMGain : public Event
|
||||
{
|
||||
float Gain;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef Events_SetSFXGain_h__
|
||||
#define Events_SetSFXGain_h__
|
||||
|
||||
#include "Core/Event.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
// Set the "volume" for all effect sounds
|
||||
struct SetSFXGain : public Event
|
||||
{
|
||||
float Gain;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef Events_StopSound_h__
|
||||
#define Events_StopSound_h__
|
||||
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Core/Entity.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
// Stops a sound emitter, and will also delete it.
|
||||
struct StopSound : Event
|
||||
{
|
||||
EntityID EmitterID;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,113 @@
|
||||
#ifndef Sound_h__
|
||||
#define Sound_h__
|
||||
|
||||
#include "Core/ResourceManager.h"
|
||||
|
||||
class Sound : public Resource
|
||||
{
|
||||
friend class ResourceManager;
|
||||
public:
|
||||
Sound(std::string path) { m_Buffer = LoadFile(path); m_Path = path; }
|
||||
~Sound() { ClearBuffer(); }
|
||||
ALuint Buffer() { return m_Buffer; }
|
||||
std::string Path() { return m_Path; }
|
||||
float Gain() { return m_Gain; }
|
||||
void SetGain(float value) { m_Gain = value; }
|
||||
void ClearBuffer() { alDeleteBuffers(1, &m_Buffer); m_BufferCache.clear(); };
|
||||
|
||||
private:
|
||||
float m_Gain = 1;
|
||||
ALuint m_Buffer;
|
||||
std::string m_Path;
|
||||
|
||||
// File info
|
||||
char m_Type[4];
|
||||
unsigned long m_Size, m_ChunkSize;
|
||||
short m_FormatType, m_Channels;
|
||||
unsigned long m_SampleRate, m_AvgBytesPerSec;
|
||||
short m_BytesPerSample, m_BitsPerSample;
|
||||
unsigned int m_DataSize;
|
||||
std::map<std::string, ALuint> m_BufferCache;
|
||||
|
||||
ALuint LoadFile(std::string path)
|
||||
{
|
||||
if (m_BufferCache.find(path) != m_BufferCache.end()) {
|
||||
return m_BufferCache[path];
|
||||
}
|
||||
|
||||
// Open file
|
||||
FILE *fp = fopen(path.c_str(), "rb");
|
||||
if (!fp) {
|
||||
printf("Sound: Failed to open file %s, no such file exists", path.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
//CHECK FOR VALID WAVE-FILE
|
||||
fread(m_Type, sizeof(char), 4, fp);
|
||||
if (m_Type[0] != 'R' || m_Type[1] != 'I' || m_Type[2] != 'F' || m_Type[3] != 'F') {
|
||||
printf("ERROR: No RIFF in WAVE-file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fread(&m_Size, 4 * sizeof(char), 1, fp);
|
||||
fread(m_Type, sizeof(char), 4, fp);
|
||||
if (m_Type[0] != 'W' || m_Type[1] != 'A' || m_Type[2] != 'V' || m_Type[3] != 'E') {
|
||||
printf("ERROR: Not WAVE-file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fread(m_Type, sizeof(char), 4, fp);
|
||||
if (m_Type[0] != 'f' || m_Type[1] != 'm' || m_Type[2] != 't' || m_Type[3] != ' ') {
|
||||
printf("ERROR: No fmt in WAVE-file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// READ THE DATA FROM WAVE-FILE
|
||||
fread(&m_ChunkSize, 4 * sizeof(char), 1, fp);
|
||||
fread(&m_FormatType, 2 * sizeof(char), 1, fp);
|
||||
fread(&m_Channels, 2 * sizeof(char), 1, fp);
|
||||
fread(&m_SampleRate, 4 * sizeof(char), 1, fp);
|
||||
fread(&m_AvgBytesPerSec, 4 * sizeof(char), 1, fp);
|
||||
fread(&m_BytesPerSample, 2 * sizeof(char), 1, fp);
|
||||
fread(&m_BitsPerSample, 2 * sizeof(char), 1, fp);
|
||||
|
||||
fread(m_Type, sizeof(char), 4, fp);
|
||||
if (m_Type[0] != 'd' || m_Type[1] != 'a' || m_Type[2] != 't' || m_Type[3] != 'a') {
|
||||
printf("ERROR: WAVE-file Missing data");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fread(&m_DataSize, 4 * sizeof(char), 1, fp);
|
||||
|
||||
unsigned char* buf = new unsigned char[m_DataSize];
|
||||
fread(buf, sizeof(char), m_DataSize, fp);
|
||||
fclose(fp);
|
||||
|
||||
// Create buffer
|
||||
ALuint format = 0;
|
||||
if (m_BitsPerSample == 8) {
|
||||
if (m_Channels == 1) {
|
||||
format = AL_FORMAT_MONO8;
|
||||
} else if (m_Channels == 2) {
|
||||
format = AL_FORMAT_STEREO8;
|
||||
}
|
||||
}
|
||||
if (m_BitsPerSample == 16) {
|
||||
if (m_Channels == 1) {
|
||||
format = AL_FORMAT_MONO16;
|
||||
} else if (m_Channels == 2) {
|
||||
format = AL_FORMAT_STEREO16;
|
||||
}
|
||||
}
|
||||
|
||||
ALuint buffer;
|
||||
alGenBuffers(1, &buffer);
|
||||
alBufferData(buffer, format, buf, m_DataSize, m_SampleRate);
|
||||
delete[] buf;
|
||||
|
||||
m_BufferCache[path] = buffer;
|
||||
return buffer;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
#ifndef SoundSystem_h__
|
||||
#define SoundSystem_h__
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "glm/common.hpp"
|
||||
#include "glm/gtx/rotate_vector.hpp" // Calculate Up vector
|
||||
#include "OpenAL/al.h"
|
||||
#include "OpenAL/alc.h"
|
||||
|
||||
#include "Core/World.h"
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Core/Transform.h" // Absolute transform
|
||||
#include "Sound/Sound.h"
|
||||
#include "Sound/EPlaySoundOnEntity.h"
|
||||
#include "Sound/EPlaySoundOnPosition.h"
|
||||
#include "Sound/EPlayBackgroundMusic.h"
|
||||
#include "Sound/EPauseSound.h"
|
||||
#include "Sound/EContinueSound.h"
|
||||
#include "Sound/EStopSound.h"
|
||||
#include "Sound/ESetBGMGain.h"
|
||||
#include "Sound/ESetSFXGain.h"
|
||||
|
||||
enum class SoundType {
|
||||
SFX,
|
||||
BGM
|
||||
};
|
||||
|
||||
struct Source
|
||||
{
|
||||
Source() { }
|
||||
Sound* SoundResource = nullptr;
|
||||
ALuint ALsource;
|
||||
SoundType Type;
|
||||
};
|
||||
|
||||
class SoundSystem
|
||||
{
|
||||
public:
|
||||
SoundSystem() { }
|
||||
SoundSystem(World* world, EventBroker* eventBroker, bool editorMode);
|
||||
~SoundSystem();
|
||||
// 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); };
|
||||
void setListenerVel(glm::vec3 vel) { alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z); };
|
||||
void setListenerOri(glm::vec3 ori);
|
||||
glm::vec3 listnerPos() { glm::vec3 pos; alGetListener3f(AL_POSITION, &pos.x, &pos.y, &pos.z); return pos; };
|
||||
glm::vec3 listenerVel() { glm::vec3 vel; alGetListener3f(AL_VELOCITY, &vel.x, &vel.y, &vel.z); return vel; };
|
||||
glm::vec3 listenerOri() { glm::vec3 ori; alGetListener3f(AL_ORIENTATION, &ori.x, &ori.y, &ori.z); return ori; };
|
||||
void setSourcePos(ALuint source, glm::vec3 pos) { ALfloat spos[3] = { pos.x, pos.y, pos.z }; alSourcefv(source, AL_POSITION, spos); };
|
||||
void setSourceVel(ALuint source, glm::vec3 vel) { ALfloat svel[3] = { vel.x, vel.y, vel.z }; alSourcefv(source, AL_VELOCITY, svel); };
|
||||
|
||||
// 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();
|
||||
ALenum getSourceState(ALuint source);
|
||||
void setGain(Source* source, float gain);
|
||||
void setSoundProperties(ALuint source, ComponentWrapper* soundComponent);
|
||||
|
||||
// OpenAL system variables
|
||||
ALCdevice* m_ALCdevice = nullptr;
|
||||
ALCcontext* m_ALCcontext = nullptr;
|
||||
|
||||
// Logic
|
||||
World* m_World = nullptr;
|
||||
EventBroker* m_EventBroker = nullptr;
|
||||
std::unordered_map<EntityID, Source*> m_Sources;
|
||||
float m_BGMVolumeChannel = 1.0f;
|
||||
float m_SFXVolumeChannel = 1.f;
|
||||
bool m_EditorEnabled = false;
|
||||
|
||||
// Events
|
||||
EventRelay<SoundSystem, Events::PlaySoundOnEntity> m_EPlaySoundOnEntity;
|
||||
bool OnPlaySoundOnEntity(const Events::PlaySoundOnEntity &e);
|
||||
EventRelay<SoundSystem, Events::PlaySoundOnPosition> m_EPlaySoundOnPosition;
|
||||
bool OnPlaySoundOnPosition(const Events::PlaySoundOnPosition &e);
|
||||
EventRelay<SoundSystem, Events::PlayBackgroundMusic> m_EPlayBackgroundMusic;
|
||||
bool OnPlayBackgroundMusic(const Events::PlayBackgroundMusic &e);
|
||||
EventRelay<SoundSystem, Events::PauseSound> m_EPauseSound;
|
||||
bool OnPauseSound(const Events::PauseSound &e);
|
||||
EventRelay<SoundSystem, Events::StopSound> m_EStopSound;
|
||||
bool OnStopSound(const Events::StopSound &e);
|
||||
EventRelay<SoundSystem, Events::ContinueSound> m_EContinueSound;
|
||||
bool OnContinueSound(const Events::ContinueSound &e);
|
||||
EventRelay<SoundSystem, Events::SetBGMGain> m_ESetBGMGain;
|
||||
bool OnSetBGMGain(const Events::SetBGMGain &e); // Not tested
|
||||
EventRelay<SoundSystem, Events::SetSFXGain> m_ESetSFXGain;
|
||||
bool OnSetSFXGain(const Events::SetSFXGain &e); // Not tested
|
||||
};
|
||||
|
||||
#endif
|
||||
+7
-2
@@ -27,6 +27,8 @@
|
||||
#include "Network/Server.h"
|
||||
#include "Network/Client.h"
|
||||
|
||||
// Sound
|
||||
#include "Sound/SoundSystem.h"
|
||||
|
||||
class Game
|
||||
{
|
||||
@@ -56,8 +58,11 @@ private:
|
||||
Network* m_ClientOrServer;
|
||||
bool m_IsClientOrServer = false;
|
||||
|
||||
EventRelay<Game, Events::InputCommand> m_EInputCommand;
|
||||
bool debugOnInputCommand(const Events::InputCommand& e);
|
||||
// Sound
|
||||
SoundSystem* m_SoundSystem;
|
||||
|
||||
//EventRelay<Game, Events::InputCommand> m_EInputCommand;
|
||||
//bool debugOnInputCommand(const Events::InputCommand& e);
|
||||
|
||||
void debugInitialize();
|
||||
void debugTick(double dt);
|
||||
|
||||
@@ -11,4 +11,6 @@
|
||||
<xs:include schemaLocation="Components/PointLight.xsd"/>
|
||||
<xs:include schemaLocation="Components/Trigger.xsd"/>
|
||||
<xs:include schemaLocation="Components/Health.xsd"/>
|
||||
<xs:include schemaLocation="Components/Listener.xsd"/>
|
||||
<xs:include schemaLocation="Components/SoundEmitter.xsd"/>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,3 @@
|
||||
<c:Listener>
|
||||
|
||||
</c:Listener>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:t="types">
|
||||
<xs:import schemaLocation="../Types.xsd" namespace="types"/>
|
||||
|
||||
<xs:element name="Listener">
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,9 @@
|
||||
<c:SoundEmitter>
|
||||
<FilePath></FilePath>
|
||||
<Gain>1.0</Gain>
|
||||
<Pitch>1.0</Pitch>
|
||||
<Loop>false</Loop>
|
||||
<MaxDistance>20.0</MaxDistance>
|
||||
<RollOffFactor>1.0</RollOffFactor>
|
||||
<ReferenceDistance>1.0</ReferenceDistance>
|
||||
</c:SoundEmitter>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:t="types">
|
||||
<xs:import schemaLocation="../Types.xsd" namespace="types"/>
|
||||
|
||||
<xs:element name="SoundEmitter">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="FilePath" type="t:string" minOccurs="0"/>
|
||||
<xs:element name="Gain" type="t:double" minOccurs="0"/>
|
||||
<xs:annotation><xs:documentation>The "volume" of the emitter. A value betweeen 0-1</xs:documentation></xs:annotation>
|
||||
<xs:element name="Pitch" type="t:double" minOccurs="0"/>
|
||||
<xs:annotation><xs:documentation>The pitch of the emitter. A value betweeen 0-1</xs:documentation></xs:annotation>
|
||||
<xs:element name="Loop" type="t:bool" minOccurs="0"/>
|
||||
<xs:annotation><xs:documentation>If the sound should loop or not.</xs:documentation></xs:annotation>
|
||||
<xs:element name="MaxDistance" type="t:double" minOccurs="0"/>
|
||||
<xs:annotation><xs:documentation>The distance where there will no longer be any attenuation.</xs:documentation></xs:annotation>
|
||||
<xs:element name="RollOffFactor" type="t:double" minOccurs="0"/>
|
||||
<xs:annotation><xs:documentation>The rolloff rate of the source.</xs:documentation></xs:annotation>
|
||||
<xs:element name="ReferenceDistance" type="t:double" minOccurs="0"/>
|
||||
<xs:annotation><xs:documentation>The distance that the source will be the loudest.</xs:documentation></xs:annotation>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<Entity xmlns:c="components" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../Types/Entity.xsd">
|
||||
|
||||
<Components>
|
||||
<c:Transform/>
|
||||
</Components>
|
||||
|
||||
<Children>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:SoundEmitter>
|
||||
<FilePath></FilePath>
|
||||
</c:SoundEmitter>
|
||||
<c:Model>
|
||||
<Resource>Models/Core/UnitCube.obj</Resource>
|
||||
<Color A="1" B="1" G="0.58431375" R="0.43921569"/>
|
||||
</c:Model>
|
||||
<c:Transform/>
|
||||
</Components>
|
||||
<Children/>
|
||||
</Entity>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Camera/>
|
||||
<c:Listener/>
|
||||
<c:Transform>
|
||||
<Position X="-1.81944144" Y="1.960464" Z="8.79182625"/>
|
||||
<Orientation X="-0.261799812" Y="-0.261786997" Z="-8.48482742e-08"/>
|
||||
</c:Transform>
|
||||
</Components>
|
||||
<Children/>
|
||||
</Entity>
|
||||
</Children>
|
||||
|
||||
</Entity>
|
||||
@@ -17,6 +17,8 @@
|
||||
<xs:element ref="c:Player" minOccurs="0"/>
|
||||
<xs:element ref="c:Health" minOccurs="0"/>
|
||||
<xs:element ref="c:PointLight" minOccurs="0"/>
|
||||
<xs:element ref="c:Listener" minOccurs="0"/>
|
||||
<xs:element ref="c:SoundEmitter" minOccurs="0"/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
@@ -9,8 +9,8 @@ find_package(ZLIB REQUIRED)
|
||||
find_package(PNG REQUIRED)
|
||||
find_package(Xerces REQUIRED)
|
||||
# Because FindOpenAL is retarded
|
||||
#set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "${CMAKE_SOURCE_DIR}/deps/include/AL")
|
||||
#find_package(OpenAL REQUIRED)
|
||||
set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "${CMAKE_SOURCE_DIR}/deps/include/OpenAL")
|
||||
find_package(OpenAL REQUIRED)
|
||||
if(UNIX)
|
||||
find_package(X11 REQUIRED)
|
||||
endif()
|
||||
@@ -52,6 +52,12 @@ file(GLOB SOURCE_FILES_Network
|
||||
)
|
||||
source_group(Network FILES ${SOURCE_FILES_Network})
|
||||
|
||||
file(GLOB SOURCE_FILES_Sound
|
||||
"${INCLUDE_PATH}/Sound/*.h"
|
||||
"Sound/*.cpp"
|
||||
)
|
||||
source_group(Sound FILES ${SOURCE_FILES_Sound})
|
||||
|
||||
file(GLOB SOURCE_FILES_Rendering
|
||||
"${INCLUDE_PATH}/Rendering/*.h"
|
||||
"Rendering/*.cpp"
|
||||
@@ -86,6 +92,7 @@ set(SOURCE_FILES
|
||||
${SOURCE_FILES_Core_Util}
|
||||
${SOURCE_FILES_Input}
|
||||
${SOURCE_FILES_Network}
|
||||
${SOURCE_FILES_Sound}
|
||||
${SOURCE_FILES_GUI}
|
||||
${SOURCE_FILES_Rendering}
|
||||
${SOURCE_FILES_Rendering_Util}
|
||||
|
||||
@@ -39,11 +39,17 @@ void RenderSystem::switchCamera(EntityID entity)
|
||||
if (m_World->HasComponent(m_CurrentCamera, "Model")) {
|
||||
m_World->GetComponent(m_CurrentCamera, "Model")["Visible"] = true;
|
||||
}
|
||||
if (m_World->HasComponent(m_CurrentCamera, "Listener")) {
|
||||
m_World->DeleteComponent(m_CurrentCamera, "Listener");
|
||||
}
|
||||
}
|
||||
|
||||
if (m_World->HasComponent(entity, "Model")) {
|
||||
m_World->GetComponent(entity, "Model")["Visible"] = false;
|
||||
}
|
||||
if (!m_World->HasComponent(entity, "Listener")) {
|
||||
m_World->AttachComponent(entity, "Listener");
|
||||
}
|
||||
m_CurrentCamera = entity;
|
||||
m_SwitchCamera = false;
|
||||
|
||||
@@ -162,6 +168,9 @@ void RenderSystem::updateCamera(World* world, double dt)
|
||||
{
|
||||
if (m_SwitchCamera) {
|
||||
auto cameras = world->GetComponents("Camera");
|
||||
if (cameras == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (auto it = cameras->begin(); it != cameras->end(); it++) {
|
||||
if ((*it).EntityID == m_CurrentCamera) {
|
||||
it++;
|
||||
@@ -173,11 +182,13 @@ void RenderSystem::updateCamera(World* world, double dt)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera");
|
||||
ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform");
|
||||
if (m_World->HasComponent(m_CurrentCamera, "Camera")) {
|
||||
ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera");
|
||||
ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform");
|
||||
|
||||
m_DebugCameraInputController->SetOrientation(glm::quat((glm::vec3)cameraTransform["Orientation"]));
|
||||
m_DebugCameraInputController->SetPosition(cameraTransform["Position"]);
|
||||
m_DebugCameraInputController->SetOrientation(glm::quat((glm::vec3)cameraTransform["Orientation"]));
|
||||
m_DebugCameraInputController->SetPosition(cameraTransform["Position"]);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_World->ValidEntity(m_CurrentCamera)) {
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
#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<EntityID, Source*>::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<EntityID, Source*>::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<SoundSystem>();
|
||||
addNewEmitters(dt); // can be optimized with "EEntityCreated"
|
||||
deleteInactiveEmitters(); // can be optimized with "EEntityDeleted"
|
||||
updateEmitters( dt);
|
||||
updateListener( dt);
|
||||
}
|
||||
|
||||
void SoundSystem::deleteInactiveEmitters()
|
||||
{
|
||||
std::unordered_map<EntityID, Source*>::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<EntityID, Source*>::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<EntityID, Source*>::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;
|
||||
(bool)(it->second->Type) ? gain = m_SFXVolumeChannel : 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<Sound>((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<Sound>(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.obj";
|
||||
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.");
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -7,6 +7,7 @@
|
||||
Game::Game(int argc, char* argv[])
|
||||
{
|
||||
ResourceManager::RegisterType<ConfigFile>("ConfigFile");
|
||||
ResourceManager::RegisterType<Sound>("Sound");
|
||||
ResourceManager::RegisterType<Model>("Model");
|
||||
ResourceManager::RegisterType<RawModel>("RawModel");
|
||||
ResourceManager::RegisterType<Texture>("Texture");
|
||||
@@ -83,12 +84,17 @@ Game::Game(int argc, char* argv[])
|
||||
//boost::thread workerThread(&Game::networkFunction, this);
|
||||
networkFunction();
|
||||
}
|
||||
|
||||
// Invoke sound system
|
||||
m_SoundSystem = new SoundSystem(m_World, m_EventBroker, m_Config->Get<bool>("Debug.EditorEnabled", false));
|
||||
|
||||
m_LastTime = glfwGetTime();
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
delete m_SystemPipeline;
|
||||
delete m_SoundSystem;
|
||||
delete m_World;
|
||||
delete m_FrameStack;
|
||||
delete m_InputProxy;
|
||||
@@ -122,9 +128,10 @@ void Game::Tick()
|
||||
}
|
||||
// Iterate through systems and update world!
|
||||
m_SystemPipeline->Update(m_World, dt);
|
||||
debugTick(dt);
|
||||
m_Renderer->Update(dt);
|
||||
m_EventBroker->Process<Client>();
|
||||
|
||||
m_SoundSystem->Update(dt);
|
||||
GLERROR("Game::Tick m_RenderQueueFactory->Update");
|
||||
m_Renderer->Draw(*m_RenderFrame);
|
||||
GLERROR("Game::Tick m_Renderer->Draw");
|
||||
|
||||
Reference in New Issue
Block a user