Page MenuHomeWildfire Games

WIP Ambient emitters (#1962)
ActivePublic

Authored by Sandarac on May 3 2017, 8:24 AM.
Index: source/simulation2/components/CCmpSoundManager.cpp
===================================================================
--- source/simulation2/components/CCmpSoundManager.cpp (revision 19503)
+++ source/simulation2/components/CCmpSoundManager.cpp (working copy)
@@ -92,6 +92,36 @@
g_SoundManager->Pause(true);
}
+ virtual void AddAmbientEmitter(const std::wstring& name, entity_id_t source)
+ {
+ if (!g_SoundManager)
+ return;
+
+ CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), source);
+ if (!cmpPosition || !cmpPosition->IsInWorld())
+ return;
+
+ g_SoundManager->AddAmbientEmitter(name, CVector3D(cmpPosition->GetPosition()), source);
+ }
+
+ virtual void RemoveAmbientEmitter(entity_id_t source)
+ {
+ if (!g_SoundManager)
+ return;
+ g_SoundManager->RemoveAmbientEmitter(source);
+ }
+
+ virtual void UpdateAmbientEmitterPosition(entity_id_t source)
+ {
+ if (!g_SoundManager)
+ return;
+
+ CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), source);
+ if (!cmpPosition || !cmpPosition->IsInWorld())
+ return;
+
+ g_SoundManager->UpdateAmbientEmitterPosition(source, CVector3D(cmpPosition->GetPosition()));
+ }
};
REGISTER_COMPONENT_TYPE(SoundManager)
Index: source/simulation2/components/ICmpSoundManager.cpp
===================================================================
--- source/simulation2/components/ICmpSoundManager.cpp (revision 19503)
+++ source/simulation2/components/ICmpSoundManager.cpp (working copy)
@@ -23,5 +23,8 @@
BEGIN_INTERFACE_WRAPPER(SoundManager)
DEFINE_INTERFACE_METHOD_2("PlaySoundGroup", void, ICmpSoundManager, PlaySoundGroup, std::wstring, entity_id_t)
+DEFINE_INTERFACE_METHOD_2("AddAmbientEmitter", void, ICmpSoundManager, AddAmbientEmitter, std::wstring, entity_id_t)
+DEFINE_INTERFACE_METHOD_1("RemoveAmbientEmitter", void, ICmpSoundManager, RemoveAmbientEmitter, entity_id_t)
+DEFINE_INTERFACE_METHOD_1("UpdateAmbientEmitterPosition", void, ICmpSoundManager, UpdateAmbientEmitterPosition, entity_id_t)
DEFINE_INTERFACE_METHOD_0("StopMusic", void, ICmpSoundManager, StopMusic)
END_INTERFACE_WRAPPER(SoundManager)
Index: source/simulation2/components/ICmpSoundManager.h
===================================================================
--- source/simulation2/components/ICmpSoundManager.h (revision 19503)
+++ source/simulation2/components/ICmpSoundManager.h (working copy)
@@ -33,6 +33,12 @@
*/
virtual void PlaySoundGroup(const std::wstring& name, entity_id_t source) = 0;
+ virtual void AddAmbientEmitter(const std::wstring& name, entity_id_t source) = 0;
+
+ virtual void RemoveAmbientEmitter(entity_id_t source) = 0;
+
+ virtual void UpdateAmbientEmitterPosition(entity_id_t source) = 0;
+
virtual void StopMusic() = 0;
DECLARE_INTERFACE_TYPE(SoundManager)
Index: source/soundmanager/SoundManager.cpp
===================================================================
--- source/soundmanager/SoundManager.cpp (revision 19503)
+++ source/soundmanager/SoundManager.cpp (working copy)
@@ -24,11 +24,13 @@
#include "items/CSoundItem.h"
#include "items/CStreamItem.h"
+#include "graphics/GameView.h"
#include "lib/external_libraries/libsdl.h"
#include "ps/CLogger.h"
#include "ps/CStr.h"
#include "ps/ConfigDB.h"
#include "ps/Filesystem.h"
+#include "ps/Game.h"
#include "ps/Profiler2.h"
#include "ps/XML/Xeromyces.h"
@@ -587,6 +589,13 @@
if (m_CurrentEnvirons)
m_CurrentEnvirons->EnsurePlay();
+ if (g_Game && g_Game->GetView())
+ for (SAmbientEmitterData& ambientEmitter : m_AmbientEmitters)
+ {
+ ambientEmitter.soundItem->EnsurePlay();
+ ambientEmitter.soundItem->SetGain(GetAmbientEmitterGain(ambientEmitter.position));
+ }
+
if (m_Worker)
m_Worker->CleanupItems();
}
@@ -665,6 +674,62 @@
group->PlayNext(sourcePos, source);
}
+float CSoundManager::GetAmbientEmitterGain(CVector3D& emitterPos)
+{
+ CVector3D cameraPos(g_Game->GetView()->GetCameraPosX(), g_Game->GetView()->GetCameraPosY(), g_Game->GetView()->GetCameraPosZ());
+ float itemDist = (emitterPos - cameraPos).Length();
+ return std::max(1.0f - itemDist / 200.f, 0.f); // TODO: Use something better
+}
+
+// TODO: Properly handle SoundGroups instead
+void CSoundManager::AddAmbientEmitter(const VfsPath& groupPath, CVector3D& sourcePos, entity_id_t source)
+{
+ if (!m_Enabled)
+ return;
+
+ ISoundItem* ambientItem = LoadItem(groupPath);
+ if (ambientItem == NULL)
+ return;
+
+ // Use the ambient gain config option for ambient emitters
+ if (m_AmbientGain > 0)
+ {
+ ambientItem->SetGain(0);
+ ambientItem->PlayLoop();
+ ambientItem->FadeToIn(GetAmbientEmitterGain(sourcePos), 2.00);
+ }
+
+ SAmbientEmitterData ambientEmitter;
+ ambientEmitter.soundItem = ambientItem;
+ ambientEmitter.position = sourcePos;
+ ambientEmitter.id = source;
+
+ m_AmbientEmitters.emplace_back(ambientEmitter);
+}
+
+void CSoundManager::RemoveAmbientEmitter(entity_id_t source)
+{
+ // TODO: Make this function cleaner
+ for (SAmbientEmitterData& ambientEmitter : m_AmbientEmitters)
+ if (ambientEmitter.id == source)
+ ambientEmitter.soundItem->FadeAndDelete(3.00);
+
+ m_AmbientEmitters.erase(
+ std::remove_if(m_AmbientEmitters.begin(), m_AmbientEmitters.end(),
+ [&](SAmbientEmitterData& emitterData)
+ {
+ return emitterData.id == source;
+ }
+ ), m_AmbientEmitters.end());
+}
+
+void CSoundManager::UpdateAmbientEmitterPosition(entity_id_t source, CVector3D& sourcePos)
+{
+ for (SAmbientEmitterData& ambientEmitter : m_AmbientEmitters)
+ if (ambientEmitter.id == source)
+ ambientEmitter.position = sourcePos;
+}
+
void CSoundManager::PlayAsMusic(const VfsPath& itemPath, bool looping)
{
if (m_Enabled)
@@ -713,6 +778,7 @@
PauseMusic(pauseIt);
PauseAmbient(pauseIt);
PauseAction(pauseIt);
+ PauseAmbientEmitters(pauseIt);
}
void CSoundManager::PauseMusic(bool pauseIt)
@@ -740,6 +806,13 @@
m_AmbientPaused = pauseIt;
}
+void CSoundManager::PauseAmbientEmitters(bool pause)
+{
+ for (SAmbientEmitterData& emitterData : m_AmbientEmitters)
+ if (emitterData.soundItem)
+ pause ? emitterData.soundItem->Pause() : emitterData.soundItem->Resume();
+}
+
void CSoundManager::PauseAction(bool pauseIt)
{
m_ActionPaused = pauseIt;
Index: source/soundmanager/SoundManager.h
===================================================================
--- source/soundmanager/SoundManager.h (revision 19503)
+++ source/soundmanager/SoundManager.h (working copy)
@@ -63,6 +63,12 @@
ALSourceHolder* m_ALSourceBuffer;
ISoundItem* m_CurrentTune;
ISoundItem* m_CurrentEnvirons;
+ struct SAmbientEmitterData {
+ ISoundItem* soundItem;
+ CVector3D position;
+ entity_id_t id;
+ };
+ std::vector<SAmbientEmitterData> m_AmbientEmitters;
CSoundManagerWorker* m_Worker;
CMutex m_DistressMutex;
PlayList* m_PlayListItems;
@@ -148,6 +154,7 @@
void Pause(bool pauseIt);
void PauseMusic(bool pauseIt);
void PauseAmbient(bool pauseIt);
+ void PauseAmbientEmitters(bool pause);
void PauseAction(bool pauseIt);
void SetAmbientItem(ISoundItem* anItem);
@@ -157,6 +164,11 @@
void SetActionGain(float gain);
void SetUIGain(float gain);
+ float CSoundManager::GetAmbientEmitterGain(CVector3D& emitterPos);
+ void AddAmbientEmitter(const VfsPath& groupPath, CVector3D& sourcePos, entity_id_t source);
+ void RemoveAmbientEmitter(entity_id_t source);
+ void UpdateAmbientEmitterPosition(entity_id_t source, CVector3D& sourcePos);
+
protected:
void InitListener();
Status AlcInit();
Index: source/soundmanager/ISoundManager.h
===================================================================
--- source/soundmanager/ISoundManager.h (revision 19503)
+++ source/soundmanager/ISoundManager.h (working copy)
@@ -49,6 +49,10 @@
virtual void PlayAsAmbient(const VfsPath& itemPath, bool looping) = 0;
virtual void PlayAsGroup(const VfsPath& groupPath, CVector3D sourcePos, entity_id_t source, bool ownedSound) = 0;
+ virtual void AddAmbientEmitter(const VfsPath& groupPath, CVector3D& sourcePos, entity_id_t source) = 0;
+ virtual void RemoveAmbientEmitter(entity_id_t source) = 0;
+ virtual void UpdateAmbientEmitterPosition(entity_id_t source, CVector3D& sourcePos) = 0;
+
virtual bool InDistress() = 0;
};
Index: binaries/data/mods/public/simulation/components/Sound.js
===================================================================
--- binaries/data/mods/public/simulation/components/Sound.js (revision 19503)
+++ binaries/data/mods/public/simulation/components/Sound.js (working copy)
@@ -8,6 +8,7 @@
"<run>actor/human/movement/walk.xml</run>" +
"<attack>attack/weapon/sword.xml</attack>" +
"<death>actor/human/death/death.xml</death>" +
+ "<ambientEmitter>ambient/water/river_slow.xml</ambientEmitter>" +
"</SoundGroups>" +
"</a:example>" +
"<element name='SoundGroups'>" +
@@ -21,6 +22,12 @@
Sound.prototype.Init = function()
{
+ this.hasAmbientEmitter = !!this.template.SoundGroups.ambientEmitter;
+ if (this.hasAmbientEmitter)
+ {
+ let cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager);
+ cmpSoundManager.AddAmbientEmitter(this.template.SoundGroups.ambientEmitter, this.entity);
+ }
};
Sound.prototype.Serialize = null; // we have no dynamic state to save
@@ -50,4 +57,23 @@
}
};
+Sound.prototype.OnOwnershipChanged = function(msg)
+{
+ if (!this.hasAmbientEmitter || msg.to != -1)
+ return;
+
+ let cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager);
+ cmpSoundManager.RemoveAmbientEmitter(this.entity);
+};
+
+// This may be performace-intensive for moving ents with ambient emitters
+Sound.prototype.OnPositionChanged = function(msg)
+{
+ if (!this.hasAmbientEmitter || !msg.inWorld)
+ return;
+
+ let cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager);
+ cmpSoundManager.UpdateAmbientEmitterPosition(this.entity);
+};
+
Engine.RegisterComponentType(IID_Sound, "Sound", Sound);

