Skip to content

Commit

Permalink
✨ Implement directed sounds for exhausts, turboprops and -jets
Browse files Browse the repository at this point in the history
For the sake of performance, it might make sense to cache which nodes are attached to exhausts/turboprops/-jets in the future.
  • Loading branch information
Hiradur committed Dec 21, 2024
1 parent 6e44dc1 commit df1906a
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 3 deletions.
1 change: 1 addition & 0 deletions source/main/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ CVar* audio_master_volume;
CVar* audio_enable_creak;
CVar* audio_enable_obstruction;
CVar* audio_enable_occlusion;
CVar* audio_enable_directed_sounds;
CVar* audio_enable_reflection_panning;
CVar* audio_enable_efx;
CVar* audio_engine_controls_environmental_audio;
Expand Down
1 change: 1 addition & 0 deletions source/main/Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ extern CVar* audio_master_volume;
extern CVar* audio_enable_creak;
extern CVar* audio_enable_obstruction;
extern CVar* audio_enable_occlusion;
extern CVar* audio_enable_directed_sounds;
extern CVar* audio_enable_reflection_panning;
extern CVar* audio_enable_efx;
extern CVar* audio_engine_controls_environmental_audio;
Expand Down
138 changes: 138 additions & 0 deletions source/main/audio/SoundManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include "SoundManager.h"

#include "AeroEngine.h"
#include "Application.h"
#include "IWater.h"
#include "Sound.h"
Expand Down Expand Up @@ -425,6 +426,11 @@ void SoundManager::Update(const float dt_sec)

this->UpdateListenerEffectSlot(dt_sec);
}

if (App::audio_enable_directed_sounds->getBool())
{
this->UpdateDirectedSounds();
}
}

void SoundManager::SetListener(Ogre::Vector3 position, Ogre::Vector3 direction, Ogre::Vector3 up, Ogre::Vector3 velocity)
Expand Down Expand Up @@ -1079,6 +1085,138 @@ bool SoundManager::UpdateOcclusionFilter(const int hardware_index, const ALuint
return occlusion_detected;
}

