-
Notifications
You must be signed in to change notification settings - Fork 0
WIP - Attenuation Models #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 17 commits
7f3dda5
7668712
b9fbe82
e076320
3197b48
280fe12
ec6435e
6f492d3
b2f58d5
84f4029
2b782d3
da822de
4f37bdf
c6a457a
093fcbc
310deaf
7e0d015
e09b969
b09b239
d73248c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ | ||
|
|
||
| #include "RtsAttenuationModel.h" | ||
| #include "Game/Camera.h" | ||
| #include "Game/CameraHandler.h" | ||
| #include "System/Sound/ISoundAttenuationModel.h" | ||
| #include "Game/TraceRay.h" | ||
| #include <algorithm> | ||
| #include <cmath> | ||
|
|
||
| SoundAttenuationOutput RtsAttenuationModel::Evaluate(const SoundAttenuationInput& in) const { | ||
|
|
||
| SoundAttenuationOutput out; | ||
|
|
||
| // ----------------------------------------------------------------- | ||
| // Convert the soundPosition in to camera space and calculate necessary data | ||
| // ----------------------------------------------------------------- | ||
|
|
||
| { | ||
| CCamera* playerCamera = CCameraHandler::GetCamera(CCamera::CAMTYPE_PLAYER); | ||
|
|
||
| float terrainDistance = playerCamera->GetTerrainDistance(); | ||
|
|
||
| out.zoomFactor = 1 - std::clamp(terrainDistance == -1 ? 1.0f : terrainDistance / GetForwardAttenuationRange(), 0.0f, 1.0f); | ||
| out.tiltFactor = playerCamera->GetForward().dot(float3(0.0f, -1.0f, 0.0f)); | ||
|
|
||
| float3 toSound = in.soundPosition - playerCamera->GetPos(); | ||
|
|
||
| float camForward = playerCamera->GetForward().dot(toSound); | ||
| float camRight = playerCamera->GetRight().dot(toSound); | ||
| float camUp = playerCamera->GetUp().dot(toSound); | ||
|
|
||
| out.forwardDistance = camForward; | ||
|
|
||
| float hfov = playerCamera->GetHFOV() * math::DEG_TO_RAD; | ||
| float vfov = playerCamera->GetVFOV() * math::DEG_TO_RAD; | ||
|
|
||
| float distance = std::abs(camForward); | ||
| if (distance <= 0.0f) distance = 1.0f; | ||
|
|
||
| out.frustumWidth = distance * std::tan(hfov * 0.5f); | ||
| out.frustumHeight = distance * std::tan(vfov * 0.5f); | ||
|
|
||
| float outsideRight = std::max(0.0f, std::abs(camRight) - out.frustumWidth); | ||
| float outsideUp = std::max(0.0f, std::abs(camUp) - out.frustumHeight); | ||
|
|
||
| out.outerDistance = std::sqrt(outsideRight * outsideRight + outsideUp * outsideUp); | ||
| } | ||
|
|
||
| // ----------------------------------------------------------------- | ||
| // Convert all the positional data to normalized ranges and calculate the final ranges | ||
| // ----------------------------------------------------------------- | ||
|
|
||
| { | ||
| // Calculate forward attenuation | ||
| float forwardValue = std::clamp(out.forwardDistance >= 0 ? | ||
| std::lerp(GetMinVolumeAttenuation(), 1.0f, 1.0f - out.forwardDistance / GetForwardAttenuationRange()) : | ||
| std::clamp(1.0f - (-out.forwardDistance) / GetBackwardAttenuationRange(), 0.0f, 1.0f), GetMinVolumeAttenuation(), 1.0f); | ||
|
|
||
| // Calculate outer attenuation | ||
| float outerValue = std::clamp(std::lerp(GetMinFilterAttenuation(), 1.0f, 1.0f - out.outerDistance / GetOuterAttenuationRange()), GetMinFilterAttenuation(), 1.0f); | ||
|
|
||
| out.volumeFactor = std::pow(forwardValue, 6) * outerValue; | ||
| out.filterFactor = std::pow(forwardValue, 1) * outerValue; | ||
| } | ||
|
|
||
| return out; | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "System/Config/ConfigHandler.h" | ||
| #include "System/Sound/ISoundAttenuationModel.h" | ||
|
|
||
| class RtsAttenuationModel : public ISoundAttenuationModel | ||
| { | ||
| public: | ||
| SoundAttenuationOutput Evaluate(const SoundAttenuationInput& in) const override; | ||
|
|
||
| private: | ||
| float GetForwardAttenuationRange() const { return configHandler->GetFloat("snd_forwardAttenuationRange"); } | ||
| float GetBackwardAttenuationRange() const { return configHandler->GetFloat("snd_backwardAttenuationRange"); } | ||
| float GetOuterAttenuationRange() const { return configHandler->GetFloat("snd_outerAttenuationRange"); } | ||
| float GetMinVolumeAttenuation() const { return configHandler->GetFloat("snd_minVolumeAttenuation"); } | ||
| float GetMinFilterAttenuation() const { return configHandler->GetFloat("snd_minFilterAttenuation"); } | ||
| }; | ||
|
|
||
| // "RTS Model: Designed for RTS games with viewport-based attenuation, forward/backward distance handling, off-screen attenuation, and zoom-aware center attenuation. Seen in games like \"Planetary Annihilation\" or \"Supreme Commander 2\"" | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| #pragma once | ||
|
|
||
| #include "System/float3.h" | ||
| struct SoundAttenuationInput { | ||
| float3 soundPosition; | ||
| //maybe some sound specific settings like should it be resistant to attenuation | ||
| }; | ||
|
|
||
| /// All of the data used to calculate the totalFactor as well as the final totalFactor | ||
| struct SoundAttenuationOutput { | ||
| float forwardDistance; | ||
| float innerDistance; | ||
|
Majavaa marked this conversation as resolved.
Outdated
|
||
| float outerDistance; | ||
| float frustumHeight; | ||
| float frustumWidth; | ||
| float volumeFactor; | ||
| float filterFactor; | ||
| float tiltFactor; | ||
| float zoomFactor; | ||
| }; | ||
|
|
||
| class ISoundAttenuationModel { | ||
| public: | ||
| virtual ~ISoundAttenuationModel() = default; | ||
|
|
||
| virtual SoundAttenuationOutput Evaluate( | ||
| const SoundAttenuationInput& in | ||
| ) const = 0; | ||
| }; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,6 +44,8 @@ | |
|
|
||
| #include "System/float3.h" | ||
|
|
||
| #include "Rendering/GL/glExtra.h" | ||
|
|
||
|
|
||
| spring::recursive_mutex soundMutex; | ||
|
|
||
|
|
@@ -58,7 +60,6 @@ CSound::~CSound() | |
| configHandler->RemoveObserver(this); | ||
| } | ||
|
|
||
|
|
||
| void CSound::Init() | ||
| { | ||
| std::lock_guard<spring::recursive_mutex> lck(soundMutex); | ||
|
|
@@ -79,6 +80,8 @@ void CSound::Init() | |
| updateListener = false; | ||
| soundThreadQuit = false; | ||
| canLoadDefs = false; | ||
|
|
||
| attenuationModel = new RtsAttenuationModel(); | ||
|
Majavaa marked this conversation as resolved.
|
||
| } | ||
| { | ||
| Channels::General->SetVolume(configHandler->GetInt("snd_volgeneral") * 0.01f); | ||
|
|
@@ -358,7 +361,7 @@ void CSound::DeviceChanged(uint32_t sdlDeviceIndex) | |
| // In these cases, no event is emitted — SDL2 switches the active audio device internally through the OS-specific audio backend (WASAPI, PulseAudio, etc.). | ||
| // However, with certain device changes a short dropout may occur, and SDL2 will emit the SDL_AUDIODEVICEREMOVED event. | ||
| // Shortly afterwards, the default device can usually be reinitialized. | ||
|
|
||
| // This behavior can be reproduced on several test systems, for example when switching the Windows default device from monitor audio over HDMI to a sound card (HDMI->analog) | ||
|
|
||
| std::lock_guard<spring::recursive_mutex> lck(soundMutex); | ||
|
|
@@ -513,7 +516,7 @@ bool CSound::OpenSdlDevice(const std::string& deviceName, SDL_AudioSpec& obtaine | |
| desiredSpec.samples = 4096; | ||
| desiredSpec.callback = RenderSDLSamples; | ||
| desiredSpec.userdata = this; | ||
|
|
||
| /* SDL bug: can return devices with >2 channels (3D surround), even if we ask for just 2. | ||
| * This causes the 2 "primary" channels to be moved in the 3D space compared to their "normal" state | ||
| * and directional sound doesn't work anymore (though volume change with distance still does). | ||
|
|
@@ -1108,3 +1111,23 @@ std::vector<std::string> CSound::GetSoundDevices() | |
| } | ||
| return devices; | ||
| } | ||
|
|
||
| void CSound::DrawDebug() const | ||
| { | ||
| // Only draw 3D sound sources that are currently playing | ||
| for (const CSoundSource& source: soundSources) { | ||
| // Use your extracted data functions instead of OpenAL calls | ||
| if (source.IsPlaying(false) && source.IsIn3D()) { | ||
| // Get position using your accessor methods instead of OpenAL calls | ||
| float3 pos = source.GetPosition(); // Use your implemented accessor | ||
|
|
||
| // Draw a wireframe sphere at the sound source position | ||
| float color[4] = {1.0f, 0.0f, 0.0f, 0.7f}; // Red with some transparency | ||
| CMatrix44f matrix; | ||
| matrix.Translate(pos.x, pos.y, pos.z); | ||
| matrix.Scale(CSoundSource::REFERENCE_DIST * ELMOS_TO_METERS); // Adjusted scale for better visibility | ||
| GL::shapes.DrawWireSphere(8, 8, matrix, color); | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+1118
to
+1135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thread safety concern:
void CSound::DrawDebug() const
{
+ std::lock_guard<spring::recursive_mutex> lck(soundMutex);
+
// Only draw 3D sound sources that are currently playing
for (const CSoundSource& source: soundSources) {Note: This requires making
🤖 Prompt for AI Agents |
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.