Event Timeline

Sandarac created this paste.May 3 2017, 8:24 AM
Sandarac created this object with visibility "Public (No Login Required)".

Right now, this only supports ambient emitters that loop continuously, like the sound of waves, and it doesn't take into account the FOW. The gain of the emitter depends on how close it is to the camera.

elexis added a subscriber: elexis.May 3 2017, 11:07 AM

It's going to be placed with a nonbroken version of marker_object_sound.xml in atlas / random mapgen script?
How will we specify the soundfile it plays? Typically all values come predefined with the template, which is also the reason why we only have so few trigger points :/
That thing shouldn't have ownership I guess

Should most likely be const CVector3D& instead of a non-const &.

If you are using emplace pass it the parameters for the ctor you want (or in that case those for aggregate initialization), making it call a copy ctor seems pointless.

I'd indent that lambda, but so far I still haven't found a way to write those (in C++) in a way that doesn't hurt my eyes.

The CmpSoundManager functions could be const (but I didn't do that, because doing that while changing things on that global seemed like lying). Maybe there's a way to do that without the global, eg storing a reference to it in that component, but that also sounds ugly.

GetAmbientEmitterGain should be const.

Is adding multiple emitters for one source entity going to be common, or should we provide another id in those cases, so we can enable/disable specific ones?

Sandarac added a comment.EditedMay 5 2017, 8:16 AM

Thanks for the comments!

In P41#285, @leper wrote:

Is adding multiple emitters for one source entity going to be common, or should we provide another id in those cases, so we can enable/disable specific ones?

I'm not sure if it should be possible to have multiple emitters for one entity, I think it is design decision and from my point of view I'm really not sure.

The idea is that an ambient emitter can be specified as a SoundGroup in the XML, which could possible specify things like cool-down times or constant looping, but this might need a lot of modifications to SoundGroup.cpp.

Handling FOW for ambient emitters may also be rather non-trivial.

I was mostly wondering if we couldn't simplify the removal function, and in any way the code should either explicitly support multiple emitters per entity, or verify that there is at most one.

(Maybe I should read up on what happened to that unstable_remove proposal from SG14. Roughly the thing that could be done would be checking if we are removing something apart from the last element, if yes moving .back() into that spot, and doing .pop_back(). (Maybe one could move that thing to the same place, but that sounds slightly questionable, read the standard if you want to convince yourself that that is fine (if you do, point me to the relevant parts).))

For FoW/SoD that seems like an issue for all sounds, nay?

vladislavbelov added a subscriber: vladislavbelov.EditedMay 5 2017, 10:43 AM

Does it make sense to use vector? I didn't find using index here. So you could replace it with list, because erase in vector is O(n).
But it also depends on how often we delete emitter. If not frequently then vector could be enough.