void SoundManager::UpdateDirectedSounds() const
{
for (int hardware_index = 0; hardware_index < hardware_sources_num; hardware_index++)
{
if (hardware_sources_map[hardware_index] == -1) { continue;; } // no sound assigned to hardware source at this index

const SoundPtr& corresponding_sound = audio_sources[hardware_sources_map[hardware_index]];
const ActorPtrVec& actors = App::GetGameContext()->GetActorManager()->GetActors();

for (const ActorPtr& actor : actors)
{
NodeNum_t sound_node = 0;
bool sound_belongs_to_current_actor = false;

// check if the sound corresponding to this hardware source belongs to the actor
for (int soundsource_index = 0; soundsource_index < actor->ar_num_soundsources; ++soundsource_index)
{
const soundsource_t& soundsource = actor->ar_soundsources[soundsource_index];
const int num_sounds = soundsource.ssi->getTemplate()->getNumSounds();
for (int num_sound = 0; num_sound < num_sounds; ++num_sound)
{
if (soundsource.ssi->getSound(num_sound) == corresponding_sound)
{
sound_node = soundsource.nodenum;
}
}
if (sound_node > 0) { break; }
}

// if the sound does not belong to a node of the current actor, there is no need for further checks
if (sound_node == 0) { continue; }

// Check if the sound corresponding to the hardware source is attached to an exhaust node of the actor
const std::vector<exhaust_t>& exhausts = actor->exhausts;
for (const exhaust_t& exhaust : exhausts)
{
if ( sound_node == exhaust.emitterNode
|| sound_node == exhaust.directionNode)
{
const Ogre::Vector3 emitter_node_pos = actor->getNodePosition(exhaust.emitterNode);
const Ogre::Vector3 direction_node_pos = actor->getNodePosition(exhaust.directionNode);

this->UpdateConeProperties(
hardware_sources[hardware_index],
emitter_node_pos - direction_node_pos,
60.0f,
170.0f,
0.85f,
0.80f);

break;
}
}

// Check if the sound corresponding to the hardware source is attached to an AeroEngine
for (int engine_num = 0; engine_num < actor->ar_num_aeroengines; ++engine_num)
{
const auto& aero_engine = actor->ar_aeroengines[engine_num];

if (aero_engine->getType() == AeroEngineType::AE_XPROP)
{
if ( sound_node == aero_engine->getNoderef()
|| sound_node == aero_engine->GetBackNode())
{
const Ogre::Vector3 aero_engine_ref_node = actor->getNodePosition(aero_engine->getNoderef());
const Ogre::Vector3 aero_engine_back_node = actor->getNodePosition(aero_engine->GetBackNode());

this->UpdateConeProperties(
hardware_sources[hardware_index],
aero_engine_ref_node - aero_engine_back_node,
170.0f,
270.0f,
0.85f,
0.70f);

break;
}
}
else if (aero_engine->getType() == AeroEngineType::AE_TURBOJET)
{
/*
* Since turbojets currently have no high-pitched noise sounds
* for the air intake, we currently assume all sounds are
* directed rearwards of the engine.
* Should air intake noises be added, a front-directed cone should
* be set for them with significant high-frequency dropoff outside.
*/

if ( sound_node == aero_engine->getNoderef()
|| sound_node == aero_engine->GetFrontNode())
{
const Ogre::Vector3 aero_engine_ref_node = actor->getNodePosition(aero_engine->getNoderef());
const Ogre::Vector3 aero_engine_front_node = actor->getNodePosition(aero_engine->GetFrontNode());

this->UpdateConeProperties(
hardware_sources[hardware_index],
aero_engine_ref_node - aero_engine_front_node,
60.0f,
240.0f,
0.60f,
0.60f);
}

break;
}
else { continue; }
}
}
}
}

void SoundManager::UpdateConeProperties(
const ALuint source,
const Ogre::Vector3& cone_direction,
const float cone_inner_angle,
const float cone_outer_angle,
const float cone_outer_gain,
const float cone_outer_gain_hf
) const
{
alSource3f(source, AL_DIRECTION, cone_direction.x, cone_direction.y, cone_direction.z);

alSourcef (source, AL_CONE_INNER_ANGLE, cone_inner_angle);
alSourcef (source, AL_CONE_OUTER_ANGLE, cone_outer_angle);
alSourcef (source, AL_CONE_OUTER_GAIN, cone_outer_gain);

if (App::audio_enable_efx->getBool())
{
alSourcef(source, AL_CONE_OUTER_GAINHF, cone_outer_gain_hf);
}
}

void SoundManager::recomputeSource(int source_index, int reason, float vfl, Vector3* vvec)
{
if (!audio_device)
Expand Down
23 changes: 23 additions & 0 deletions source/main/audio/SoundManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,29 @@ class SoundManager
* @return True if occlusion was detected, false otherwise.
*/
bool UpdateOcclusionFilter(const int hardware_index, const ALuint effect_slot_id, const EFXEAXREVERBPROPERTIES* reference_efx_reverb_properties) const;

/**
* Updates AL Cones for sources of directed sound emissions (exhausts, turboprops and turbojets).
*/
void UpdateDirectedSounds() const;

/**
* Updates the Cone properties for the hardware source.
* @param source The AL source of which the cone properties will be modified.
* @param cone_direction The direction the cone will face.
* @param cone_inner_angle Angle for the inner zone.
* @param cone_outer_angle Angle that marks the border between transitional and outside zone of the cone
* @param cone_outer_gain Gain for the outside zone.
* @param cone_outer_gain_hf High-frequency gain for the outside zone.
*/
void UpdateConeProperties(
const ALuint source,
const Ogre::Vector3& cone_direction,
const float cone_inner_angle,
const float cone_outer_angle,
const float cone_outer_gain,
const float cone_outer_gain_hf
) const;
};

