diff --git a/src/xrEngine/IGame_Level.cpp b/src/xrEngine/IGame_Level.cpp index d075673ec2c..b0f99fc3d3b 100644 --- a/src/xrEngine/IGame_Level.cpp +++ b/src/xrEngine/IGame_Level.cpp @@ -25,6 +25,8 @@ IGame_Level::IGame_Level() bReady = false; pCurrentEntity = NULL; pCurrentViewEntity = NULL; + Sound = GEnv.Sound->create_scene(); + DefaultSoundScene = Sound; #ifndef MASTER_GOLD GEnv.Render->ResourcesDumpMemoryUsage(); #endif @@ -44,8 +46,8 @@ IGame_Level::~IGame_Level() Device.seqFrame.Remove(this); CCameraManager::ResetPP(); /////////////////////////////////////////// - GEnv.Sound->set_geometry_occ(nullptr); - GEnv.Sound->set_handler(nullptr); + DefaultSoundScene = g_pGamePersistent->m_pSound; + GEnv.Sound->destroy_scene(Sound); #ifndef MASTER_GOLD GEnv.Render->ResourcesDumpMemoryUsage(); #endif @@ -115,8 +117,8 @@ bool IGame_Level::Load(u32 dwNum) g_pGamePersistent->SpatialSpace.initialize(ObjectSpace.GetBoundingVolume()); g_pGamePersistent->SpatialSpacePhysic.initialize(ObjectSpace.GetBoundingVolume()); - GEnv.Sound->set_geometry_occ(ObjectSpace.GetStaticModel()); - GEnv.Sound->set_handler([](const ref_sound& S, float range) + Sound->set_geometry_occ(ObjectSpace.GetStaticModel()); + Sound->set_handler([](const ref_sound& S, float range) { if (g_pGameLevel && S && S->feedback) g_pGameLevel->SoundEvent_Register(S, range); @@ -303,7 +305,7 @@ void IGame_Level::SoundEvent_Register(const ref_sound& S, float range) // Energy and signal VERIFY(_valid(it->GetSpatialData().sphere.P)); - float dist = snd_position.distance_to(it->GetSpatialData().sphere.P); + const float dist = snd_position.distance_to(it->GetSpatialData().sphere.P); if (dist > p->max_ai_distance) continue; VERIFY(_valid(dist)); @@ -312,7 +314,7 @@ void IGame_Level::SoundEvent_Register(const ref_sound& S, float range) VERIFY(_valid(Power)); if (Power > EPS_S) { - float occ = GEnv.Sound->get_occlusion_to(it->GetSpatialData().sphere.P, snd_position); + const float occ = Sound->get_occlusion_to(it->GetSpatialData().sphere.P, snd_position); VERIFY(_valid(occ)); Power *= occ; if (Power > EPS_S) diff --git a/src/xrEngine/IGame_Level.h b/src/xrEngine/IGame_Level.h index 66523e8d9ac..6de9655221a 100644 --- a/src/xrEngine/IGame_Level.h +++ b/src/xrEngine/IGame_Level.h @@ -75,6 +75,8 @@ class ENGINE_API IGame_Level : public FactoryObjectBase, xr_vector snd_ER; public: + ISoundScene* Sound{}; + CObjectList Objects; CObjectSpace ObjectSpace; CCameraManager& Cameras() { return *m_pCameras; }; diff --git a/src/xrEngine/IGame_Persistent.cpp b/src/xrEngine/IGame_Persistent.cpp index 9ddb6937b9f..89f9f6e47da 100644 --- a/src/xrEngine/IGame_Persistent.cpp +++ b/src/xrEngine/IGame_Persistent.cpp @@ -44,10 +44,16 @@ IGame_Persistent::IGame_Persistent() pEnvironment = xr_new(); m_pGShaderConstants = xr_new(); //--#SM+#-- + + m_pSound = GEnv.Sound->create_scene(); + DefaultSoundScene = m_pSound; } IGame_Persistent::~IGame_Persistent() { + GEnv.Sound->destroy_scene(m_pSound); + DefaultSoundScene = nullptr; + xr_delete(PerlinNoise1D); Device.seqFrame.Remove(this); Device.seqAppStart.Remove(this); diff --git a/src/xrEngine/IGame_Persistent.h b/src/xrEngine/IGame_Persistent.h index d8a4fb97616..16977d95b7b 100644 --- a/src/xrEngine/IGame_Persistent.h +++ b/src/xrEngine/IGame_Persistent.h @@ -147,6 +147,7 @@ class ENGINE_API IGame_Persistent : CEnvironment& Environment() { return *pEnvironment; }; void Prefetch(); #endif + ISoundScene* m_pSound{}; IMainMenu* m_pMainMenu{}; static bool IsMainMenuActive(); static bool MainMenuActiveOrLevelNotExist(); diff --git a/src/xrEngine/xr_object_list.cpp b/src/xrEngine/xr_object_list.cpp index 4be4216e140..aacfa149cff 100644 --- a/src/xrEngine/xr_object_list.cpp +++ b/src/xrEngine/xr_object_list.cpp @@ -311,7 +311,7 @@ void CObjectList::Update(bool bForce) (*oit)->net_Relcase(destroy_queue[it]); for (int it = destroy_queue.size() - 1; it >= 0; it--) - GEnv.Sound->object_relcase(destroy_queue[it]); + g_pGameLevel->Sound->object_relcase(destroy_queue[it]); RELCASE_CALLBACK_VEC::iterator it = m_relcase_callbacks.begin(); const RELCASE_CALLBACK_VEC::iterator ite = m_relcase_callbacks.end(); diff --git a/src/xrGame/Level_load.cpp b/src/xrGame/Level_load.cpp index b1c6776b38a..b2b7c8cf44e 100644 --- a/src/xrGame/Level_load.cpp +++ b/src/xrGame/Level_load.cpp @@ -90,14 +90,14 @@ bool CLevel::Load_GameSpecific_After() if (FS.exist(fn_game, "$level$", "level.snd_env")) { IReader* F = FS.r_open(fn_game); - GEnv.Sound->set_geometry_env(F); + Sound->set_geometry_env(F); FS.r_close(F); } // loading SOM if (FS.exist(fn_game, "$level$", "level.som")) { IReader* F = FS.r_open(fn_game); - GEnv.Sound->set_geometry_som(F); + Sound->set_geometry_som(F); FS.r_close(F); } diff --git a/src/xrGame/level_sounds.cpp b/src/xrGame/level_sounds.cpp index 017dfcacfcb..68fcd0ebd78 100644 --- a/src/xrGame/level_sounds.cpp +++ b/src/xrGame/level_sounds.cpp @@ -34,7 +34,7 @@ void SStaticSound::Update(u32 game_time, u32 global_time) if (0 == m_Source._feedback()) { Fvector occ[3]; - const float occluder_volume = GEnv.Sound->get_occlusion(m_Position, .2f, occ); + const float occluder_volume = g_pGameLevel->Sound->get_occlusion(m_Position, .2f, occ); const float vol = m_Volume * occluder_volume; if ((0 == m_PauseTime.x) && (0 == m_PauseTime.y)) diff --git a/src/xrSound/Sound.cpp b/src/xrSound/Sound.cpp index 3a1539eb0a9..f26d697a69e 100644 --- a/src/xrSound/Sound.cpp +++ b/src/xrSound/Sound.cpp @@ -4,6 +4,8 @@ XRSOUND_API u32 snd_device_id = u32(-1); +ISoundScene* DefaultSoundScene{}; + void CSoundManager::CreateDevicesList() { static bool noSound = strstr(Core.Params, "-nosound"); diff --git a/src/xrSound/Sound.h b/src/xrSound/Sound.h index d12dce13361..2a7271246f1 100644 --- a/src/xrSound/Sound.h +++ b/src/xrSound/Sound.h @@ -24,6 +24,7 @@ constexpr pcstr SNDENV_FILENAME = "sEnvironment.xr"; class IGameObject; class CSound; struct resptrcode_sound; +class ISoundScene; class XRSOUND_API CSound_params; class XRSOUND_API CSound_source; class XRSOUND_API CSound_emitter; @@ -52,6 +53,8 @@ XRSOUND_API extern int psSoundCacheSizeMB; XRSOUND_API extern u32 psSoundPrecacheAll; XRSOUND_API extern u32 snd_device_id; +XRSOUND_API extern ISoundScene* DefaultSoundScene; + // Flags enum : u32 { @@ -200,6 +203,40 @@ namespace CDB class MODEL; } +class XRSOUND_API XR_NOVTABLE ISoundScene +{ +protected: + friend struct resptrcode_sound; + +public: + virtual ~ISoundScene() = 0; + + virtual void play(ref_sound& S, IGameObject* O, u32 flags = 0, float delay = 0.f) = 0; + virtual void play_at_pos(ref_sound& S, IGameObject* O, const Fvector& pos, u32 flags = 0, float delay = 0.f) = 0; + virtual void play_no_feedback(ref_sound& S, IGameObject* O, u32 flags = 0, float delay = 0.f, Fvector* pos = nullptr, + float* vol = nullptr, float* freq = nullptr, Fvector2* range = nullptr) = 0; + + virtual void stop_emitters() const = 0; + virtual int pause_emitters(bool pauseState) = 0; + + virtual void set_handler(sound_event* E) = 0; + virtual void set_geometry_env(IReader* I) = 0; + virtual void set_geometry_som(IReader* I) = 0; + virtual void set_geometry_occ(CDB::MODEL* M) = 0; + + virtual void set_user_env(CSound_environment* E) = 0; + virtual void set_environment(u32 id, CSound_environment** dst_env) = 0; + virtual void set_environment_size(CSound_environment* src_env, CSound_environment** dst_env) = 0; + virtual CSound_environment* get_environment(const Fvector& P) = 0; + + virtual float get_occlusion_to(const Fvector& hear_pt, const Fvector& snd_pt, float dispersion = 0.2f) = 0; + virtual float get_occlusion(Fvector& P, float R, Fvector* occ) = 0; + + virtual void object_relcase(IGameObject* obj) = 0; +}; + +inline ISoundScene::~ISoundScene() = default; + /// definition (Sound Manager Interface) class XRSOUND_API XR_NOVTABLE ISoundManager { @@ -212,43 +249,30 @@ class XRSOUND_API XR_NOVTABLE ISoundManager virtual void attach_tail(CSound& S, pcstr fName) = 0; - virtual void play(ref_sound& S, IGameObject* O, u32 flags = 0, float delay = 0.f) = 0; - virtual void play_at_pos(ref_sound& S, IGameObject* O, const Fvector& pos, u32 flags = 0, float delay = 0.f) = 0; - virtual void play_no_feedback(ref_sound& S, IGameObject* O, u32 flags = 0, float delay = 0.f, Fvector* pos = nullptr, - float* vol = nullptr, float* freq = nullptr, Fvector2* range = nullptr) = 0; - public: virtual ~ISoundManager() = default; + virtual ISoundScene* create_scene() = 0; + virtual void destroy_scene(ISoundScene*&) = 0; + virtual void _restart() = 0; virtual bool i_locked() = 0; virtual void prefetch() = 0; virtual void stop_emitters() = 0; - virtual int pause_emitters(bool val) = 0; + virtual int pause_emitters(bool pauseState) = 0; virtual void set_master_volume(float f = 1.f) = 0; - virtual void set_geometry_env(IReader* I) = 0; - virtual void set_geometry_som(IReader* I) = 0; - virtual void set_geometry_occ(CDB::MODEL* M) = 0; - virtual void set_handler(sound_event* E) = 0; virtual void update(const Fvector& P, const Fvector& D, const Fvector& N) = 0; virtual void statistic(CSound_stats* s0, CSound_stats_ext* s1) = 0; virtual void DumpStatistics(class IGameFont& font, class IPerformanceAlert* alert) = 0; - virtual float get_occlusion_to(const Fvector& hear_pt, const Fvector& snd_pt, float dispersion = 0.2f) = 0; - virtual float get_occlusion(Fvector& P, float R, Fvector* occ) = 0; - - virtual void object_relcase(IGameObject* obj) = 0; virtual const Fvector& listener_position() = 0; virtual void refresh_env_library() = 0; - virtual void set_user_env(CSound_environment* E) = 0; virtual void refresh_sources() = 0; - virtual void set_environment(u32 id, CSound_environment** dst_env) = 0; - virtual void set_environment_size(CSound_environment* src_env, CSound_environment** dst_env) = 0; }; class XRSOUND_API CSoundManager @@ -331,7 +355,6 @@ struct resptrcode_sound : public resptr_base [[nodiscard]] ICF CSound_UserDataPtr _g_userdata() const { VERIFY(p_); return p_ ? p_->g_userdata : nullptr; } - bool create(pcstr name, esound_type sound_type, int game_type, bool replaceWithNoSound = true) { VerSndUnlocked(); @@ -369,20 +392,26 @@ struct resptrcode_sound : public resptr_base void play(IGameObject* O, u32 flags = 0, float delay = 0.f) { + if (!p_ || !DefaultSoundScene) + return; VerSndUnlocked(); - GEnv.Sound->play(static_cast(*this), O, flags, delay); + DefaultSoundScene->play(static_cast(*this), O, flags, delay); } void play_at_pos(IGameObject* O, const Fvector& pos, u32 flags = 0, float delay = 0.f) { + if (!p_ || !DefaultSoundScene) + return; VerSndUnlocked(); - GEnv.Sound->play_at_pos(static_cast(*this), O, pos, flags, delay); + DefaultSoundScene->play_at_pos(static_cast(*this), O, pos, flags, delay); } void play_no_feedback(IGameObject* O, u32 flags = 0, float delay = 0.f, Fvector* pos = nullptr, float* vol = nullptr, float* freq = nullptr, Fvector2* range = nullptr) { + if (!p_ || !DefaultSoundScene) + return; VerSndUnlocked(); - GEnv.Sound->play_no_feedback(static_cast(*this), O, flags, delay, pos, vol, freq, range); + DefaultSoundScene->play_no_feedback(static_cast(*this), O, flags, delay, pos, vol, freq, range); } ICF void stop() const { VerSndUnlocked(); if (_feedback()) _feedback()->stop(false); } diff --git a/src/xrSound/SoundRender_Core.cpp b/src/xrSound/SoundRender_Core.cpp index cf0a5771a99..910363d4a78 100644 --- a/src/xrSound/SoundRender_Core.cpp +++ b/src/xrSound/SoundRender_Core.cpp @@ -38,11 +38,6 @@ CSoundRender_Core::CSoundRender_Core(CSoundManager& p) : Parent(p) { bPresent = false; - bUserEnvironment = false; - geom_MODEL = nullptr; - geom_ENV = nullptr; - geom_SOM = nullptr; - Handler = nullptr; s_targets_pu = 0; s_emitters_u = 0; e_current.set_identity(); @@ -54,13 +49,6 @@ CSoundRender_Core::CSoundRender_Core(CSoundManager& p) fTimer_Delta = 0.0f; fTimerPersistent_Value = TimerPersistent.GetElapsed_sec(); fTimerPersistent_Delta = 0.0f; - m_iPauseCounter = 1; -} - -CSoundRender_Core::~CSoundRender_Core() -{ - xr_delete(geom_ENV); - xr_delete(geom_SOM); } void CSoundRender_Core::_initialize() @@ -91,138 +79,39 @@ void CSoundRender_Core::_clear() } s_sources.clear(); - // remove emitters - for (auto& emit : s_emitters) - xr_delete(emit); - s_emitters.clear(); - g_target_temp_data.clear(); } -void CSoundRender_Core::stop_emitters() +ISoundScene* CSoundRender_Core::create_scene() { - for (auto& emit : s_emitters) - emit->stop(false); + return m_scenes.emplace_back(xr_new()); } -int CSoundRender_Core::pause_emitters(bool val) +void CSoundRender_Core::destroy_scene(ISoundScene*& sound_scene) { - m_iPauseCounter += val ? +1 : -1; - VERIFY(m_iPauseCounter >= 0); - - for (auto& emit : s_emitters) - static_cast(emit)->pause(val, val ? m_iPauseCounter : m_iPauseCounter + 1); - - return m_iPauseCounter; + m_scenes.erase(std::remove(m_scenes.begin(), m_scenes.end(), sound_scene), m_scenes.end()); + xr_delete(sound_scene); } -void CSoundRender_Core::_restart() +void CSoundRender_Core::stop_emitters() { - cache.destroy(); - cache.initialize(psSoundCacheSizeMB * 1024, cache_bytes_per_line); - env_apply(); + for (const auto& scene : m_scenes) + scene->stop_emitters(); } -void CSoundRender_Core::set_handler(sound_event* E) { Handler = E; } -void CSoundRender_Core::set_geometry_occ(CDB::MODEL* M) { geom_MODEL = M; } - -void CSoundRender_Core::set_geometry_som(IReader* I) +int CSoundRender_Core::pause_emitters(bool pauseState) { - xr_delete(geom_SOM); - if (nullptr == I) - return; - - // check version - R_ASSERT(I->find_chunk(0)); - [[maybe_unused]] u32 version = I->r_u32(); - VERIFY2(version == 0, "Invalid SOM version"); - - struct SOM_poly - { - Fvector3 v1; - Fvector3 v2; - Fvector3 v3; - u32 b2sided; - float occ; - }; - - CDB::Collector CL; - { - // load geometry - IReader* geom = I->open_chunk(1); - VERIFY2(geom, "Corrupted SOM file"); - if (!geom) - return; - - // Load tris and merge them - const auto begin = static_cast(geom->pointer()); - const auto end = static_cast(geom->end()); - for (SOM_poly* poly = begin; poly != end; ++poly) - { - CL.add_face_packed_D(poly->v1, poly->v2, poly->v3, *(u32*)&poly->occ, 0.01f); - if (poly->b2sided) - CL.add_face_packed_D(poly->v3, poly->v2, poly->v1, *(u32*)&poly->occ, 0.01f); - } - geom->close(); - } - - // Create AABB-tree - geom_SOM = xr_new(); - geom_SOM->build(CL.getV(), int(CL.getVS()), CL.getT(), int(CL.getTS())); + int cnt = 0; + for (const auto& scene : m_scenes) + cnt += scene->pause_emitters(pauseState); + return cnt; } -void CSoundRender_Core::set_geometry_env(IReader* I) +void CSoundRender_Core::_restart() { - xr_delete(geom_ENV); - if (nullptr == I) - return; - const auto envLib = Parent.get_env_library(); - if (!envLib) - return; - - // Associate names - xr_vector ids; - IReader* names = I->open_chunk(0); - while (!names->eof()) - { - string256 n; - names->r_stringZ(n, sizeof(n)); - int id = envLib->GetID(n); - R_ASSERT(id >= 0); - ids.push_back((u16)id); - } - names->close(); - - // Load geometry - IReader* geom_ch = I->open_chunk(1); - - u8* _data = (u8*)xr_malloc(geom_ch->length()); - - memcpy(_data, geom_ch->pointer(), geom_ch->length()); - - IReader* geom = xr_new(_data, geom_ch->length(), 0); - - hdrCFORM H; - geom->r(&H, sizeof(hdrCFORM)); - Fvector* verts = (Fvector*)geom->pointer(); - CDB::TRI* tris = (CDB::TRI*)(verts + H.vertcount); - for (u32 it = 0; it < H.facecount; it++) - { - CDB::TRI* T = tris + it; - u16 id_front = (u16)((T->dummy & 0x0000ffff) >> 0); // front face - u16 id_back = (u16)((T->dummy & 0xffff0000) >> 16); // back face - R_ASSERT(id_front < (u16)ids.size()); - R_ASSERT(id_back < (u16)ids.size()); - T->dummy = u32(ids[id_back] << 16) | u32(ids[id_front]); - } - geom_ENV = xr_new(); - geom_ENV->build(verts, H.vertcount, tris, H.facecount); -#ifdef _EDITOR // XXX: may be we are interested in applying env in the game build too? + cache.destroy(); + cache.initialize(psSoundCacheSizeMB * 1024, cache_bytes_per_line); env_apply(); -#endif - geom_ch->close(); - geom->close(); - xr_free(_data); } CSound* CSoundRender_Core::create(pcstr fName, esound_type sound_type, int game_type, bool replaceWithNoSound /*= true*/) @@ -287,71 +176,6 @@ void CSoundRender_Core::attach_tail(CSound& snd, pcstr fName) i_destroy_source(s); } -void CSoundRender_Core::play(ref_sound& S, IGameObject* O, u32 flags, float delay) -{ - if (!bPresent || !S._handle()) - return; - S->g_object = O; - if (S._feedback()) - ((CSoundRender_Emitter*)S._feedback())->rewind(); - else - i_play(S, flags, delay); - - if (flags & sm_2D || S._handle()->channels_num() == 2) - S._feedback()->switch_to_2D(); - - S._feedback()->set_ignore_time_factor(flags & sm_IgnoreTimeFactor); -} - -void CSoundRender_Core::play_no_feedback( - ref_sound& S, IGameObject* O, u32 flags, float delay, Fvector* pos, float* vol, float* freq, Fvector2* range) -{ - if (!bPresent || !S._handle()) - return; - const ref_sound orig = S; - S._set(xr_new()); - S->handle = orig->handle; - S->g_type = orig->g_type; - S->g_object = O; - S->dwBytesTotal = orig->dwBytesTotal; - S->fTimeTotal = orig->fTimeTotal; - S->fn_attached[0] = orig->fn_attached[0]; - S->fn_attached[1] = orig->fn_attached[1]; - - i_play(S, flags, delay); - - if (flags & sm_2D || S._handle()->channels_num() == 2) - S._feedback()->switch_to_2D(); - - if (pos) - S._feedback()->set_position(*pos); - if (freq) - S._feedback()->set_frequency(*freq); - if (range) - S._feedback()->set_range((*range)[0], (*range)[1]); - if (vol) - S._feedback()->set_volume(*vol); - S = orig; -} - -void CSoundRender_Core::play_at_pos(ref_sound& S, IGameObject* O, const Fvector& pos, u32 flags, float delay) -{ - if (!bPresent || !S._handle()) - return; - S->g_object = O; - if (S._feedback()) - ((CSoundRender_Emitter*)S._feedback())->rewind(); - else - i_play(S, flags, delay); - - S._feedback()->set_position(pos); - - if (flags & sm_2D || S._handle()->channels_num() == 2) - S._feedback()->switch_to_2D(); - - S._feedback()->set_ignore_time_factor(flags & sm_IgnoreTimeFactor); -} - void CSoundRender_Core::destroy(CSound& S) { if (auto* emitter = (CSoundRender_Emitter*)S.feedback) @@ -363,43 +187,6 @@ void CSoundRender_Core::destroy(CSound& S) S.handle = nullptr; } -CSoundRender_Environment* CSoundRender_Core::get_environment(const Fvector& P) -{ - static CSoundRender_Environment identity; - - if (bUserEnvironment) - { - return &s_user_environment; - } - if (geom_ENV) - { - Fvector dir = {0, -1, 0}; - geom_DB.ray_query(CDB::OPT_ONLYNEAREST, geom_ENV, P, dir, 1000.f); - if (geom_DB.r_count()) - { - const auto envLib = Parent.get_env_library(); - - CDB::RESULT* r = geom_DB.r_begin(); - CDB::TRI* T = geom_ENV->get_tris() + r->id; - Fvector* V = geom_ENV->get_verts(); - Fvector tri_norm; - tri_norm.mknormal(V[T->verts[0]], V[T->verts[1]], V[T->verts[2]]); - float dot = dir.dotproduct(tri_norm); - if (dot < 0) - { - u16 id_front = (u16)((T->dummy & 0x0000ffff) >> 0); // front face - return envLib->Get(id_front); - } - u16 id_back = (u16)((T->dummy & 0xffff0000) >> 16); // back face - return envLib->Get(id_back); - } - identity.set_identity(); - return &identity; - } - identity.set_identity(); - return &identity; -} - void CSoundRender_Core::env_apply() { /* @@ -415,37 +202,22 @@ void CSoundRender_Core::env_apply() bListenerMoved = true; } -void CSoundRender_Core::update_listener(const Fvector& P, const Fvector& D, const Fvector& N, float dt) {} - -void CSoundRender_Core::object_relcase(IGameObject* obj) +void CSoundRender_Core::update_listener(const Fvector& P, const Fvector& D, const Fvector& N, float dt) { - if (obj) - { - for (auto& emit : s_emitters) - { - if (emit) - if (emit->owner_data) - if (obj == emit->owner_data->g_object) - emit->owner_data->g_object = 0; - } - } -} - -void CSoundRender_Core::set_user_env(CSound_environment* E) -{ - if (0 == E && !bUserEnvironment) + if (!psSoundFlags.test(ss_EFX) || !m_effects) return; - if (E) - { - s_user_environment = *((CSoundRender_Environment*)E); - bUserEnvironment = true; - } - else + // Update effects + if (bListenerMoved) { - bUserEnvironment = false; + bListenerMoved = false; + e_target = *(CSoundRender_Environment*)DefaultSoundScene->get_environment(P); } - env_apply(); + + e_current.lerp(e_current, e_target, fTimer_Delta); + + m_effects->set_listener(e_current); + m_effects->commit(); } void CSoundRender_Core::refresh_env_library() @@ -457,8 +229,8 @@ void CSoundRender_Core::refresh_env_library() void CSoundRender_Core::refresh_sources() { - for (auto& emit : s_emitters) - emit->stop(false); + stop_emitters(); + for (const auto& kv : s_sources) { CSoundRender_Source* s = kv.second; @@ -466,40 +238,3 @@ void CSoundRender_Core::refresh_sources() s->load(*s->fname); } } - -void CSoundRender_Core::set_environment_size(CSound_environment* src_env, CSound_environment** dst_env) -{ - // XXX: old SDK functionality - /*if (bEAX) - { - CSoundRender_Environment* SE = static_cast(src_env); - CSoundRender_Environment* DE = static_cast(*dst_env); -#if defined(XR_PLATFORM_WINDOWS) - // set environment - i_eax_set(&DSPROPSETID_EAX_ListenerProperties, - DSPROPERTY_EAXLISTENER_IMMEDIATE | DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, &SE->EnvironmentSize, - sizeof(SE->EnvironmentSize)); - i_eax_listener_set(SE); - i_eax_commit_setting(); - i_eax_set(&DSPROPSETID_EAX_ListenerProperties, - DSPROPERTY_EAXLISTENER_IMMEDIATE | DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, &DE->EnvironmentSize, - sizeof(DE->EnvironmentSize)); - i_eax_listener_get(DE); -#endif - }*/ -} - -void CSoundRender_Core::set_environment(u32 id, CSound_environment** dst_env) -{ - // XXX: old SDK functionality - /*if (bEAX) - { - CSoundRender_Environment* DE = static_cast(*dst_env); -#if defined(XR_PLATFORM_WINDOWS) - // set environment - i_eax_set(&DSPROPSETID_EAX_ListenerProperties, - DSPROPERTY_EAXLISTENER_IMMEDIATE | DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, &id, sizeof(id)); - i_eax_listener_get(DE); -#endif - }*/ -} diff --git a/src/xrSound/SoundRender_Core.h b/src/xrSound/SoundRender_Core.h index 2762b3290e6..a33df9e0e7d 100644 --- a/src/xrSound/SoundRender_Core.h +++ b/src/xrSound/SoundRender_Core.h @@ -1,10 +1,12 @@ #pragma once +#include "xrCommon/xr_unordered_map.h" + #include "SoundRender.h" #include "SoundRender_Cache.h" #include "SoundRender_Environment.h" #include "SoundRender_Effects.h" -#include "xrCommon/xr_unordered_map.h" +#include "SoundRender_Scene.h" class CSoundRender_Core : public ISoundManager { @@ -31,11 +33,7 @@ class CSoundRender_Core : public ISoundManager public: CSoundManager& Parent; - using event = std::pair; - xr_vector s_events; - bool bPresent; - bool bUserEnvironment; bool bReady; CTimer Timer; @@ -45,29 +43,19 @@ class CSoundRender_Core : public ISoundManager float fTimerPersistent_Value; float fTimerPersistent_Delta; - sound_event* Handler; - protected: - // Collider -#ifndef _EDITOR - CDB::COLLIDER geom_DB; -#endif - CDB::MODEL* geom_SOM; - CDB::MODEL* geom_MODEL; - CDB::MODEL* geom_ENV; - // Containers + xr_vector m_scenes; + Lock s_sources_lock; xr_unordered_map s_sources; - xr_vector s_emitters; + u32 s_emitters_u; // emitter update marker xr_vector s_targets; xr_vector s_targets_defer; u32 s_targets_pu; // parameters update - CSoundRender_Environment s_user_environment; - CSoundRender_Effects* m_effects{}; - int m_iPauseCounter; + CSoundRender_Effects* m_effects{}; public: // Cache @@ -76,16 +64,18 @@ class CSoundRender_Core : public ISoundManager public: CSoundRender_Core(CSoundManager& p); - ~CSoundRender_Core() override; // General virtual void _initialize_devices_list() = 0; virtual void _initialize() = 0; virtual void _clear() = 0; + + ISoundScene* create_scene() override; + void destroy_scene(ISoundScene*&) override; + void _restart() override; // Sound interface - void verify_refsound(ref_sound& S); CSound* create(pcstr fName, esound_type sound_type, int game_type, bool replaceWithNoSound = true) override; void attach_tail(CSound& S, pcstr fName) override; @@ -99,21 +89,11 @@ class CSoundRender_Core : public ISoundManager } void stop_emitters() override; - int pause_emitters(bool val) override; - - void play(ref_sound& S, IGameObject* O, u32 flags = 0, float delay = 0.f) override; - void play_at_pos(ref_sound& S, IGameObject* O, const Fvector& pos, u32 flags = 0, float delay = 0.f) override; - void play_no_feedback(ref_sound& S, IGameObject* O, u32 flags = 0, float delay = 0.f, Fvector* pos = nullptr, - float* vol = nullptr, float* freq = nullptr, Fvector2* range = nullptr) override; + int pause_emitters(bool pauseState) override; void set_master_volume(float f) override = 0; - void set_geometry_env(IReader* I) override; - void set_geometry_som(IReader* I) override; - void set_geometry_occ(CDB::MODEL* M) override; - void set_handler(sound_event* E) override; void update(const Fvector& P, const Fvector& D, const Fvector& N) override; - virtual void update_events(); void statistic(CSound_stats* dest, CSound_stats_ext* ext) override; void DumpStatistics(class IGameFont& font, class IPerformanceAlert* alert) override; @@ -121,11 +101,8 @@ class CSoundRender_Core : public ISoundManager // virtual const Fvector& listener_position ( )=0; virtual void update_listener(const Fvector& P, const Fvector& D, const Fvector& N, float dt) = 0; - virtual void refresh_env_library(); - virtual void set_user_env(CSound_environment* E); - virtual void refresh_sources(); - virtual void set_environment(u32 id, CSound_environment** dst_env); - virtual void set_environment_size(CSound_environment* src_env, CSound_environment** dst_env); + void refresh_env_library() override; + void refresh_sources() override; public: bool i_create_source(CSound_source*& result, pcstr name, bool replaceWithNoSound = true); @@ -134,17 +111,12 @@ class CSoundRender_Core : public ISoundManager void i_create_all_sources(); void i_destroy_source(CSoundRender_Source* S); - CSoundRender_Emitter* i_play(const ref_sound& S, u32 flags, float delay); void i_start(CSoundRender_Emitter* E); void i_stop(CSoundRender_Emitter* E); void i_rewind(CSoundRender_Emitter* E); bool i_allow_play(CSoundRender_Emitter* E); bool i_locked() override { return isLocked; } - void object_relcase(IGameObject* obj) override; - float get_occlusion_to(const Fvector& hear_pt, const Fvector& snd_pt, float dispersion = 0.2f) override; - float get_occlusion(Fvector& P, float R, Fvector* occ) override; - CSoundRender_Environment* get_environment(const Fvector& P); void env_apply(); }; diff --git a/src/xrSound/SoundRender_Core_Processor.cpp b/src/xrSound/SoundRender_Core_Processor.cpp index 3f6f5eedea6..8049639a936 100644 --- a/src/xrSound/SoundRender_Core_Processor.cpp +++ b/src/xrSound/SoundRender_Core_Processor.cpp @@ -9,18 +9,8 @@ #include "SoundRender_Target.h" #include "SoundRender_Source.h" -CSoundRender_Emitter* CSoundRender_Core::i_play(const ref_sound& S, u32 flags, float delay) -{ - VERIFY(S._get()->feedback == nullptr); - CSoundRender_Emitter* E = s_emitters.emplace_back(xr_new()); - S._get()->feedback = E; - E->start(S, flags, delay); - return E; -} - void CSoundRender_Core::update(const Fvector& P, const Fvector& D, const Fvector& N) { - u32 it; if (0 == bReady) return; Stats.Update.Begin(); @@ -38,19 +28,22 @@ void CSoundRender_Core::update(const Fvector& P, const Fvector& D, const Fvector } s_emitters_u++; + const auto update_emitter = [this](CSoundRender_Emitter* emitter) + { + const bool ignore = emitter->bIgnoringTimeFactor; + const float time = ignore ? fTimerPersistent_Value : fTimer_Value; + const float delta = ignore ? fTimerPersistent_Delta : fTimer_Delta; + emitter->update(time, delta); + emitter->marker = s_emitters_u; + }; + // Firstly update emitters, which are now being rendered - // Msg("! update: r-emitters"); - for (it = 0; it < s_targets.size(); it++) + for (CSoundRender_Target* T : s_targets) { - CSoundRender_Target* T = s_targets[it]; - CSoundRender_Emitter* E = T->get_emitter(); - if (E) + if (CSoundRender_Emitter* E = T->get_emitter()) { - const bool ignore = E->bIgnoringTimeFactor; - const float time = ignore ? fTimerPersistent_Value : fTimer_Value; - const float delta = ignore ? fTimerPersistent_Delta : fTimer_Delta; - E->update(time, delta); - E->marker = s_emitters_u; + update_emitter(E); + E = T->get_emitter(); // update can stop itself if (E) T->priority = E->priority(); @@ -64,41 +57,36 @@ void CSoundRender_Core::update(const Fvector& P, const Fvector& D, const Fvector } // Update emitters - // Msg("! update: emitters"); - for (it = 0; it < s_emitters.size(); it++) + for (CSoundRender_Scene* scene : m_scenes) { - CSoundRender_Emitter* pEmitter = s_emitters[it]; - if (pEmitter->marker != s_emitters_u) - { - const bool ignore = pEmitter->bIgnoringTimeFactor; - const float time = ignore ? fTimerPersistent_Value : fTimer_Value; - const float delta = ignore ? fTimerPersistent_Delta : fTimer_Delta; - pEmitter->update(time, delta); - pEmitter->marker = s_emitters_u; - } - if (!pEmitter->isPlaying()) + auto& emitters = scene->get_emitters(); + for (u32 it = 0; it < emitters.size(); it++) { - // Stopped - xr_delete(pEmitter); - s_emitters.erase(s_emitters.begin() + it); - it--; + CSoundRender_Emitter* pEmitter = emitters[it]; + if (pEmitter->marker != s_emitters_u) + { + update_emitter(pEmitter); + } + if (!pEmitter->isPlaying()) + { + // Stopped + xr_delete(pEmitter); + emitters.erase(emitters.begin() + it); + it--; + } } } - // Get currently rendering emitters - // Msg("! update: targets"); s_targets_defer.clear(); s_targets_pu++; - // u32 PU = s_targets_pu%s_targets.size(); - for (it = 0; it < s_targets.size(); it++) + + for (CSoundRender_Target* T : s_targets) { - CSoundRender_Target* T = s_targets[it]; if (T->get_emitter()) { // Has emmitter, maybe just not started rendering if (T->get_Rendering()) { - /*if (PU == it)*/ T->fill_parameters(); T->update(); } else @@ -109,25 +97,9 @@ void CSoundRender_Core::update(const Fvector& P, const Fvector& D, const Fvector // Commit parameters from pending targets if (!s_targets_defer.empty()) { - // Msg ("! update: start render - commit"); s_targets_defer.erase(std::unique(s_targets_defer.begin(), s_targets_defer.end()), s_targets_defer.end()); - for (it = 0; it < s_targets_defer.size(); it++) - s_targets_defer[it]->fill_parameters(); - } - - // Update effects - if (psSoundFlags.test(ss_EFX) && m_effects) - { - if (bListenerMoved) - { - bListenerMoved = false; - e_target = *get_environment(P); - } - - e_current.lerp(e_current, e_target, fTimer_Delta); - - m_effects->set_listener(e_current); - m_effects->commit(); + for (CSoundRender_Target* target : s_targets_defer) + target->fill_parameters(); } // update listener @@ -136,68 +108,68 @@ void CSoundRender_Core::update(const Fvector& P, const Fvector& D, const Fvector // Start rendering of pending targets if (!s_targets_defer.empty()) { - // Msg ("! update: start render"); - for (it = 0; it < s_targets_defer.size(); it++) - s_targets_defer[it]->render(); + for (CSoundRender_Target* target : s_targets_defer) + target->render(); } // Events - update_events(); + for (CSoundRender_Scene* scene : m_scenes) + scene->update(); isLocked = false; Stats.Update.End(); } -static u32 g_saved_event_count = 0; - -void CSoundRender_Core::update_events() -{ - g_saved_event_count = static_cast(s_events.size()); - for (auto& E : s_events) - Handler(E.first, E.second); - - s_events.clear(); -} - void CSoundRender_Core::statistic(CSound_stats* dest, CSound_stats_ext* ext) { if (dest) { dest->_rendered = 0; + dest->_simulated = 0; + dest->_events = 0; + for (auto T : s_targets) { if (T->get_emitter() && T->get_Rendering()) dest->_rendered++; } - dest->_simulated = static_cast(s_emitters.size()); + + for (CSoundRender_Scene* scene : m_scenes) + { + dest->_simulated += scene->get_emitters().size(); + dest->_events += scene->get_prev_events_count(); + } dest->_cache_hits = cache._stat_hit; dest->_cache_misses = cache._stat_miss; - dest->_events = g_saved_event_count; cache.stats_clear(); } if (ext) { - for (auto _E : s_emitters) + for (CSoundRender_Scene* scene : m_scenes) { - CSound_stats_ext::SItem _I; - _I._3D = !_E->b2D; - _I._rendered = !!_E->target; - _I.params = _E->p_source; - _I.volume = _E->smooth_volume; - if (_E->owner_data) + auto& emitters = scene->get_emitters(); + for (const auto emitter : emitters) { - _I.name = _E->source()->fname; - _I.game_object = _E->owner_data->g_object; - _I.game_type = _E->owner_data->g_type; - _I.type = _E->owner_data->s_type; + CSound_stats_ext::SItem item; + item._3D = !emitter->b2D; + item._rendered = !!emitter->target; + item.params = emitter->p_source; + item.volume = emitter->smooth_volume; + if (emitter->owner_data) + { + item.name = emitter->source()->fname; + item.game_object = emitter->owner_data->g_object; + item.game_type = emitter->owner_data->g_type; + item.type = emitter->owner_data->s_type; + } + else + { + item.game_object = nullptr; + item.game_type = 0; + item.type = st_Effect; + } + ext->append(item); } - else - { - _I.game_object = nullptr; - _I.game_type = 0; - _I.type = st_Effect; - } - ext->append(_I); } } } @@ -214,93 +186,3 @@ void CSoundRender_Core::DumpStatistics(IGameFont& font, IPerformanceAlert* alert font.OutNext("Hits/misses: %d/%d", sndStat._cache_hits, sndStat._cache_misses); Stats.FrameStart(); } - -float CSoundRender_Core::get_occlusion_to(const Fvector& hear_pt, const Fvector& snd_pt, float dispersion) -{ - float occ_value = 1.f; - - if (nullptr != geom_SOM) - { - // Calculate RAY params - Fvector pos, dir; - pos.random_dir(); - pos.mul(dispersion); - pos.add(snd_pt); - dir.sub(pos, hear_pt); - float range = dir.magnitude(); - dir.div(range); - - geom_DB.ray_query(CDB::OPT_CULL, geom_SOM, hear_pt, dir, range); - const auto r_cnt = geom_DB.r_count(); - CDB::RESULT* _B = geom_DB.r_begin(); - if (0 != r_cnt) - { - for (size_t k = 0; k < r_cnt; k++) - { - CDB::RESULT* R = _B + k; - occ_value *= *reinterpret_cast(&R->dummy); - } - } - } - return occ_value; -} - -float CSoundRender_Core::get_occlusion(Fvector& P, float R, Fvector* occ) -{ - float occ_value = 1.f; - - // Calculate RAY params - Fvector base = listener_position(); - Fvector pos, dir; - float range; - pos.random_dir(); - pos.mul(R); - pos.add(P); - dir.sub(pos, base); - range = dir.magnitude(); - dir.div(range); - - if (nullptr != geom_MODEL) - { - bool bNeedFullTest = true; - // 1. Check cached polygon - float _u, _v, _range; - if (CDB::TestRayTri(base, dir, occ, _u, _v, _range, true)) - if (_range > 0 && _range < range) - { - occ_value = psSoundOcclusionScale; - bNeedFullTest = false; - } - // 2. Polygon doesn't picked up - real database query - if (bNeedFullTest) - { - geom_DB.ray_query(CDB::OPT_ONLYNEAREST, geom_MODEL, base, dir, range); - if (0 != geom_DB.r_count()) - { - // cache polygon - const CDB::RESULT* R2 = geom_DB.r_begin(); - const CDB::TRI& T = geom_MODEL->get_tris()[R2->id]; - const Fvector* V = geom_MODEL->get_verts(); - occ[0].set(V[T.verts[0]]); - occ[1].set(V[T.verts[1]]); - occ[2].set(V[T.verts[2]]); - occ_value = psSoundOcclusionScale; - } - } - } - if (nullptr != geom_SOM) - { - geom_DB.ray_query(CDB::OPT_CULL, geom_SOM, base, dir, range); - const auto r_cnt = geom_DB.r_count(); - CDB::RESULT* _B = geom_DB.r_begin(); - if (0 != r_cnt) - { - for (size_t k = 0; k < r_cnt; k++) - { - CDB::RESULT* R2 = _B + k; - occ_value *= *reinterpret_cast(&R2->dummy); - } - } - } - return occ_value; -} diff --git a/src/xrSound/SoundRender_Emitter.cpp b/src/xrSound/SoundRender_Emitter.cpp index 4735310397d..f805f6c34e8 100644 --- a/src/xrSound/SoundRender_Emitter.cpp +++ b/src/xrSound/SoundRender_Emitter.cpp @@ -2,6 +2,7 @@ #include "SoundRender_Emitter.h" #include "SoundRender_Core.h" +#include "SoundRender_Scene.h" #include "SoundRender_Source.h" #include "SoundRender_TargetA.h" @@ -33,7 +34,8 @@ void CSoundRender_Emitter::set_time(float t) fTimeToRewind = t; } -CSoundRender_Emitter::CSoundRender_Emitter() +CSoundRender_Emitter::CSoundRender_Emitter(CSoundRender_Scene* s) + : scene(s) { #ifdef DEBUG static u32 incrementalID = 0; @@ -78,11 +80,13 @@ void CSoundRender_Emitter::Event_ReleaseOwner() if (!owner_data) return; - for (u32 it = 0; it < SoundRender->s_events.size(); it++) + auto& events = scene->get_events(); + + for (u32 it = 0; it < events.size(); it++) { - if (owner_data == SoundRender->s_events[it].first) + if (owner_data == events[it].first) { - SoundRender->s_events.erase(SoundRender->s_events.begin() + it); + events.erase(events.begin() + it); it--; } } @@ -91,24 +95,24 @@ void CSoundRender_Emitter::Event_ReleaseOwner() void CSoundRender_Emitter::Event_Propagade() { fTimeToPropagade += ::Random.randF(s_f_def_event_pulse - 0.030f, s_f_def_event_pulse + 0.030f); - if (!(owner_data)) + if (!owner_data) return; - if (0 == owner_data->g_type) + if (!owner_data->g_type) return; - if (0 == owner_data->g_object) + if (!owner_data->g_object) return; - if (0 == SoundRender->Handler) + if (!scene->get_events_handler()) return; VERIFY(_valid(p_source.volume)); // Calculate range - float clip = p_source.max_ai_distance * p_source.volume; - float range = std::min(p_source.max_ai_distance, clip); + const float clip = p_source.max_ai_distance * p_source.volume; + const float range = std::min(p_source.max_ai_distance, clip); if (range < 0.1f) return; // Inform objects - SoundRender->s_events.emplace_back(owner_data, range); + scene->get_events().emplace_back(owner_data, range); } void CSoundRender_Emitter::switch_to_2D() @@ -117,7 +121,11 @@ void CSoundRender_Emitter::switch_to_2D() set_priority(100.f); } -void CSoundRender_Emitter::switch_to_3D() { b2D = false; } +void CSoundRender_Emitter::switch_to_3D() +{ + b2D = false; +} + u32 CSoundRender_Emitter::play_time() { if (m_current_state == stPlaying || m_current_state == stPlayingLooped || m_current_state == stSimulating || diff --git a/src/xrSound/SoundRender_Emitter.h b/src/xrSound/SoundRender_Emitter.h index 001e2d19b20..35c83ecbbf3 100644 --- a/src/xrSound/SoundRender_Emitter.h +++ b/src/xrSound/SoundRender_Emitter.h @@ -1,8 +1,10 @@ #pragma once +#include "xrCore/_std_extensions.h" + #include "SoundRender.h" #include "SoundRender_Environment.h" -#include "xrCore/_std_extensions.h" +#include "SoundRender_Scene.h" class CSoundRender_Emitter final : public CSound_emitter { @@ -33,7 +35,8 @@ class CSoundRender_Emitter final : public CSound_emitter static constexpr float TIME_TO_STOP_INFINITE = static_cast(0xffffffff); - CSoundRender_Target* target; + CSoundRender_Target* target{}; + CSoundRender_Scene* scene{}; ref_sound owner_data; [[nodiscard]] @@ -107,7 +110,7 @@ class CSoundRender_Emitter final : public CSound_emitter void fill_block(void* ptr, u32 size); void fill_data(u8* ptr, u32 offset, u32 size); - float priority(); + float priority() const; void start(const ref_sound& _owner, u32 flags, float delay); void cancel(); // manager forces out of rendering void update(float time, float dt); @@ -121,6 +124,6 @@ class CSoundRender_Emitter final : public CSound_emitter void set_ignore_time_factor(bool ignore) override { bIgnoringTimeFactor = ignore; }; - CSoundRender_Emitter(); + CSoundRender_Emitter(CSoundRender_Scene* s); ~CSoundRender_Emitter() override; }; diff --git a/src/xrSound/SoundRender_Emitter_FSM.cpp b/src/xrSound/SoundRender_Emitter_FSM.cpp index 4ebf9e852b2..6f1c2c3304a 100644 --- a/src/xrSound/SoundRender_Emitter_FSM.cpp +++ b/src/xrSound/SoundRender_Emitter_FSM.cpp @@ -49,11 +49,11 @@ void CSoundRender_Emitter::update(float fTime, float dt) fTimeToStop = fTime + (get_length_sec() / p_source.freq); //--#SM+#-- fTimeToPropagade = fTime; fade_volume = 1.f; - occluder_volume = SoundRender->get_occlusion(p_source.position, .2f, occluder); + occluder_volume = scene->get_occlusion(p_source.position, .2f, occluder); smooth_volume = p_source.base_volume * p_source.volume * (owner_data->s_type == st_Effect ? psSoundVEffects * psSoundVFactor : psSoundVMusic) * (b2D ? 1.f : occluder_volume); - e_current = e_target = *SoundRender->get_environment(p_source.position); + e_current = e_target = *(CSoundRender_Environment*)scene->get_environment(p_source.position); if (update_culling(dt)) { m_current_state = stPlaying; @@ -77,11 +77,11 @@ void CSoundRender_Emitter::update(float fTime, float dt) fTimeToStop = TIME_TO_STOP_INFINITE; fTimeToPropagade = fTime; fade_volume = 1.f; - occluder_volume = SoundRender->get_occlusion(p_source.position, .2f, occluder); + occluder_volume = scene->get_occlusion(p_source.position, .2f, occluder); smooth_volume = p_source.base_volume * p_source.volume * (owner_data->s_type == st_Effect ? psSoundVEffects * psSoundVFactor : psSoundVMusic) * (b2D ? 1.f : occluder_volume); - e_current = e_target = *SoundRender->get_environment(p_source.position); + e_current = e_target = *(CSoundRender_Environment*)scene->get_environment(p_source.position); if (update_culling(dt)) { m_current_state = stPlayingLooped; @@ -140,7 +140,7 @@ void CSoundRender_Emitter::update(float fTime, float dt) } else { - u32 ptr = calc_cursor(fTimeStarted, fTime, get_length_sec(), p_source.freq, source()->m_wformat); //--#SM+#-- + const u32 ptr = calc_cursor(fTimeStarted, fTime, get_length_sec(), p_source.freq, source()->m_wformat); //--#SM+#-- set_cursor(ptr); if (update_culling(dt)) @@ -193,7 +193,7 @@ void CSoundRender_Emitter::update(float fTime, float dt) { // switch to: PLAY m_current_state = stPlayingLooped; // switch state - u32 ptr = calc_cursor(fTimeStarted, fTime, get_length_sec(), p_source.freq, source()->m_wformat); //--#SM+#-- + const u32 ptr = calc_cursor(fTimeStarted, fTime, get_length_sec(), p_source.freq, source()->m_wformat); //--#SM+#-- set_cursor(ptr); SoundRender->i_start(this); @@ -213,13 +213,13 @@ void CSoundRender_Emitter::update(float fTime, float dt) case stSimulatingLooped: if (fTimeToRewind > 0.0f) { - float fLength = get_length_sec(); - bool bLooped = (fTimeToStop == 0xffffffff); + const float fLength = get_length_sec(); + const bool bLooped = (fTimeToStop == 0xffffffff); R_ASSERT2(fLength >= fTimeToRewind, "set_time: target time is bigger than length of sound"); - float fRemainingTime = (fLength - fTimeToRewind) / p_source.freq; - float fPastTime = fTimeToRewind / p_source.freq; + const float fRemainingTime = (fLength - fTimeToRewind) / p_source.freq; + const float fPastTime = fTimeToRewind / p_source.freq; fTimeStarted = fTime - fPastTime; fTimeToPropagade = fTimeStarted; //--> For AI events @@ -242,7 +242,7 @@ void CSoundRender_Emitter::update(float fTime, float dt) fTimeToStop = fTime + fRemainingTime; } - u32 ptr = calc_cursor(fTimeStarted, fTime, fLength, p_source.freq, source()->m_wformat); + const u32 ptr = calc_cursor(fTimeStarted, fTime, fLength, p_source.freq, source()->m_wformat); set_cursor(ptr); fTimeToRewind = 0.0f; @@ -275,8 +275,8 @@ void CSoundRender_Emitter::update(float fTime, float dt) IC void volume_lerp(float& c, float t, float s, float dt) { - float diff = t - c; - float diff_a = _abs(diff); + const float diff = t - c; + const float diff_a = _abs(diff); if (diff_a < EPS_S) return; float mot = s * dt; @@ -309,7 +309,7 @@ bool CSoundRender_Emitter::update_culling(float dt) // Calc attenuated volume float att = p_source.min_distance / (psSoundRolloff * dist); clamp(att, 0.f, 1.f); - float fade_scale = + const float fade_scale = bStopping || (att * p_source.base_volume * p_source.volume * (owner_data->s_type == st_Effect ? psSoundVEffects * psSoundVFactor : psSoundVMusic) < psSoundCull) ? @@ -318,9 +318,9 @@ bool CSoundRender_Emitter::update_culling(float dt) fade_volume += dt * 10.f * fade_scale; // Update occlusion - float occ = (owner_data->g_type == SOUND_TYPE_WORLD_AMBIENT) ? + const float occ = (owner_data->g_type == SOUND_TYPE_WORLD_AMBIENT) ? 1.0f : - SoundRender->get_occlusion(p_source.position, .2f, occluder); + scene->get_occlusion(p_source.position, .2f, occluder); volume_lerp(occluder_volume, occ, 1.f, dt); clamp(occluder_volume, 0.f, 1.f); @@ -352,11 +352,10 @@ bool CSoundRender_Emitter::update_culling(float dt) // --- else check availability of resources if (target) return TRUE; - else - return SoundRender->i_allow_play(this); + return SoundRender->i_allow_play(this); } -float CSoundRender_Emitter::priority() +float CSoundRender_Emitter::priority() const { float dist = SoundRender->listener_position().distance_to(p_source.position); float att = p_source.min_distance / (psSoundRolloff * dist); @@ -368,7 +367,7 @@ void CSoundRender_Emitter::update_environment(float dt) { if (bMoved) { - e_target = *SoundRender->get_environment(p_source.position); + e_target = *(CSoundRender_Environment*)scene->get_environment(p_source.position); // Cribbledirge: updates the velocity of the sound. p_source.update_velocity(dt); } diff --git a/src/xrSound/SoundRender_Scene.cpp b/src/xrSound/SoundRender_Scene.cpp new file mode 100644 index 00000000000..bf5578cbdd8 --- /dev/null +++ b/src/xrSound/SoundRender_Scene.cpp @@ -0,0 +1,421 @@ +#include "stdafx.h" + +#include "Common/LevelStructure.hpp" +#include "xrCDB/Intersect.hpp" + +#include "SoundRender_Core.h" +#include "SoundRender_Scene.h" +#include "SoundRender_Emitter.h" + +CSoundRender_Scene::~CSoundRender_Scene() +{ + stop_emitters(); + + // remove emitters + for (auto& emit : s_emitters) + xr_delete(emit); + s_emitters.clear(); + + xr_delete(geom_ENV); + xr_delete(geom_SOM); +} + +void CSoundRender_Scene::stop_emitters() const +{ + for (const auto& emit : s_emitters) + emit->stop(false); +} + +int CSoundRender_Scene::pause_emitters(bool pauseState) +{ + m_iPauseCounter += pauseState ? +1 : -1; + VERIFY(m_iPauseCounter >= 0); + + for (const auto& emit : s_emitters) + emit->pause(pauseState, pauseState ? m_iPauseCounter : m_iPauseCounter + 1); + + return m_iPauseCounter; +} + +void CSoundRender_Scene::set_handler(sound_event* E) { sound_event_handler = E; } + +void CSoundRender_Scene::set_geometry_occ(CDB::MODEL* M) { geom_MODEL = M; } + +void CSoundRender_Scene::set_geometry_som(IReader* I) +{ + xr_delete(geom_SOM); + if (nullptr == I) + return; + + // check version + R_ASSERT(I->find_chunk(0)); + [[maybe_unused]] const u32 version = I->r_u32(); + VERIFY2(version == 0, "Invalid SOM version"); + + struct SOM_poly + { + Fvector3 v1; + Fvector3 v2; + Fvector3 v3; + u32 b2sided; + float occ; + }; + + CDB::Collector CL; + { + // load geometry + IReader* geom = I->open_chunk(1); + VERIFY2(geom, "Corrupted SOM file"); + if (!geom) + return; + + // Load tris and merge them + const auto begin = static_cast(geom->pointer()); + const auto end = static_cast(geom->end()); + for (SOM_poly* poly = begin; poly != end; ++poly) + { + CL.add_face_packed_D(poly->v1, poly->v2, poly->v3, *(u32*)&poly->occ, 0.01f); + if (poly->b2sided) + CL.add_face_packed_D(poly->v3, poly->v2, poly->v1, *(u32*)&poly->occ, 0.01f); + } + geom->close(); + } + + // Create AABB-tree + geom_SOM = xr_new(); + geom_SOM->build(CL.getV(), int(CL.getVS()), CL.getT(), int(CL.getTS())); +} + +void CSoundRender_Scene::set_geometry_env(IReader* I) +{ + xr_delete(geom_ENV); + if (nullptr == I) + return; + const auto envLib = SoundRender->Parent.get_env_library(); + if (!envLib) + return; + + // Associate names + xr_vector ids; + IReader* names = I->open_chunk(0); + while (!names->eof()) + { + string256 n; + names->r_stringZ(n, sizeof(n)); + const int id = envLib->GetID(n); + R_ASSERT(id >= 0); + ids.push_back((u16)id); + } + names->close(); + + // Load geometry + IReader* geom_ch = I->open_chunk(1); + + u8* _data = (u8*)xr_malloc(geom_ch->length()); + + memcpy(_data, geom_ch->pointer(), geom_ch->length()); + + IReader* geom = xr_new(_data, geom_ch->length(), 0); + + hdrCFORM H; + geom->r(&H, sizeof(hdrCFORM)); + Fvector* verts = (Fvector*)geom->pointer(); + CDB::TRI* tris = (CDB::TRI*)(verts + H.vertcount); + for (u32 it = 0; it < H.facecount; it++) + { + CDB::TRI* T = tris + it; + const u16 id_front = (u16)((T->dummy & 0x0000ffff) >> 0); // front face + const u16 id_back = (u16)((T->dummy & 0xffff0000) >> 16); // back face + R_ASSERT(id_front < (u16)ids.size()); + R_ASSERT(id_back < (u16)ids.size()); + T->dummy = u32(ids[id_back] << 16) | u32(ids[id_front]); + } + geom_ENV = xr_new(); + geom_ENV->build(verts, H.vertcount, tris, H.facecount); +#ifdef _EDITOR // XXX: may be we are interested in applying env in the game build too? + env_apply(); +#endif + geom_ch->close(); + geom->close(); + xr_free(_data); +} + +void CSoundRender_Scene::play(ref_sound& S, IGameObject* O, u32 flags, float delay) +{ + if (!SoundRender->bPresent || !S._handle()) + return; + S->g_object = O; + if (S._feedback()) + ((CSoundRender_Emitter*)S._feedback())->rewind(); + else + i_play(S, flags, delay); + + if (flags & sm_2D || S._handle()->channels_num() == 2) + S._feedback()->switch_to_2D(); + + S._feedback()->set_ignore_time_factor(flags & sm_IgnoreTimeFactor); +} + +void CSoundRender_Scene::play_no_feedback( + ref_sound& S, IGameObject* O, u32 flags, float delay, Fvector* pos, float* vol, float* freq, Fvector2* range) +{ + if (!SoundRender->bPresent || !S._handle()) + return; + const ref_sound orig = S; + S._set(xr_new()); + S->handle = orig->handle; + S->g_type = orig->g_type; + S->g_object = O; + S->dwBytesTotal = orig->dwBytesTotal; + S->fTimeTotal = orig->fTimeTotal; + S->fn_attached[0] = orig->fn_attached[0]; + S->fn_attached[1] = orig->fn_attached[1]; + + i_play(S, flags, delay); + + if (flags & sm_2D || S._handle()->channels_num() == 2) + S._feedback()->switch_to_2D(); + + if (pos) + S._feedback()->set_position(*pos); + if (freq) + S._feedback()->set_frequency(*freq); + if (range) + S._feedback()->set_range((*range)[0], (*range)[1]); + if (vol) + S._feedback()->set_volume(*vol); + S = orig; +} + +void CSoundRender_Scene::play_at_pos(ref_sound& S, IGameObject* O, const Fvector& pos, u32 flags, float delay) +{ + if (!SoundRender->bPresent || !S._handle()) + return; + S->g_object = O; + if (S._feedback()) + ((CSoundRender_Emitter*)S._feedback())->rewind(); + else + i_play(S, flags, delay); + + S._feedback()->set_position(pos); + + if (flags & sm_2D || S._handle()->channels_num() == 2) + S._feedback()->switch_to_2D(); + + S._feedback()->set_ignore_time_factor(flags & sm_IgnoreTimeFactor); +} + +CSoundRender_Emitter* CSoundRender_Scene::i_play(ref_sound& S, u32 flags, float delay) +{ + VERIFY(!S->feedback); + CSoundRender_Emitter* E = s_emitters.emplace_back(xr_new(this)); + S->feedback = E; + E->start(S, flags, delay); + return E; +} + +void CSoundRender_Scene::update() +{ + s_events_prev_count = s_events.size(); + + for (auto& [sound, range] : s_events) + sound_event_handler(sound, range); + + s_events.clear(); +} + +void CSoundRender_Scene::object_relcase(IGameObject* obj) +{ + if (obj) + { + for (const auto& emit : s_emitters) + { + if (emit) + if (emit->owner_data) + if (obj == emit->owner_data->g_object) + emit->owner_data->g_object = 0; + } + } +} + +float CSoundRender_Scene::get_occlusion_to(const Fvector& hear_pt, const Fvector& snd_pt, float dispersion) +{ + float occ_value = 1.f; + + if (nullptr != geom_SOM) + { + // Calculate RAY params + Fvector pos, dir; + pos.random_dir(); + pos.mul(dispersion); + pos.add(snd_pt); + dir.sub(pos, hear_pt); + const float range = dir.magnitude(); + dir.div(range); + + geom_DB.ray_query(CDB::OPT_CULL, geom_SOM, hear_pt, dir, range); + const auto r_cnt = geom_DB.r_count(); + CDB::RESULT* _B = geom_DB.r_begin(); + if (0 != r_cnt) + { + for (size_t k = 0; k < r_cnt; k++) + { + CDB::RESULT* R = _B + k; + occ_value *= *reinterpret_cast(&R->dummy); + } + } + } + return occ_value; +} + +float CSoundRender_Scene::get_occlusion(Fvector& P, float R, Fvector* occ) +{ + float occ_value = 1.f; + + // Calculate RAY params + const Fvector base = SoundRender->listener_position(); + Fvector pos, dir; + float range; + pos.random_dir(); + pos.mul(R); + pos.add(P); + dir.sub(pos, base); + range = dir.magnitude(); + dir.div(range); + + if (nullptr != geom_MODEL) + { + bool bNeedFullTest = true; + // 1. Check cached polygon + float _u, _v, _range; + if (CDB::TestRayTri(base, dir, occ, _u, _v, _range, true)) + if (_range > 0 && _range < range) + { + occ_value = psSoundOcclusionScale; + bNeedFullTest = false; + } + // 2. Polygon doesn't picked up - real database query + if (bNeedFullTest) + { + geom_DB.ray_query(CDB::OPT_ONLYNEAREST, geom_MODEL, base, dir, range); + if (0 != geom_DB.r_count()) + { + // cache polygon + const CDB::RESULT* R2 = geom_DB.r_begin(); + const CDB::TRI& T = geom_MODEL->get_tris()[R2->id]; + const Fvector* V = geom_MODEL->get_verts(); + occ[0].set(V[T.verts[0]]); + occ[1].set(V[T.verts[1]]); + occ[2].set(V[T.verts[2]]); + occ_value = psSoundOcclusionScale; + } + } + } + if (nullptr != geom_SOM) + { + geom_DB.ray_query(CDB::OPT_CULL, geom_SOM, base, dir, range); + const auto r_cnt = geom_DB.r_count(); + CDB::RESULT* _B = geom_DB.r_begin(); + if (0 != r_cnt) + { + for (size_t k = 0; k < r_cnt; k++) + { + CDB::RESULT* R2 = _B + k; + occ_value *= *reinterpret_cast(&R2->dummy); + } + } + } + return occ_value; +} + +void CSoundRender_Scene::set_user_env(CSound_environment* E) +{ + if (0 == E && !bUserEnvironment) + return; + + if (E) + { + s_user_environment = *((CSoundRender_Environment*)E); + bUserEnvironment = true; + } + else + { + bUserEnvironment = false; + } + SoundRender->env_apply(); +} + +CSound_environment* CSoundRender_Scene::get_environment(const Fvector& P) +{ + static CSoundRender_Environment identity; + + if (bUserEnvironment) + { + return &s_user_environment; + } + if (geom_ENV) + { + constexpr Fvector dir = { 0, -1, 0 }; + geom_DB.ray_query(CDB::OPT_ONLYNEAREST, geom_ENV, P, dir, 1000.f); + if (geom_DB.r_count()) + { + const auto envLib = SoundRender->Parent.get_env_library(); + + const CDB::RESULT* r = geom_DB.r_begin(); + const CDB::TRI* T = geom_ENV->get_tris() + r->id; + const Fvector* V = geom_ENV->get_verts(); + Fvector tri_norm; + tri_norm.mknormal(V[T->verts[0]], V[T->verts[1]], V[T->verts[2]]); + const float dot = dir.dotproduct(tri_norm); + if (dot < 0) + { + const u16 id_front = (u16)((T->dummy & 0x0000ffff) >> 0); // front face + return envLib->Get(id_front); + } + const u16 id_back = (u16)((T->dummy & 0xffff0000) >> 16); // back face + return envLib->Get(id_back); + } + identity.set_identity(); + return &identity; + } + identity.set_identity(); + return &identity; +} + +void CSoundRender_Scene::set_environment_size(CSound_environment* src_env, CSound_environment** dst_env) +{ + // XXX: old SDK functionality + /*if (bEAX) + { + CSoundRender_Environment* SE = static_cast(src_env); + CSoundRender_Environment* DE = static_cast(*dst_env); +#if defined(XR_PLATFORM_WINDOWS) + // set environment + i_eax_set(&DSPROPSETID_EAX_ListenerProperties, + DSPROPERTY_EAXLISTENER_IMMEDIATE | DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, &SE->EnvironmentSize, + sizeof(SE->EnvironmentSize)); + i_eax_listener_set(SE); + i_eax_commit_setting(); + i_eax_set(&DSPROPSETID_EAX_ListenerProperties, + DSPROPERTY_EAXLISTENER_IMMEDIATE | DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, &DE->EnvironmentSize, + sizeof(DE->EnvironmentSize)); + i_eax_listener_get(DE); +#endif + }*/ +} + + +void CSoundRender_Scene::set_environment(u32 id, CSound_environment** dst_env) +{ + // XXX: old SDK functionality + /*if (bEAX) + { + CSoundRender_Environment* DE = static_cast(*dst_env); +#if defined(XR_PLATFORM_WINDOWS) + // set environment + i_eax_set(&DSPROPSETID_EAX_ListenerProperties, + DSPROPERTY_EAXLISTENER_IMMEDIATE | DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, &id, sizeof(id)); + i_eax_listener_get(DE); +#endif + }*/ +} diff --git a/src/xrSound/SoundRender_Scene.h b/src/xrSound/SoundRender_Scene.h new file mode 100644 index 00000000000..f832a1c32d1 --- /dev/null +++ b/src/xrSound/SoundRender_Scene.h @@ -0,0 +1,66 @@ +#pragma once + +#include "SoundRender_Environment.h" + +class CSoundRender_Emitter; + +class CSoundRender_Scene final : public ISoundScene +{ +public: + ~CSoundRender_Scene() override; + + void stop_emitters() const override; + int pause_emitters(bool pauseState) override; + + void set_handler(sound_event* E) override; + + void set_geometry_env(IReader* I) override; + void set_geometry_som(IReader* I) override; + void set_geometry_occ(CDB::MODEL* M) override; + + void set_user_env(CSound_environment* E) override; + void set_environment(u32 id, CSound_environment** dst_env) override; + void set_environment_size(CSound_environment* src_env, CSound_environment** dst_env) override; + CSound_environment* get_environment(const Fvector& P) override; + + void play(ref_sound& S, IGameObject* O, u32 flags = 0, float delay = 0.f) override; + void play_at_pos(ref_sound& S, IGameObject* O, const Fvector& pos, u32 flags = 0, float delay = 0.f) override; + void play_no_feedback(ref_sound& S, IGameObject* O, u32 flags = 0, float delay = 0.f, Fvector* pos = nullptr, + float* vol = nullptr, float* freq = nullptr, Fvector2* range = nullptr) override; + + float get_occlusion_to(const Fvector& hear_pt, const Fvector& snd_pt, float dispersion = 0.2f) override; + float get_occlusion(Fvector& P, float R, Fvector* occ) override; + + void object_relcase(IGameObject* obj) override; + +public: + CSoundRender_Emitter* i_play(ref_sound& S, u32 flags, float delay); + + void update(); + + auto get_events_handler() const { return sound_event_handler; } + auto& get_events() { return s_events; } + auto& get_prev_events_count() { return s_events_prev_count; } + + auto& get_emitters() { return s_emitters; } + +private: + xr_vector s_emitters; + + using event = std::pair; + xr_vector s_events; + size_t s_events_prev_count{}; + + sound_event* sound_event_handler{}; + + // Collider + CDB::COLLIDER geom_DB; + CDB::MODEL* geom_SOM{}; + CDB::MODEL* geom_MODEL{}; + CDB::MODEL* geom_ENV{}; + + bool bUserEnvironment{}; + CSoundRender_Environment s_user_environment; + + int m_iPauseCounter{ 1 }; +}; diff --git a/src/xrSound/SoundRender_Target.h b/src/xrSound/SoundRender_Target.h index 763f4d88db3..e17bb3bb7d2 100644 --- a/src/xrSound/SoundRender_Target.h +++ b/src/xrSound/SoundRender_Target.h @@ -28,8 +28,8 @@ class CSoundRender_Target CSoundRender_Target(); virtual ~CSoundRender_Target(); - CSoundRender_Emitter* get_emitter() { return m_pEmitter; } - bool get_Rendering() { return rendering; } + CSoundRender_Emitter* get_emitter() const { return m_pEmitter; } + bool get_Rendering() const { return rendering; } virtual bool _initialize() = 0; virtual void _destroy() = 0; virtual void _restart() = 0; diff --git a/src/xrSound/xrSound.vcxproj b/src/xrSound/xrSound.vcxproj index 45e998ffeba..dfaf3f000dd 100644 --- a/src/xrSound/xrSound.vcxproj +++ b/src/xrSound/xrSound.vcxproj @@ -41,6 +41,7 @@ + @@ -66,6 +67,7 @@ + diff --git a/src/xrSound/xrSound.vcxproj.filters b/src/xrSound/xrSound.vcxproj.filters index d888556773e..e10f7b3390a 100644 --- a/src/xrSound/xrSound.vcxproj.filters +++ b/src/xrSound/xrSound.vcxproj.filters @@ -34,6 +34,9 @@ {d66ed9a4-3e7a-471f-9b63-7596368b6f66} + + {764beefb-01ae-4d1b-9504-a66144a44420} + @@ -81,6 +84,9 @@ Effects\OpenAL + + Scene + @@ -146,6 +152,9 @@ Effects\OpenAL + + Scene + diff --git a/src/xrUICore/CMakeLists.txt b/src/xrUICore/CMakeLists.txt index c3c63a5390d..ae8df434075 100644 --- a/src/xrUICore/CMakeLists.txt +++ b/src/xrUICore/CMakeLists.txt @@ -149,6 +149,7 @@ target_link_libraries(xrUICore xrMiscMath xrImGui xrCore + xrSound ) if (WIN32) diff --git a/src/xrUICore/xrUICore.vcxproj b/src/xrUICore/xrUICore.vcxproj index 93fe0ce7037..94fc70434fe 100644 --- a/src/xrUICore/xrUICore.vcxproj +++ b/src/xrUICore/xrUICore.vcxproj @@ -51,6 +51,9 @@ {1daec516-e52c-4a3c-a4da-ae3553e6e0f8} + + {ccca7859-eb86-493e-9b53-c4235f45b3c5} +