/// @}
Expand Down
7 changes: 4 additions & 3 deletions source/main/gui/panels/GUI_GameSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,10 @@ void GameSettings::DrawAudioSettings()

if (App::audio_enable_efx->getBool())
{
DrawGCombo(App::audio_efx_reverb_engine, _LC("GameSettings", "OpenAL Reverb engine"), m_combo_items_efx_reverb_engine.c_str());
DrawGCheckbox(App::audio_enable_obstruction, _LC("GameSettings", "Sound obstruction"));
DrawGCheckbox(App::audio_enable_occlusion, _LC("GameSettings", "Sound occlusion"));
DrawGCombo(App::audio_efx_reverb_engine, _LC("GameSettings", "OpenAL Reverb engine"), m_combo_items_efx_reverb_engine.c_str());
DrawGCheckbox(App::audio_enable_obstruction, _LC("GameSettings", "Sound obstruction"));
DrawGCheckbox(App::audio_enable_occlusion, _LC("GameSettings", "Sound occlusion"));
DrawGCheckbox(App::audio_enable_directed_sounds, _LC("GameSettings", "Directed sounds (exhausts etc.)"));
if (App::audio_efx_reverb_engine->getEnum<EfxReverbEngine>() == EfxReverbEngine::EAXREVERB)
{
DrawGCheckbox(App::audio_enable_reflection_panning, _LC("GameSettings", "Early reflections panning (experimental)"));
Expand Down
2 changes: 2 additions & 0 deletions source/main/physics/air/AeroEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class AeroEngine
virtual bool getIgnition() =0;
virtual void setIgnition(bool val) =0;
virtual int getNoderef() =0;
virtual NodeNum_t GetFrontNode() const =0;
virtual NodeNum_t GetBackNode() const =0;
virtual bool getWarmup() =0;
virtual float getRadius() =0;

Expand Down
2 changes: 2 additions & 0 deletions source/main/physics/air/TurboJet.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ class Turbojet: public AeroEngine
float getThrottle();
float getpropwash() { return m_propwash; };
int getNoderef() { return m_node_back; };
NodeNum_t GetFrontNode() const override { return m_node_front; };
NodeNum_t GetBackNode() const override { return m_node_back; };
AeroEngineType getType() { return AeroEngineType::AE_TURBOJET; };

// AeroEngine visuals
Expand Down
2 changes: 2 additions & 0 deletions source/main/physics/air/TurboProp.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ class Turboprop: public AeroEngine
bool getIgnition() { return ignition; };
void setIgnition(bool val) { ignition = val; };
int getNoderef() { return noderef; };
NodeNum_t GetFrontNode() const override { return RoR::NODENUM_INVALID; }; // turboprops have no front node
NodeNum_t GetBackNode() const override { return nodeback; };
bool getWarmup() { return warmup; };
float getRadius() { return radius; };

Expand Down
1 change: 1 addition & 0 deletions source/main/system/CVar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ void Console::cVarSetupBuiltins()
App::audio_enable_creak = this->cVarCreate("audio_enable_creak", "Creak Sound", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "false");
App::audio_enable_obstruction = this->cVarCreate("audio_enable_obstruction", "Obstruction of sounds", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "false");
App::audio_enable_occlusion = this->cVarCreate("audio_enable_occlusion", "Occlusion of sounds", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "false");
App::audio_enable_directed_sounds = this->cVarCreate("audio_enable_directed_sounds", "Directed sounds", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "false");
App::audio_enable_reflection_panning = this->cVarCreate("audio_enable_reflection_panning", "Pan reflections", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "false");
App::audio_device_name = this->cVarCreate("audio_device_name", "AudioDevice", CVAR_ARCHIVE);
App::audio_doppler_factor = this->cVarCreate("audio_doppler_factor", "Doppler Factor", CVAR_ARCHIVE | CVAR_TYPE_FLOAT, "1.0");
Expand Down

0 comments on commit df1906a

Please # to comment.