2451 lines
87 KiB
C++
2451 lines
87 KiB
C++
//
|
||
// audio/emitteraudioentity.cpp
|
||
//
|
||
// Copyright (C) 1999-2006 Rockstar Games. All Rights Reserved.
|
||
//
|
||
#include "ambience/ambientaudioentity.h"
|
||
#include "ambience/audambientzone.h"
|
||
#include "emitteraudioentity.h"
|
||
#include "northaudioengine.h"
|
||
#include "audio/environment/environment.h"
|
||
#include "audio/environment/environmentgroup.h"
|
||
#include "music/musicplayer.h"
|
||
#include "radioaudioentity.h"
|
||
#include "scriptaudioentity.h"
|
||
#include "streamslot.h"
|
||
#include "weatheraudioentity.h"
|
||
#include "policescanner.h"
|
||
#include "profile/profiler.h"
|
||
|
||
#include "audioengine/engine.h"
|
||
#include "audiosoundtypes/soundcontrol.h"
|
||
#include "audioengine/environment.h"
|
||
#include "audioengine/widgets.h"
|
||
#include "audiohardware/driverdefs.h"
|
||
#include "audiohardware/streamingwaveslot.h"
|
||
#include "scene/playerswitch/PlayerSwitchInterface.h"
|
||
|
||
#include "grcore/debugdraw.h"
|
||
#include "game/clock.h"
|
||
#include "scene/scene.h"
|
||
#include "game/weather.h"
|
||
#include "scene/entity.h"
|
||
#include "objects/object.h"
|
||
#include "control/gamelogic.h"
|
||
#include "scene/RefMacros.h"
|
||
#include "scene/2dEffect.h"
|
||
#include "renderer/Lights/LightSource.h"
|
||
#include "system/filemgr.h"
|
||
#include "fwsys/timer.h"
|
||
#include "string/stringutil.h"
|
||
#include "TimeCycle/TimeCycle.h"
|
||
#include "Vfx/Misc/Coronas.h"
|
||
#include "Vfx/VfxHelper.h"
|
||
#include "camera/CamInterface.h"
|
||
#include "frontend/loadingscreens.h"
|
||
|
||
AUDIO_AMBIENCE_OPTIMISATIONS()
|
||
|
||
PF_PAGE(AudioEmitterPage, "Audio Emitters");
|
||
PF_GROUP(AudioEmitters);
|
||
PF_LINK(AudioEmitterPage, AudioEmitters);
|
||
PF_TIMER(StaticEmitterUpdate, AudioEmitters);
|
||
|
||
PF_VALUE_INT(NumStaticEmitters, AudioEmitters);
|
||
PF_VALUE_INT(NumStaticEmittersPlaying, AudioEmitters);
|
||
PF_VALUE_INT(NumEntityEmitters, AudioEmitters);
|
||
PF_VALUE_INT(NumEntityEmittersPlaying, AudioEmitters);
|
||
|
||
#include "debugaudio.h"
|
||
|
||
audEmitterAudioEntity g_EmitterAudioEntity;
|
||
|
||
// min time in ms between emitter retriggers
|
||
static const u32 g_audMinEmitterRetriggerTime = 500;
|
||
|
||
static const u8 g_MaxStaticEmitterLists = 99;
|
||
static const u8 g_MaxStaticEmitterListNameLength = 64;
|
||
static const char *g_StaticEmitterListBaseName = "AMBIENT_EMITTER_LIST_";
|
||
|
||
//Keep static emitters playing until we are 1.333 times the maximum distance away, to avoid ping-pong streaming.
|
||
static const f32 g_StaticEmitterMaxDistanceScalingForStop = 1.333f;
|
||
|
||
#if __BANK
|
||
|
||
XPARAM(audiodesigner);
|
||
|
||
char g_DrawStaticEmitterFilter[128]={0};
|
||
bool audEmitterAudioEntity::sm_ShouldDrawStaticEmitters = false;
|
||
bool audEmitterAudioEntity::sm_DrawStaticEmitterOpennessValues = false;
|
||
bool audEmitterAudioEntity::sm_ShouldDrawEntityEmitters = false;
|
||
extern char g_CreateEmitterName[128];
|
||
extern char g_DuplicateEmitterName[128];
|
||
|
||
bool g_DebugStaticEmitterEnvironmentGroups = false;
|
||
#endif
|
||
|
||
extern CPlayerSwitchInterface g_PlayerSwitch;
|
||
const u32 g_MaxEmittersLinkedToProps = 256;
|
||
u32 g_CurrentPropLinkId = 0;
|
||
u32 g_PropLinkedEmitters[g_MaxEmittersLinkedToProps];
|
||
|
||
bool g_StaticEmitterNeedsInteriorInfo = false;
|
||
|
||
f32 g_SpaceFillingInteriorDuckingVolumeLin = 0.5f;
|
||
f32 g_EmitterInteriorDuckingSmootherRate = 0.1f;
|
||
|
||
s32 StaticEmitterEntry::GetRequestedPCMChannel() const
|
||
{
|
||
// See url:bugstar:5154546 - Investigate missing emitter channel in boiler room cam captures. We only actually care
|
||
// about setting the channel on the two main_area emitters, but just hard coding all of these to ensure we never get
|
||
// into a state where other emitters have stolen all the instances of the pcm channel that we want.
|
||
if (nameHash == ATSTRINGHASH("SE_ba_dlc_int_01_main_area", 0x6772F86B) ||
|
||
nameHash == ATSTRINGHASH("SE_ba_dlc_int_01_bars", 0xC7CB4) ||
|
||
nameHash == ATSTRINGHASH("SE_ba_dlc_int_01_Bogs", 0xE9E54990) ||
|
||
nameHash == ATSTRINGHASH("SE_ba_dlc_int_01_Entry_Hall", 0x7D5E6C2))
|
||
{
|
||
return 1;
|
||
}
|
||
else if (nameHash == ATSTRINGHASH("SE_ba_dlc_int_01_main_area_2", 0xFD2A8934) ||
|
||
nameHash == ATSTRINGHASH("SE_ba_dlc_int_01_Entry_Stairs", 0xEE2730F0) ||
|
||
nameHash == ATSTRINGHASH("SE_ba_dlc_int_01_garage", 0xEE2AE5C4) ||
|
||
nameHash == ATSTRINGHASH("SE_ba_dlc_int_01_office", 0x4112DBDA) ||
|
||
nameHash == ATSTRINGHASH("SE_ba_dlc_int_01_rear_L_corridor", 0x7F43408F))
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
else if (nameHash == ATSTRINGHASH("SE_h4_dlc_int_02_h4_Bogs", 0xABAAF6E8) ||
|
||
nameHash == ATSTRINGHASH("SE_h4_dlc_int_02_h4_main_front_02", 0x15069E11) ||
|
||
nameHash == ATSTRINGHASH("SE_h4_dlc_int_02_h4_lobby", 0xA8017F05) ||
|
||
nameHash == ATSTRINGHASH("SE_h4_dlc_int_02_h4_main_bar", 0x3C27B29E))
|
||
{
|
||
return 1;
|
||
}
|
||
else if (nameHash == ATSTRINGHASH("SE_h4_dlc_int_02_h4_main_front_01", 0x43837B0A) ||
|
||
nameHash == ATSTRINGHASH("SE_h4_dlc_int_02_h4_Entrance_Doorway", 0x739B10DE) ||
|
||
nameHash == ATSTRINGHASH("SE_h4_dlc_int_02_h4_main_room_cutscenes", 0x88424B8E))
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
else if (nameHash == ATSTRINGHASH("SE_DLC_Hei4_Island_Beach_Party_Music_New_01_Left", 0x3C8BE84A))
|
||
{
|
||
return 0;
|
||
}
|
||
else if (nameHash == ATSTRINGHASH("SE_DLC_Hei4_Island_Beach_Party_Music_New_02_Right", 0xF796B9AD))
|
||
{
|
||
return 1;
|
||
}
|
||
else if (nameHash == ATSTRINGHASH("SE_DLC_Hei4_Island_Beach_Party_Music_New_03_Reverb", 0x35BDD571) ||
|
||
nameHash == ATSTRINGHASH("SE_DLC_Hei4_Island_Beach_Party_Music_New_04_Reverb", 0xBF137CE3))
|
||
{
|
||
return 2;
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
bool StaticEmitterEntry::IsHighPriorityEmitter() const
|
||
{
|
||
if (nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_Meet_rm_Music_00", 0xCAF4290A) ||
|
||
nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_Meet_rm_Music_01", 0xBD6A8DF7) ||
|
||
nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_Meet_rm_Music_02", 0x3D1C0D60) ||
|
||
nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_Meet_rm_Music_03", 0x61DD56E2) ||
|
||
nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_Meet_rm_Music_04", 0x939EBA64) ||
|
||
nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_Meet_rm_Music_05", 0x86611FE9) ||
|
||
nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_sandbox_music_01", 0x4408D5CE) ||
|
||
nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_sandbox_music_02", 0x71D5B16B) ||
|
||
nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_sandbox_viewer_area_music_01", 0x4DC334CE) ||
|
||
nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_sandbox_viewer_area_music_02", 0x5EDED705))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
if(nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_Main_rm_Vehicle_Noise_01", 0xFFB16118) ||
|
||
nameHash == ATSTRINGHASH("SE_tr_tuner_car_meet_Main_rm_Vehicle_Noise_02", 0xDB1497DF))
|
||
{
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
return false;
|
||
}
|
||
|
||
bool StaticEmitterEntry::IsClubEmitter() const
|
||
{
|
||
return emitter && GetRequestedPCMChannel() != -1;
|
||
}
|
||
|
||
void audEmitterAudioEntity::Init()
|
||
{
|
||
if(!m_PreserveEntityEmitters)
|
||
{
|
||
m_EntityEnabledList.Init(g_audMaxEntityEmitters);
|
||
m_NumEntityEmitters = 0;
|
||
m_NumEntities = 0;
|
||
m_FirstFreeBin = 0;
|
||
|
||
|
||
for(u32 i = 0; i < 255; i++)
|
||
{
|
||
m_EntityBins[i].numEntities = 0;
|
||
}
|
||
}
|
||
|
||
SetActive(false);
|
||
g_CurrentPropLinkId = 0;
|
||
m_HasInitialisedStaticEmitters = false;
|
||
m_FirstUpdate = true;
|
||
m_ConeCurve.Init("LINEAR_RISE");
|
||
m_FillsInteriorSpaceDuckingSmoother.Init(g_EmitterInteriorDuckingSmootherRate, true);
|
||
m_LastListenerPos = Vec3V(V_ZERO);
|
||
}
|
||
|
||
void audEmitterAudioEntity::AddBuildingAnimEvent(u32 hash, Vec3V_In inPos)
|
||
{
|
||
for(int i=0; i < g_audNumBuildingAnimEvents; i++ )
|
||
{
|
||
if(m_BuildingAnimEvents[i].hash == 0)
|
||
{
|
||
m_BuildingAnimEvents[i].hash = hash;
|
||
m_BuildingAnimEvents[i].pos = inPos;
|
||
return;
|
||
}
|
||
}
|
||
naAssertf(0, "Run out of building anim events in emitter audio entity");
|
||
}
|
||
|
||
void audEmitterAudioEntity::ClearBuildingAnimEvents()
|
||
{
|
||
for(int i=0; i < g_audNumBuildingAnimEvents; i++ )
|
||
{
|
||
m_BuildingAnimEvents[i].hash = 0;
|
||
}
|
||
}
|
||
|
||
bool audEmitterAudioEntity::IsWithinRangeOfRadio(const Vector3& inPosition, float fRange)
|
||
{
|
||
const float fRangeSq = rage::square(fRange);
|
||
for(u32 scan = 0 ; scan < m_CurrentStaticEmitterIndex; ++scan)
|
||
{
|
||
if(m_StaticEmitterFlags[scan].bIsEnabled)
|
||
{
|
||
if (m_StaticEmitterFlags[scan].bIsPlayingRadio)
|
||
{
|
||
if (m_StaticEmitterList[scan].position.Dist2(inPosition) < fRangeSq)
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void audEmitterAudioEntity::UpdateBuildingAnimEvents()
|
||
{
|
||
for(int i=0; i<g_audNumBuildingAnimEvents; i++)
|
||
{
|
||
if(m_BuildingAnimEvents[i].hash)
|
||
{
|
||
audSoundInitParams initParams;
|
||
initParams.Position = VEC3V_TO_VECTOR3(m_BuildingAnimEvents[i].pos);
|
||
CreateAndPlaySound(m_BuildingAnimEvents[i].hash, &initParams);
|
||
m_BuildingAnimEvents[i].hash = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::AddStaticEmitter(u32 emitterName, const u16 gameTimeMinutes)
|
||
{
|
||
StaticEmitter* emitter = audNorthAudioEngine::GetObject<StaticEmitter>(emitterName);
|
||
|
||
if(emitter)
|
||
{
|
||
audAssert(m_CurrentStaticEmitterIndex < g_audMaxStaticEmitters);
|
||
if(m_CurrentStaticEmitterIndex < g_audMaxStaticEmitters)
|
||
{
|
||
m_StaticEmitterList[m_CurrentStaticEmitterIndex].emitter = emitter;
|
||
m_StaticEmitterList[m_CurrentStaticEmitterIndex].nameHash = emitterName;
|
||
|
||
TristateValue enabledValue = AUD_GET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_STATICEMITTER_ENABLED);
|
||
m_StaticEmitterFlags[m_CurrentStaticEmitterIndex].bIsEnabled = (enabledValue != AUD_TRISTATE_FALSE);
|
||
NA_RADIO_ENABLED_ONLY(m_StaticEmitterList[m_CurrentStaticEmitterIndex].radioEmitter.SetEmitter(emitter));
|
||
m_StaticEmitterList[m_CurrentStaticEmitterIndex].position.Set(emitter->Position.x, emitter->Position.y, emitter->Position.z);
|
||
m_StaticEmitterList[m_CurrentStaticEmitterIndex].lastTriggerTime = 0;
|
||
|
||
if(AUD_GET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_STATICEMITTER_LINKEDTOPROP) == AUD_TRISTATE_TRUE)
|
||
{
|
||
Assert(g_CurrentPropLinkId < g_MaxEmittersLinkedToProps-1);
|
||
if(g_CurrentPropLinkId < g_MaxEmittersLinkedToProps-1)
|
||
{
|
||
g_PropLinkedEmitters[g_CurrentPropLinkId++] = m_CurrentStaticEmitterIndex;
|
||
m_StaticEmitterFlags[m_CurrentStaticEmitterIndex].bIsLinked = true;
|
||
m_StaticEmitterList[m_CurrentStaticEmitterIndex].nextDamageFlipTime = 0;
|
||
}
|
||
}
|
||
|
||
const u32 hospitalsRadio = ATSTRINGHASH("HOSPITALS_POLICE_SCANNER", 0x03094b005);
|
||
|
||
m_StaticEmitterFlags[m_CurrentStaticEmitterIndex].bStartStopImmediatelyAtTimeLimits = AUD_GET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_STATICEMITTER_STARTSTOPIMMEDIATELYATTIMELIMITS) != AUD_TRISTATE_FALSE;
|
||
m_StaticEmitterFlags[m_CurrentStaticEmitterIndex].bIsActive24h = (emitter->MinTimeMinutes == 0 && emitter->MaxTimeMinutes == 1440);
|
||
m_StaticEmitterFlags[m_CurrentStaticEmitterIndex].bIsScanner = (emitter->Sound == hospitalsRadio);
|
||
m_StaticEmitterFlags[m_CurrentStaticEmitterIndex].bWasValidTime = audAmbientZone::IsValidTime(m_StaticEmitterList[m_CurrentStaticEmitterIndex].emitter->MinTimeMinutes, m_StaticEmitterList[m_CurrentStaticEmitterIndex].emitter->MaxTimeMinutes, gameTimeMinutes);
|
||
m_StaticEmitterList[m_CurrentStaticEmitterIndex].occlusionGroup = NULL;
|
||
m_StaticEmitterList[m_CurrentStaticEmitterIndex].alarmSettings = audNorthAudioEngine::GetObject<AlarmSettings>(emitter->AlarmSettings);
|
||
|
||
class StreamingSoundQueryFn : public audSoundFactory::audSoundProcessHierarchyFn
|
||
{
|
||
public:
|
||
bool containsStreamingSounds;
|
||
|
||
StreamingSoundQueryFn() : containsStreamingSounds(false) {};
|
||
|
||
void operator()(u32 classID, const void* UNUSED_PARAM(soundData))
|
||
{
|
||
if(classID == StreamingSound::TYPE_ID)
|
||
{
|
||
containsStreamingSounds = true;
|
||
}
|
||
}
|
||
};
|
||
|
||
StreamingSoundQueryFn streamingSoundQuery;
|
||
SOUNDFACTORY.ProcessHierarchy(emitter->Sound, streamingSoundQuery);
|
||
m_StaticEmitterFlags[m_CurrentStaticEmitterIndex].bIsStreamingSound = streamingSoundQuery.containsStreamingSounds;
|
||
|
||
// Pack the distance values into the position W component to save fetching them from metadata each time we want to query them. Don't need to
|
||
// be super accurate with these distances, so precision loss is acceptable.
|
||
u16 minDist = 0u;
|
||
u16 maxDist = 0u;
|
||
Assign(minDist, m_StaticEmitterList[m_CurrentStaticEmitterIndex].emitter->MinDistance);
|
||
Assign(maxDist, m_StaticEmitterList[m_CurrentStaticEmitterIndex].emitter->MaxDistance);
|
||
u32 packedDistance = (minDist << 16) | maxDist;
|
||
m_StaticEmitterList[m_CurrentStaticEmitterIndex].position.SetWAsUnsignedInt(packedDistance);
|
||
|
||
m_CurrentStaticEmitterIndex++;
|
||
}
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::UpdateStaticEmitterPosition(StaticEmitter* emitter)
|
||
{
|
||
for(u32 emitterIndex=0; emitterIndex<m_CurrentStaticEmitterIndex; emitterIndex++)
|
||
{
|
||
if(emitter == m_StaticEmitterList[emitterIndex].emitter)
|
||
{
|
||
m_StaticEmitterList[emitterIndex].position.Set(emitter->Position.x, emitter->Position.y, emitter->Position.z);
|
||
}
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::ReInitStaticEmitters()
|
||
{
|
||
for(u32 loop = 0; loop < m_CurrentStaticEmitterIndex; loop++)
|
||
{
|
||
StopEmitter(loop);
|
||
m_StaticEmitterList[loop].Reset();
|
||
m_StaticEmitterFlags[loop].Reset();
|
||
}
|
||
|
||
CreateStaticEmitters();
|
||
}
|
||
|
||
void audEmitterAudioEntity::InitStaticEmitters()
|
||
{
|
||
//NOTE: This function must be called AFTER all downloadable audio content has been mounted and all game objects have been combined.
|
||
// Otherwise downloadable static emitters will be ignored.
|
||
|
||
// only do this work once
|
||
if(!m_HasInitialisedStaticEmitters)
|
||
{
|
||
CreateStaticEmitters();
|
||
m_HasInitialisedStaticEmitters = true;
|
||
naAudioEntity::Init();
|
||
audEntity::SetName("audEmitterAudioEntity");
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::CreateStaticEmitters()
|
||
{
|
||
const u16 gameTimeMinutes = static_cast<u16>((CClock::GetHour() * 60) + CClock::GetMinute());
|
||
|
||
g_CurrentPropLinkId = 0;
|
||
m_CurrentStaticEmitterIndex = 0;
|
||
char staticEmitterListName[8];
|
||
u32 partialHash;
|
||
|
||
const char* pLevelName = "gta5";
|
||
s32 levelIndex = CGameLogic::GetCurrentLevelIndex();
|
||
|
||
// Game level index value defaults to level 1 even with 0 levels loaded...
|
||
if(levelIndex < CScene::GetLevelsData().GetCount())
|
||
{
|
||
#if RSG_BANK
|
||
if(PARAM_audiodesigner.Get())
|
||
{
|
||
pLevelName = audNorthAudioEngine::GetCurrentAudioLevelName();
|
||
}
|
||
#endif
|
||
}
|
||
|
||
atHashString levelName = pLevelName;
|
||
size_t len = strlen(pLevelName);
|
||
|
||
if(levelName == "gta5" || len <= 5)
|
||
{
|
||
partialHash = atPartialStringHash(g_StaticEmitterListBaseName);
|
||
}
|
||
else
|
||
{
|
||
const char *suffix = pLevelName + 5;
|
||
char levelSpecificStaticEmitterListName[64];
|
||
formatf(levelSpecificStaticEmitterListName, "%s%s_", g_StaticEmitterListBaseName, suffix);
|
||
partialHash = atPartialStringHash(levelSpecificStaticEmitterListName);
|
||
}
|
||
|
||
for(u8 listIndex=0; listIndex<g_MaxStaticEmitterLists; listIndex++)
|
||
{
|
||
formatf(staticEmitterListName, "%02u", listIndex + 1);
|
||
|
||
StaticEmitterList *staticEmitterList = audNorthAudioEngine::GetObject<StaticEmitterList>(atStringHash(staticEmitterListName, partialHash));
|
||
if(staticEmitterList)
|
||
{
|
||
for(u32 emitterIndex=0; emitterIndex<staticEmitterList->numEmitters; emitterIndex++)
|
||
{
|
||
AddStaticEmitter(staticEmitterList->Emitter[emitterIndex].StaticEmitter, gameTimeMinutes);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::Shutdown()
|
||
{
|
||
BANK_ONLY(m_EntityEmitterDebugInfo.Reset());
|
||
|
||
SetActive(false);
|
||
for(u32 i = 0 ; i < m_CurrentStaticEmitterIndex; i++)
|
||
{
|
||
if(m_StaticEmitterFlags[i].bIsEnabled)
|
||
{
|
||
if(m_StaticEmitterList[i].sound)
|
||
{
|
||
// the controller will do this for us when the entity is destroyed but we may as well be explicit
|
||
m_StaticEmitterList[i].sound->StopAndForget();
|
||
}
|
||
NA_RADIO_ENABLED_ONLY(m_StaticEmitterList[i].radioEmitter.SetOcclusionGroup(NULL);)
|
||
if(m_StaticEmitterList[i].occlusionGroup)
|
||
{
|
||
m_StaticEmitterList[i].occlusionGroup->RemoveSoundReference();
|
||
m_StaticEmitterList[i].occlusionGroup = NULL;
|
||
}
|
||
}
|
||
}
|
||
if(!m_PreserveEntityEmitters)
|
||
{
|
||
m_EntityEnabledList.Kill();
|
||
}
|
||
audEntity::Shutdown();
|
||
}
|
||
|
||
void audEmitterAudioEntity::StopAndResetStaticEmitters()
|
||
{
|
||
for(u32 i = 0 ; i < m_CurrentStaticEmitterIndex; i++)
|
||
{
|
||
if(m_StaticEmitterList[i].sound)
|
||
{
|
||
m_StaticEmitterList[i].sound->StopAndForget();
|
||
}
|
||
|
||
#if NA_RADIO_ENABLED
|
||
g_RadioAudioEntity.StopStaticRadio(&(m_StaticEmitterList[i].radioEmitter));
|
||
m_StaticEmitterFlags[i].bIsPlayingRadio = false;
|
||
m_StaticEmitterList[i].radioEmitter.SetOcclusionGroup(NULL);
|
||
#endif
|
||
|
||
if(m_StaticEmitterList[i].occlusionGroup)
|
||
{
|
||
m_StaticEmitterList[i].occlusionGroup->RemoveSoundReference();
|
||
m_StaticEmitterList[i].occlusionGroup = NULL;
|
||
}
|
||
|
||
audStreamSlot *streamSlot = m_StaticEmitterList[i].streamSlot;
|
||
if(streamSlot)
|
||
{
|
||
streamSlot->Free();
|
||
m_StaticEmitterList[i].streamSlot = NULL;
|
||
}
|
||
|
||
m_StaticEmitterFlags[i].bWasActive = false;
|
||
m_StaticEmitterList[i].lastTriggerTime = 0;
|
||
}
|
||
|
||
for(u32 i = 0 ; i < m_EntityEmitterList.GetMaxCount(); i++)
|
||
{
|
||
if(m_EntityEmitterList[i].sound)
|
||
{
|
||
m_EntityEmitterList[i].sound->StopAndForget();
|
||
m_EntityEmitterList[i].lastTriggerTime = 0u;
|
||
m_EntityCheckTimes[i] = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::ResetTimers()
|
||
{
|
||
for(u32 i = 0 ; i < m_CurrentStaticEmitterIndex; i++)
|
||
{
|
||
m_StaticEmitterList[i].lastTriggerTime = 0;
|
||
}
|
||
|
||
for(u32 i = 0 ; i < g_audMaxEntityEmitters; i++)
|
||
{
|
||
m_EntityCheckTimes[i] = 0;
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::FindAudioBinAndIndex(CEntity *entity, u32 &bin, u32 &idx)
|
||
{
|
||
bin = 0xff;
|
||
idx = 0xff;
|
||
if(entity->GetAudioBin() != 0xff)
|
||
{
|
||
bin = entity->GetAudioBin();
|
||
for(u32 i = 0; i < m_EntityBins[bin].numEntities; i++)
|
||
{
|
||
if(m_EntityBins[bin].entities[i] == entity)
|
||
{
|
||
idx = i;
|
||
return;
|
||
}
|
||
}
|
||
naErrorf("Couldn't find emitter audio index for entity %s, even though it has a valid bin", entity->GetModelName());
|
||
}
|
||
else
|
||
{
|
||
// allocate bin/index for this entity
|
||
if(m_NumEntities < (255*g_audNumEntitiesPerBin))
|
||
{
|
||
bin = m_FirstFreeBin;
|
||
entity->SetAudioBin((u8)bin);
|
||
idx = m_EntityBins[bin].numEntities++;
|
||
m_EntityBins[bin].entities[idx] = entity;
|
||
m_EntityBins[bin].tailEmitters[idx] = 0xffff;
|
||
m_NumEntities++;
|
||
if(m_EntityBins[bin].numEntities == g_audNumEntitiesPerBin)
|
||
{
|
||
m_FirstFreeBin++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
bool audEmitterAudioEntity::GetGeneratesWindAudio(const CAudioAttr *audioAttr) const
|
||
{
|
||
EntityEmitter *emitter = audNorthAudioEngine::GetObject<EntityEmitter>(audioAttr->effectindex);
|
||
if(emitter && AUD_GET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_ENTITYEMITTER_WINDBASEDVOLUME)==AUD_TRISTATE_TRUE)
|
||
{
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void audEmitterAudioEntity::RegisterEntityEmitter(CEntity *entity, u32 hash, Vector3 &offset, Quaternion &rotation)
|
||
{
|
||
const u32 windThroughTrees = ATSTRINGHASH("EE_WIND_THROUGH_TREES", 0x894BAFB2);
|
||
const u32 windThroughTrees2 = ATSTRINGHASH("EE_WIND_THROUGH_TREES_HUGE", 0x8C9873DC);
|
||
const u32 windThroughTrees3 = ATSTRINGHASH("EE_WIND_THROUGH_TREES_SMALL", 0xF9CA1086);
|
||
|
||
const u32 aircon1 = ATSTRINGHASH("EE_AIRCON_FAN_TINY", 0xDFD650E);
|
||
const u32 aircon2 = ATSTRINGHASH("EE_AIRCON_FAN_SMALL", 0xCDCC1905);
|
||
const u32 aircon3 = ATSTRINGHASH("EE_AIRCON_FAN_SMALL_ONE_WAY", 0xC311584C);
|
||
const u32 alarmEmitter = ATSTRINGHASH("EE_ALARM", 0xAFAB9C80);
|
||
|
||
const u32 generatorWithLightsSound = ATSTRINGHASH("EE_GENERATOR_MED_03", 0xC9D3D33E);
|
||
const u32 generatorWithLightsModel = ATSTRINGHASH("prop_generator_03b", 0xFC96F411);
|
||
|
||
DEBUG_STREAMING_ONLY(bool found = false);
|
||
|
||
const u32 pickup1 = ATSTRINGHASH("EE_DREYFUSS_PAPER", 0x868F6E86);
|
||
const u32 pickup2 = ATSTRINGHASH("EE_OMEGA_POWER_CELLS", 0x30EF1A34);
|
||
|
||
const u32 radioEmitter = ATSTRINGHASH("EE_RADIO", 0x438EC5FD);
|
||
|
||
const u32 stuntProp1 = ATSTRINGHASH("EE_DLC_STUNT_TUBE_ROTATING_ARM", 0xCCD6C2EA);
|
||
const u32 stuntProp2 = ATSTRINGHASH("EE_DLC_STUNT_TUBE_ROTATING_ARM_CENTRE", 0xF77A996E);
|
||
|
||
const bool isCritical = (hash == radioEmitter || hash == pickup1 || hash == pickup2);
|
||
const bool frequentUpdates = (hash == stuntProp1 || hash == stuntProp2);
|
||
if(m_NumEntityEmitters > (g_audMaxEntityEmitters-16) && !isCritical)
|
||
{
|
||
// Ensure we leave space for 'critical' emitters
|
||
return;
|
||
}
|
||
|
||
// don't spawn entity emitters for fragments of prop_generator_03b
|
||
if(entity->GetModelNameHash() == generatorWithLightsModel && hash == generatorWithLightsSound && entity->GetOwnedBy() == ENTITY_OWNEDBY_FRAGMENT_CACHE)
|
||
{
|
||
return;
|
||
}
|
||
|
||
#if __BANK
|
||
audEntityEmitterDebugInfo& emitterInfo = m_EntityEmitterDebugInfo.Grow();
|
||
emitterInfo.hash = hash;
|
||
emitterInfo.entity = entity;
|
||
emitterInfo.offset = offset;
|
||
#endif
|
||
|
||
if(hash == alarmEmitter || hash == radioEmitter)
|
||
{
|
||
naCErrorf(entity->GetType() == ENTITY_TYPE_OBJECT, "Entity %s is not an object", entity->GetModelName());
|
||
Vector3 searchPos = entity->TransformIntoWorldSpace(offset);
|
||
for(u32 i = 0 ; i < g_CurrentPropLinkId; i++)
|
||
{
|
||
if(!m_StaticEmitterList[g_PropLinkedEmitters[i]].isScriptLinkedEmitter)
|
||
{
|
||
// do we have a static emitter within a 4.25m radius?
|
||
if((m_StaticEmitterList[g_PropLinkedEmitters[i]].position - searchPos).Mag2() < 18.f
|
||
&& ((m_StaticEmitterList[g_PropLinkedEmitters[i]].alarmSettings && hash == alarmEmitter) || (hash == radioEmitter)))
|
||
{
|
||
m_StaticEmitterList[g_PropLinkedEmitters[i]].linkedObject = entity;
|
||
#if AUD_DEBUG_STREAMING
|
||
found = true;
|
||
naDisplayf("Found linked prop for emitter %u at pos: %f %f %f [emitter: %f %f %f]", g_PropLinkedEmitters[i], searchPos.x, searchPos.y, searchPos.z, m_StaticEmitterList[g_PropLinkedEmitters[i]].position.x, m_StaticEmitterList[g_PropLinkedEmitters[i]].position.y, m_StaticEmitterList[g_PropLinkedEmitters[i]].position.z);
|
||
#endif
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
#if AUD_DEBUG_STREAMING
|
||
if(!found)
|
||
{
|
||
naWarningf("Failed to find linked prop for emitter at pos: %f %f %f", searchPos.x, searchPos.y, searchPos.z);
|
||
}
|
||
#endif
|
||
|
||
}
|
||
else
|
||
{
|
||
if(hash != windThroughTrees && hash != windThroughTrees2 && hash != windThroughTrees3)
|
||
{
|
||
const Vector3 pos = entity->TransformIntoWorldSpace(offset);
|
||
|
||
// ignore small aircons high up
|
||
if(hash == aircon1 || hash == aircon3 || hash == aircon2)
|
||
{
|
||
if(pos.z > 49.f)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
if(naVerifyf(m_NumEntityEmitters < g_audMaxEntityEmitters, "Too many entity emitters (limit is %d, trying to add hash %u)", g_audMaxEntityEmitters, hash))
|
||
{
|
||
EntityEmitter *emitter = audNorthAudioEngine::GetObject<EntityEmitter>(hash);
|
||
if(emitter && ShouldEntityEmitterBeRegistered(emitter, pos))
|
||
{
|
||
u32 bin, entityIndex;
|
||
FindAudioBinAndIndex(entity, bin, entityIndex);
|
||
if(bin!=0xff)
|
||
{
|
||
for(u32 i = 0; i < g_audMaxEntityEmitters; i++)
|
||
{
|
||
if(!IsEntityEnabled(i))
|
||
{
|
||
if(emitter && AUD_GET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_ENTITYEMITTER_VOLUMECONE) == AUD_TRISTATE_TRUE)
|
||
{
|
||
m_EntityEmitterList[i].rotation.Set(QUATERNION_TO_QUATV(rotation));
|
||
}
|
||
|
||
m_EntityEmitterList[i].offset = offset;
|
||
m_EntityEmitterList[i].emitter = emitter;
|
||
m_EntityEmitterList[i].lastTriggerTime = fwTimer::GetTimeInMilliseconds();
|
||
m_EntityEmitterList[i].sound = NULL;
|
||
m_EntityEmitterList[i].entity = entity;
|
||
m_EntityEmitterList[i].frequentUpdates = frequentUpdates;
|
||
|
||
Assign(m_EntityEmitterList[i].prevEmitterIdx, m_EntityBins[bin].tailEmitters[entityIndex]);
|
||
Assign(m_EntityBins[bin].tailEmitters[entityIndex], i);
|
||
|
||
// derive a 'random' seed based on world position
|
||
const u32 mag = (u32)floorf(pos.Mag()*100.f);
|
||
const u32 idx = mag % 10000;
|
||
const f32 randomSeed = (idx / 10000.0f);
|
||
m_EntityEmitterList[i].randomSeed = randomSeed;
|
||
m_NumEntityEmitters++;
|
||
m_EntityCheckTimes[i] = 0;
|
||
m_EntityEnabledList.Set(i);
|
||
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::UnregisterEmittersForEntity(CEntity *entity)
|
||
{
|
||
#if __BANK
|
||
for(u32 i = 0; i < m_EntityEmitterDebugInfo.GetCount(); )
|
||
{
|
||
if(m_EntityEmitterDebugInfo[i].entity == entity)
|
||
{
|
||
m_EntityEmitterDebugInfo.DeleteFast(i);
|
||
}
|
||
else
|
||
{
|
||
i++;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if(entity->GetAudioBin() != 0xff)
|
||
{
|
||
u32 entityIdx = 0;
|
||
const u32 bin = entity->GetAudioBin();
|
||
for(; entityIdx < m_EntityBins[bin].numEntities; entityIdx++)
|
||
{
|
||
if(m_EntityBins[bin].entities[entityIdx] == entity)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
if(naVerifyf(entityIdx<m_EntityBins[bin].numEntities, "Entity index %d is invalid", entityIdx))
|
||
{
|
||
m_EntityBins[bin].entities[entityIdx] = NULL;
|
||
|
||
// remove all of the emitters for this entity
|
||
u32 idx = m_EntityBins[bin].tailEmitters[entityIdx];
|
||
m_EntityBins[bin].tailEmitters[entityIdx] = 0xffff;
|
||
|
||
while(idx < g_audMaxEntityEmitters /*!= 0xffff*/)
|
||
{
|
||
naAssertf(m_EntityEmitterList[idx].entity == entity, "Entity %s has a valid index but doesn't match the entity in the emitter list", entity->GetModelName());
|
||
naCErrorf(m_EntityEmitterList[idx].prevEmitterIdx != idx, "Index %d is same as the previous index", idx);
|
||
//Assert(m_StaticEmitterFlags[idx].bIsEnabled);
|
||
|
||
m_EntityEnabledList.Clear(idx);
|
||
|
||
if(m_EntityEmitterList[idx].sound)
|
||
{
|
||
m_EntityEmitterList[idx].sound->StopAndForget();
|
||
}
|
||
m_EntityEmitterList[idx].entity = NULL;
|
||
naAssertf(m_NumEntityEmitters, "Can't have less than 0 entity emitters!");
|
||
m_NumEntityEmitters--;
|
||
u32 temp = idx;
|
||
idx = m_EntityEmitterList[idx].prevEmitterIdx;
|
||
m_EntityEmitterList[temp].prevEmitterIdx = 0xffff;
|
||
}
|
||
|
||
// shuffle entities around in this bin if required (ie its not already the last one)
|
||
if(entityIdx != static_cast<u32>(m_EntityBins[bin].numEntities)-1)
|
||
{
|
||
// move the last one into this slot
|
||
m_EntityBins[bin].entities[entityIdx] = m_EntityBins[bin].entities[static_cast<u32>(m_EntityBins[bin].numEntities)-1];
|
||
m_EntityBins[bin].tailEmitters[entityIdx] = m_EntityBins[bin].tailEmitters[static_cast<u32>(m_EntityBins[bin].numEntities)-1];
|
||
|
||
m_EntityBins[bin].entities[static_cast<u32>(m_EntityBins[bin].numEntities)-1] = NULL;
|
||
m_EntityBins[bin].tailEmitters[static_cast<u32>(m_EntityBins[bin].numEntities)-1] = 0xffff;
|
||
}
|
||
m_EntityBins[bin].numEntities--;
|
||
m_NumEntities--;
|
||
entity->SetAudioBin(0xff);
|
||
|
||
// shuffle this bin around if <20>t's below the first free bin
|
||
if(bin < m_FirstFreeBin)
|
||
{
|
||
// swap with the last full bin
|
||
m_FirstFreeBin--;
|
||
tEntityBin tempBin = m_EntityBins[m_FirstFreeBin];
|
||
m_EntityBins[m_FirstFreeBin] = m_EntityBins[bin];
|
||
m_EntityBins[bin] = tempBin;
|
||
|
||
// update all entities in the bins that have been moved
|
||
for(u32 i = 0; i < m_EntityBins[m_FirstFreeBin].numEntities; i++)
|
||
{
|
||
naAssertf(m_EntityBins[m_FirstFreeBin].entities[i], "Invalid entity in bin %d", m_FirstFreeBin);
|
||
naCErrorf(m_EntityBins[m_FirstFreeBin].entities[i]->GetAudioBin() == bin, "Entity %s has a bin %d which doesn't match expected bin %d", entity->GetModelName(), m_EntityBins[m_FirstFreeBin].entities[i]->GetAudioBin(), bin);
|
||
|
||
m_EntityBins[m_FirstFreeBin].entities[i]->SetAudioBin((u8)m_FirstFreeBin);
|
||
}
|
||
for(u32 i = 0; i < m_EntityBins[bin].numEntities; i++)
|
||
{
|
||
naAssertf(m_EntityBins[bin].entities[i], "Invalid entity in bin %d", bin);
|
||
naCErrorf(m_EntityBins[bin].entities[i]->GetAudioBin() == m_FirstFreeBin, "Entity %s has a bin %d which doesn't match expected bin %d", entity->GetModelName(), m_EntityBins[bin].entities[i]->GetAudioBin(), m_FirstFreeBin);
|
||
m_EntityBins[bin].entities[i]->SetAudioBin((u8)bin);
|
||
}
|
||
}
|
||
}
|
||
entity->SetAudioBin(0xff);
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::Update()
|
||
{
|
||
if(m_IsActive)
|
||
{
|
||
UpdateStaticEmitters();
|
||
UpdateEntityEmitters();
|
||
UpdateBuildingAnimEvents();
|
||
m_FirstUpdate = false;
|
||
}
|
||
|
||
PF_SET(NumStaticEmitters, m_CurrentStaticEmitterIndex);
|
||
PF_SET(NumEntityEmitters, m_NumEntityEmitters);
|
||
#if __BANK
|
||
if(sm_ShouldDrawStaticEmitters || sm_ShouldDrawEntityEmitters)
|
||
{
|
||
DrawDebug();
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void audEmitterAudioEntity::UpdateStaticEmitters()
|
||
{
|
||
PF_FUNC(StaticEmitterUpdate);
|
||
|
||
m_FillsInteriorSpaceDuckingSmoother.SetRate(g_EmitterInteriorDuckingSmootherRate);
|
||
float duckLinear = 1.f;
|
||
if(g_ScriptAudioEntity.ShouldDuckForScriptedConversation())
|
||
{
|
||
duckLinear = m_FillsInteriorSpaceDuckingSmoother.CalculateValue(g_SpaceFillingInteriorDuckingVolumeLin, fwTimer::GetTimeStep());
|
||
}
|
||
else
|
||
{
|
||
duckLinear = m_FillsInteriorSpaceDuckingSmoother.CalculateValue(1.f, fwTimer::GetTimeStep());
|
||
}
|
||
const float fillsInteriorSpaceDucking = audDriverUtil::ComputeDbVolumeFromLinear(duckLinear);
|
||
|
||
u32 numStaticEmittersPlaying = 0;
|
||
const u32 timeInMs = fwTimer::GetTimeInMilliseconds();
|
||
const bool playerSwitchActive = g_PlayerSwitch.IsActive();
|
||
const bool gameWorldHidden = camInterface::IsFadedOut() || CLoadingScreens::AreActive();
|
||
const u16 gameTimeMinutes = static_cast<u16>((CClock::GetHour() * 60) + CClock::GetMinute());
|
||
|
||
for(u32 i = 0 ; i < m_CurrentStaticEmitterIndex; i++)
|
||
{
|
||
const bool updateThisFrame = (fwTimer::GetFrameCount()&3) == (i&3) || m_StaticEmitterList[i].IsClubEmitter();
|
||
|
||
// Script linked emitters follow around the attached object
|
||
if(updateThisFrame && m_StaticEmitterFlags[i].bIsEnabled && m_StaticEmitterList[i].isScriptLinkedEmitter)
|
||
{
|
||
UpdateScriptPositionedEmitter(i);
|
||
}
|
||
|
||
if(m_StaticEmitterFlags[i].bIsEnabled && (m_StaticEmitterFlags[i].bWasActive || updateThisFrame))
|
||
{
|
||
bool shouldPlay = ShouldStaticEmitterBePlaying(i, playerSwitchActive, gameWorldHidden, gameTimeMinutes);
|
||
if(shouldPlay)
|
||
{
|
||
const Vector3 pos(m_StaticEmitterList[i].emitter->Position.x, m_StaticEmitterList[i].emitter->Position.y, m_StaticEmitterList[i].emitter->Position.z);
|
||
|
||
// Update the environmentGroup and create one if we don't have one
|
||
if(!m_StaticEmitterList[i].occlusionGroup)
|
||
{
|
||
m_StaticEmitterList[i].occlusionGroup = CreateStaticEmitterEnvironmentGroup(m_StaticEmitterList[i].emitter, pos);
|
||
if(m_StaticEmitterList[i].occlusionGroup)
|
||
{
|
||
SetStaticEmitterInteriorInfo(i);
|
||
|
||
#if NA_RADIO_ENABLED
|
||
// Set the radio to reference the same environmentGroup. We'll use the m_StaticEmitterList[i].occlusionGroup to maintain the correct refs.
|
||
m_StaticEmitterList[i].radioEmitter.SetOcclusionGroup(m_StaticEmitterList[i].occlusionGroup);
|
||
#endif
|
||
|
||
// We don't always have a sound going, so hold a reference so we don't lose the group when a scanner/radio is trying to play
|
||
m_StaticEmitterList[i].occlusionGroup->AddSoundReference();
|
||
}
|
||
}
|
||
|
||
if(m_StaticEmitterList[i].occlusionGroup)
|
||
{
|
||
UpdateStaticEmitterEnvironmentGroup(i);
|
||
}
|
||
|
||
NA_RADIO_ENABLED_ONLY(f32 opennessCutoff = kVoiceFilterLPFMaxCutoff);
|
||
|
||
if(m_StaticEmitterFlags[i].bIsScanner)
|
||
{
|
||
#if NA_POLICESCANNER_ENABLED
|
||
if(timeInMs > m_StaticEmitterList[i].lastTriggerTime + 10000)
|
||
{
|
||
g_PoliceScanner.TriggerRandomMedicalReport(pos, m_StaticEmitterList[i].occlusionGroup);
|
||
m_StaticEmitterList[i].lastTriggerTime = timeInMs;
|
||
}
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
#if NA_RADIO_ENABLED
|
||
if(m_StaticEmitterFlags[i].bIsLinked && m_StaticEmitterList[i].linkedObject)
|
||
{
|
||
Vector3 linkedObjectPos = VEC3V_TO_VECTOR3(m_StaticEmitterList[i].linkedObject->GetTransform().GetPosition());
|
||
m_StaticEmitterList[i].radioEmitter.SetPosition(linkedObjectPos);
|
||
|
||
if(m_StaticEmitterList[i].sound)
|
||
{
|
||
m_StaticEmitterList[i].sound->SetRequestedPosition(linkedObjectPos);
|
||
}
|
||
|
||
Matrix34 objMat;
|
||
m_StaticEmitterList[i].linkedObject->GetMatrixCopy(objMat);
|
||
Vector3 vDown(0.f,0.f,1.f);
|
||
f32 dp = DotProduct(objMat.b, vDown);
|
||
dp = Max(0.f,dp);
|
||
|
||
const f32 openness = 1.f - dp;
|
||
opennessCutoff = 4000.f + (kVoiceFilterLPFMaxCutoff - 4000.f) * openness;
|
||
|
||
if(m_StaticEmitterList[i].damageSound)
|
||
{
|
||
const f32 minFreq = 2000.f;
|
||
const f32 maxFreq = kVoiceFilterLPFMaxCutoff;
|
||
const f32 freq = minFreq + (openness * (maxFreq - minFreq));
|
||
m_StaticEmitterList[i].damageSound->SetRequestedLPFCutoff((u32)freq);
|
||
}
|
||
|
||
#if __BANK
|
||
if(sm_DrawStaticEmitterOpennessValues)
|
||
{
|
||
char buf[128];
|
||
formatf(buf, sizeof(buf), "dp: %f open: %f", dp, openness);
|
||
grcDebugDraw::Text(objMat.d,Color32(255,255,0), buf);
|
||
}
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
m_StaticEmitterList[i].radioEmitter.SetPosition(pos);
|
||
}
|
||
#endif // NA_RDIO_ENABLED
|
||
|
||
f32 emittedVolume = (f32)m_StaticEmitterList[i].emitter->EmittedVolume * 0.01f;//Convert from mB to dB.
|
||
if(AUD_GET_TRISTATE_VALUE(m_StaticEmitterList[i].emitter->Flags, FLAG_ID_STATICEMITTER_FILLSINTERIORSPACE)==AUD_TRISTATE_TRUE)
|
||
{
|
||
emittedVolume += fillsInteriorSpaceDucking;
|
||
}
|
||
|
||
if(m_StaticEmitterFlags[i].bIsLinked)
|
||
{
|
||
if(m_StaticEmitterList[i].linkedObject && m_StaticEmitterList[i].linkedObject->GetType() == ENTITY_TYPE_OBJECT)
|
||
{
|
||
const f32 health = ((CObject*)m_StaticEmitterList[i].linkedObject.Get())->GetHealth();
|
||
const f32 howHealthy = health*0.001f;
|
||
const f32 undamagedHealth = m_StaticEmitterList[i].emitter->UndamagedHealth;
|
||
const f32 brokenHealth = m_StaticEmitterList[i].emitter->BrokenHealth;
|
||
|
||
if(howHealthy < brokenHealth)
|
||
{
|
||
if(!m_StaticEmitterFlags[i].bWasBrokenLastFrame)
|
||
{
|
||
audSoundInitParams initParams;
|
||
initParams.EnvironmentGroup = m_StaticEmitterList[i].occlusionGroup;
|
||
initParams.Tracker = m_StaticEmitterList[i].linkedObject->GetPlaceableTracker();
|
||
CreateAndPlaySound(m_StaticEmitterList[i].emitter->OnBreakOneShot, &initParams);
|
||
m_StaticEmitterFlags[i].bWasBrokenLastFrame = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
m_StaticEmitterFlags[i].bWasBrokenLastFrame = false;
|
||
}
|
||
|
||
if(howHealthy > undamagedHealth)
|
||
{
|
||
if(m_StaticEmitterList[i].damageSound)
|
||
{
|
||
// stop playing damage
|
||
m_StaticEmitterList[i].damageSound->StopAndForget();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if(howHealthy < brokenHealth)
|
||
{
|
||
emittedVolume = -100.f;
|
||
if(m_StaticEmitterList[i].damageSound)
|
||
{
|
||
// stop playing damage
|
||
m_StaticEmitterList[i].damageSound->StopAndForget();
|
||
}
|
||
}
|
||
else if(!m_StaticEmitterList[i].alarmSettings)
|
||
{
|
||
if(timeInMs > m_StaticEmitterList[i].nextDamageFlipTime)
|
||
{
|
||
if(m_StaticEmitterList[i].damageSound)
|
||
{
|
||
// stop playing damage
|
||
m_StaticEmitterList[i].damageSound->StopAndForget();
|
||
|
||
// as damage increases, time between damage decreases
|
||
m_StaticEmitterList[i].nextDamageFlipTime = timeInMs + audEngineUtil::GetRandomNumberInRange(50, (s32)(howHealthy * 2000.f));
|
||
|
||
}
|
||
else
|
||
{
|
||
if(howHealthy < undamagedHealth)
|
||
{
|
||
audSoundInitParams initParams;
|
||
initParams.EnvironmentGroup = m_StaticEmitterList[i].occlusionGroup;
|
||
initParams.Tracker = m_StaticEmitterList[i].linkedObject->GetPlaceableTracker();
|
||
CreateAndPlaySound_Persistent(g_RadioAudioEntity.GetRadioSounds().Find(ATSTRINGHASH("EmitterDamage", 0x4EFF0991)),
|
||
&m_StaticEmitterList[i].damageSound, &initParams);
|
||
}
|
||
m_StaticEmitterList[i].nextDamageFlipTime = timeInMs + audEngineUtil::GetRandomNumberInRange((s32)((1.f-howHealthy) * 2000.f), (s32)(howHealthy * 1000.f));
|
||
}
|
||
}
|
||
if(m_StaticEmitterList[i].damageSound)
|
||
{
|
||
emittedVolume -= 4.f;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#if NA_RADIO_ENABLED
|
||
m_StaticEmitterList[i].radioEmitter.SetEmittedVolume(emittedVolume);
|
||
m_StaticEmitterList[i].radioEmitter.SetLPFCutoff(Min((u32)m_StaticEmitterList[i].emitter->FilterCutoff,(u32)opennessCutoff));
|
||
m_StaticEmitterList[i].radioEmitter.SetRolloffFactor(m_StaticEmitterList[i].emitter->RolloffFactor * 0.01f);
|
||
m_StaticEmitterList[i].radioEmitter.SetHPFCutoff(m_StaticEmitterList[i].emitter->HPFCutoff);
|
||
#endif
|
||
|
||
if(!m_StaticEmitterList[i].alarmSettings)
|
||
{
|
||
const bool muteForScore = (AUD_GET_TRISTATE_VALUE(m_StaticEmitterList[i].emitter->Flags, FLAG_ID_STATICEMITTER_MUTEFORSCORE)==AUD_TRISTATE_TRUE);
|
||
if(muteForScore)
|
||
{
|
||
emittedVolume += g_InteractiveMusicManager.GetStaticRadioEmitterVolumeOffset();
|
||
}
|
||
}
|
||
|
||
numStaticEmittersPlaying++;
|
||
if(!m_StaticEmitterList[i].sound && fwTimer::GetTimeInMilliseconds() > m_StaticEmitterList[i].lastTriggerTime + g_audMinEmitterRetriggerTime)
|
||
{
|
||
m_StaticEmitterList[i].lastTriggerTime = fwTimer::GetTimeInMilliseconds();
|
||
|
||
if(m_StaticEmitterList[i].emitter->Sound && (m_StaticEmitterList[i].emitter->Sound != g_NullSoundHash))
|
||
{
|
||
audSoundInitParams initParams;
|
||
initParams.EnvironmentGroup = m_StaticEmitterList[i].occlusionGroup;
|
||
|
||
if(m_StaticEmitterFlags[i].bIsStreamingSound)
|
||
{
|
||
Assign(initParams.BucketId, audNorthAudioEngine::GetBucketIdForStreamingSounds());
|
||
}
|
||
|
||
if(!m_StaticEmitterList[i].alarmSettings)
|
||
{
|
||
//Only go up to 80% to avoid wasting disc access.
|
||
initParams.StartOffset = audEngineUtil::GetRandomNumberInRange(0, 80);
|
||
initParams.IsStartOffsetPercentage = true;
|
||
}
|
||
|
||
CreateSound_PersistentReference(m_StaticEmitterList[i].emitter->Sound, &m_StaticEmitterList[i].sound, &initParams);
|
||
if(m_StaticEmitterList[i].sound)
|
||
{
|
||
if( audNorthAudioEngine::GetMicrophones().IsSniping() && AUD_GET_TRISTATE_VALUE(m_StaticEmitterList[i].emitter->Flags, FLAG_ID_STATICEMITTER_IGNORESNIPER)==AUD_TRISTATE_TRUE)
|
||
{
|
||
m_StaticEmitterList[i].sound->SetRequestedListenerMask(1);
|
||
}
|
||
// Wave slots for alarm sounds are prepared externally
|
||
if(m_StaticEmitterList[i].alarmSettings)
|
||
{
|
||
m_StaticEmitterList[i].sound->SetRequestedPosition(pos);
|
||
f32 requestedVol = (f32)m_StaticEmitterList[i].emitter->EmittedVolume / 100.0f; //Convert mB to dB.
|
||
m_StaticEmitterList[i].sound->SetRequestedVolume(requestedVol);
|
||
m_StaticEmitterList[i].sound->SetRequestedLPFCutoff((u32)m_StaticEmitterList[i].emitter->FilterCutoff);
|
||
m_StaticEmitterList[i].sound->PrepareAndPlay();
|
||
}
|
||
else
|
||
{
|
||
if(m_StaticEmitterFlags[i].bIsStreamingSound)
|
||
{
|
||
audStreamClientSettings settings;
|
||
settings.priority = (u8)(m_StaticEmitterList[i].IsHighPriorityEmitter() ? audStreamSlot::STREAM_PRIORITY_SCRIPT_AMBIENT_EMITTER : audStreamSlot::STREAM_PRIORITY_STATIC_AMBIENT_EMITTER);
|
||
settings.stopCallback = &OnStopCallback;
|
||
settings.hasStoppedCallback = &HasStoppedCallback;
|
||
settings.userData = i;
|
||
m_StaticEmitterList[i].streamSlot = audStreamSlot::AllocateSlot(&settings);
|
||
}
|
||
|
||
if(!m_StaticEmitterFlags[i].bIsStreamingSound ||m_StaticEmitterList[i].streamSlot)
|
||
{
|
||
m_StaticEmitterList[i].sound->SetRequestedPosition(pos);
|
||
m_StaticEmitterList[i].sound->SetRequestedVolume(emittedVolume);
|
||
m_StaticEmitterList[i].sound->SetRequestedLPFCutoff((u32)m_StaticEmitterList[i].emitter->FilterCutoff);
|
||
|
||
if(m_StaticEmitterFlags[i].bIsStreamingSound)
|
||
{
|
||
m_StaticEmitterList[i].sound->PrepareAndPlay(m_StaticEmitterList[i].streamSlot->GetWaveSlot(), true, -1);
|
||
}
|
||
else
|
||
{
|
||
m_StaticEmitterList[i].sound->PrepareAndPlay();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//We don't have a slot to stream from so clean up our sound and try again next frame.
|
||
m_StaticEmitterList[i].sound->StopAndForget();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#if NA_RADIO_ENABLED
|
||
else if(!g_RadioAudioEntity.IsStaticRadioEmitterActive(&(m_StaticEmitterList[i].radioEmitter)))
|
||
{
|
||
const u32 radioStation = GetRadioStationForEmitter(i);
|
||
m_StaticEmitterList[i].radioEmitter.SetIsClubEmitter(m_StaticEmitterList[i].IsClubEmitter());
|
||
m_StaticEmitterList[i].radioEmitter.SetIsHighPriorityEmitter(m_StaticEmitterList[i].IsHighPriorityEmitter());
|
||
|
||
#if __BANK
|
||
if (m_StaticEmitterList[i].IsClubEmitter())
|
||
{
|
||
audDisplayf("[CLUB EMITTERS] %s is active, attempting to retune to station %s", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(m_StaticEmitterList[i].emitter->NameTableOffset), audRadioStation::FindStation(radioStation) ? audRadioStation::FindStation(radioStation)->GetName() : "NULL");
|
||
}
|
||
#endif
|
||
|
||
//See if we have a radio station to play.
|
||
if(g_RadioAudioEntity.RequestStaticRadio(&(m_StaticEmitterList[i].radioEmitter), radioStation, m_StaticEmitterList[i].GetRequestedPCMChannel()))
|
||
{
|
||
m_StaticEmitterList[i].playingRadioStation = GetRadioStationForEmitter(i);
|
||
m_StaticEmitterFlags[i].bIsPlayingRadio = true;
|
||
|
||
#if __BANK
|
||
if (m_StaticEmitterList[i].IsClubEmitter())
|
||
{
|
||
audDisplayf("[CLUB EMITTERS] %s retune successful, now playing station %s", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(m_StaticEmitterList[i].emitter->NameTableOffset), audRadioStation::FindStation(radioStation) ? audRadioStation::FindStation(m_StaticEmitterList[i].playingRadioStation)->GetName() : "NULL");
|
||
}
|
||
#endif
|
||
}
|
||
#if __BANK
|
||
else
|
||
{
|
||
if (m_StaticEmitterList[i].IsClubEmitter())
|
||
{
|
||
audDisplayf("[CLUB EMITTERS] %s failed to retune!", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(m_StaticEmitterList[i].emitter->NameTableOffset));
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
#endif // NA_RADIO_ENABLED
|
||
}
|
||
|
||
if(m_StaticEmitterList[i].sound)
|
||
{
|
||
m_StaticEmitterList[i].sound->SetRequestedVolume(emittedVolume);
|
||
if( audNorthAudioEngine::GetMicrophones().IsSniping() && AUD_GET_TRISTATE_VALUE(m_StaticEmitterList[i].emitter->Flags, FLAG_ID_STATICEMITTER_IGNORESNIPER)==AUD_TRISTATE_TRUE)
|
||
{
|
||
m_StaticEmitterList[i].sound->SetRequestedListenerMask(1);
|
||
}
|
||
}
|
||
}
|
||
|
||
m_StaticEmitterFlags[i].bWasActive = true;
|
||
}
|
||
else if((m_StaticEmitterFlags[i].bIsPlayingRadio || m_StaticEmitterList[i].sound || m_StaticEmitterFlags[i].bIsScanner || m_StaticEmitterFlags[i].bWasActive || m_StaticEmitterList[i].occlusionGroup)
|
||
&& ShouldStaticEmitterBeStopped(i, gameTimeMinutes))
|
||
{
|
||
if(m_StaticEmitterList[i].sound)
|
||
{
|
||
m_StaticEmitterList[i].sound->StopAndForget();
|
||
//We have finished with our stream slot, so free it.
|
||
audStreamSlot *streamSlot = m_StaticEmitterList[i].streamSlot;
|
||
if(streamSlot)
|
||
{
|
||
streamSlot->Free();
|
||
m_StaticEmitterList[i].streamSlot = NULL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
#if NA_RADIO_ENABLED
|
||
#if __BANK
|
||
audRadioStation* oldStation = audRadioStation::FindStation(m_StaticEmitterList[i].playingRadioStation);
|
||
audRadioStation* newStation = audRadioStation::FindStation(GetRadioStationForEmitter(i));
|
||
#endif
|
||
|
||
g_RadioAudioEntity.StopStaticRadio(&(m_StaticEmitterList[i].radioEmitter));
|
||
m_StaticEmitterFlags[i].bIsPlayingRadio = false;
|
||
|
||
#if __BANK
|
||
if (m_StaticEmitterList[i].IsClubEmitter())
|
||
{
|
||
audDisplayf("[CLUB EMITTERS] %s was stopped! Station switched %s -> %s, Disabled by script: %s, Missing linked object: %s, Invalid time: %s",
|
||
audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(m_StaticEmitterList[i].emitter->NameTableOffset),
|
||
oldStation ? oldStation->GetName() : "NULL",
|
||
newStation ? newStation->GetName() : "NULL",
|
||
(AUD_GET_TRISTATE_VALUE(m_StaticEmitterList[i].emitter->Flags, FLAG_ID_STATICEMITTER_DISABLEDBYSCRIPT) == AUD_TRISTATE_TRUE) ? "true" : "false",
|
||
(m_StaticEmitterFlags[i].bIsLinked && !m_StaticEmitterList[i].linkedObject) ? "true" : "false",
|
||
(!audAmbientZone::IsValidTime(m_StaticEmitterList[i].emitter->MinTimeMinutes, m_StaticEmitterList[i].emitter->MaxTimeMinutes, gameTimeMinutes)) ? "true" : "false");
|
||
}
|
||
#endif
|
||
#endif // NA_RADIO_ENABLED
|
||
}
|
||
|
||
NA_RADIO_ENABLED_ONLY(m_StaticEmitterList[i].radioEmitter.SetOcclusionGroup(NULL);)
|
||
if(m_StaticEmitterList[i].occlusionGroup)
|
||
{
|
||
m_StaticEmitterList[i].occlusionGroup->RemoveSoundReference();
|
||
m_StaticEmitterList[i].occlusionGroup = NULL;
|
||
}
|
||
|
||
m_StaticEmitterFlags[i].bWasActive = false;
|
||
}
|
||
}
|
||
//else if(!m_StaticEmitterFlags[i].bIsEnabled)
|
||
//{
|
||
// //Ensure we stop and clean up this emitter.
|
||
// if(m_StaticEmitterList[i].sound)
|
||
// {
|
||
// m_StaticEmitterList[i].sound->StopAndForget();
|
||
// //We have finished with our stream slot, so free it.
|
||
// audStreamSlot *streamSlot = m_StaticEmitterList[i].streamSlot;
|
||
// if(streamSlot)
|
||
// {
|
||
// streamSlot->Free();
|
||
// m_StaticEmitterList[i].streamSlot = NULL;
|
||
// }
|
||
// }
|
||
// else
|
||
// {
|
||
// g_RadioAudioEntity.StopStaticRadio(&(m_StaticEmitterList[i].radioEmitter));
|
||
// naEnvironmentGroup *occlusionGroup = m_StaticEmitterList[i].radioEmitter.GetOcclusionGroup();
|
||
// if(occlusionGroup)
|
||
// {
|
||
// occlusionGroup->RemoveSoundReference();
|
||
// m_StaticEmitterList[i].radioEmitter.SetOcclusionGroup(NULL);
|
||
// }
|
||
// }
|
||
//}
|
||
}
|
||
|
||
PF_SET(NumStaticEmittersPlaying,numStaticEmittersPlaying);
|
||
}
|
||
|
||
void audEmitterAudioEntity::CheckStaticEmittersForInteriorInfo()
|
||
{
|
||
if (g_StaticEmitterNeedsInteriorInfo)
|
||
{
|
||
// look through all our statics, and if they don't have interior info, grab it
|
||
for(u32 i = 0 ; i < m_CurrentStaticEmitterIndex; i++)
|
||
{
|
||
if(m_StaticEmitterFlags[i].bIsEnabled && m_StaticEmitterFlags[i].bNeedsInteriorInfo && !m_StaticEmitterList[i].linkedObject)
|
||
{
|
||
naEnvironmentGroup *occlusionGroup = m_StaticEmitterList[i].occlusionGroup;
|
||
if(occlusionGroup)
|
||
{
|
||
Vector3 pos(m_StaticEmitterList[i].emitter->Position.x, m_StaticEmitterList[i].emitter->Position.y,
|
||
m_StaticEmitterList[i].emitter->Position.z);
|
||
s32 roomIdx = 0;
|
||
CInteriorInst* pIntInst = NULL;
|
||
CPortalTracker::ProbeForInterior(pos, pIntInst, roomIdx, NULL, 20.0f);
|
||
occlusionGroup->SetInteriorInfoWithInteriorInstance(pIntInst, roomIdx);
|
||
m_StaticEmitterFlags[i].bNeedsInteriorInfo = false;
|
||
}
|
||
}
|
||
}
|
||
g_StaticEmitterNeedsInteriorInfo = false;
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::UpdateEntityEmitters()
|
||
{
|
||
BANK_ONLY(u32 numSoundsPlaying = 0;)
|
||
if(g_AudioEngine.IsAudioEnabled())
|
||
{
|
||
const u32 timeMs = fwTimer::GetTimeInMilliseconds();
|
||
const bool isRaining = (g_weather.GetTimeCycleAdjustedRain() > 0.0f);
|
||
const bool playerSwitchActive = g_PlayerSwitch.IsActive();
|
||
|
||
Vec3V listenerPos = g_AudioEngine.GetEnvironment().GetPanningListenerPosition(0);
|
||
if(playerSwitchActive && g_PlayerSwitch.GetSwitchType() == CPlayerSwitchInterface::SWITCH_TYPE_LONG)
|
||
{
|
||
listenerPos = g_PlayerSwitch.GetLongRangeMgr().GetDestPos();
|
||
}
|
||
|
||
const bool listenerHasJumped = IsGreaterThan(MagSquared(listenerPos - m_LastListenerPos), ScalarV(Vec::V4VConstantSplat<0x43C80000>())).Getb(); // 400
|
||
m_LastListenerPos = listenerPos;
|
||
|
||
u32 jumpCheckTimer = 0;
|
||
|
||
for(u32 i = 0 ; i < g_audMaxEntityEmitters; i++)
|
||
{
|
||
if(m_EntityEnabledList.IsSet(i))
|
||
{
|
||
#if __BANK
|
||
if(sm_ShouldDrawEntityEmitters)
|
||
{
|
||
if(m_EntityEmitterList[i].sound)
|
||
{
|
||
numSoundsPlaying++;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
// When the listener jumps in a single frame, force a quick update on any active emitters
|
||
// spread over a few frames if necessary
|
||
if(listenerHasJumped)
|
||
{
|
||
m_EntityCheckTimes[i] = timeMs + jumpCheckTimer++;
|
||
}
|
||
|
||
if(timeMs > m_EntityCheckTimes[i])
|
||
{
|
||
if(m_EntityEmitterList[i].sound && m_EntityEmitterList[i].ShouldStopOnRain() && isRaining)
|
||
{
|
||
m_EntityEmitterList[i].sound->StopAndForget();
|
||
}
|
||
bool shouldPlay = ShouldEntityEmitterBePlaying(i, true, playerSwitchActive);
|
||
if(shouldPlay)
|
||
{
|
||
if(!m_EntityEmitterList[i].sound && fwTimer::GetTimeInMilliseconds() > m_EntityEmitterList[i].lastTriggerTime + g_audMinEmitterRetriggerTime)
|
||
{
|
||
Vector3 pos;
|
||
audCompressedQuat orientation;
|
||
orientation.Init();
|
||
GetEntityEmitterPositionAndOrientation(i, pos,orientation);
|
||
m_EntityEmitterList[i].lastTriggerTime = fwTimer::GetTimeInMilliseconds();
|
||
|
||
naEnvironmentGroup *occlusionGroup = NULL;
|
||
if(m_EntityEmitterList[i].IsOccluded())
|
||
{
|
||
occlusionGroup = CreateEntityEmitterEnvironmentGroup(&m_EntityEmitterList[i], pos, m_EntityEmitterList[i].emitter->MaxPathDepth);
|
||
}
|
||
|
||
|
||
CreateSound_PersistentReference(m_EntityEmitterList[i].emitter->Sound, &m_EntityEmitterList[i].sound);
|
||
|
||
if(m_EntityEmitterList[i].sound)
|
||
{
|
||
m_EntityEmitterList[i].sound->FindAndSetVariableValue(ATSTRINGHASH("randomSeed", 0x69AB332B), m_EntityEmitterList[i].randomSeed);
|
||
|
||
m_EntityEmitterList[i].sound->GetRequestedSettings()->SetEnvironmentGroup(occlusionGroup);
|
||
m_EntityEmitterList[i].sound->SetClientVariable(i | (1U<<31U));
|
||
m_EntityEmitterList[i].sound->SetRequestedPosition(pos);
|
||
m_EntityEmitterList[i].sound->SetRequestedOrientation(orientation);
|
||
m_EntityEmitterList[i].sound->SetUpdateEntity(true);
|
||
m_EntityEmitterList[i].sound->SetRequestedVolume(ComputeEntityEmitterConeAttenuation(i));
|
||
m_EntityEmitterList[i].sound->PrepareAndPlay();
|
||
}
|
||
else
|
||
{
|
||
if(g_AudioEngine.IsAudioEnabled())
|
||
{
|
||
NOTFINAL_ONLY(naWarningf("Entity Emitter (%s:%u) with invalid sound ref (%u)", audNorthAudioEngine::GetMetadataManager().GetNameFromNTO_Debug(m_EntityEmitterList[i].emitter->NameTableOffset), i, m_EntityEmitterList[i].emitter->Sound);)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if(!shouldPlay)
|
||
{
|
||
if(m_EntityEmitterList[i].sound)
|
||
{
|
||
m_EntityEmitterList[i].sound->StopAndForget();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
BANK_ONLY(PF_SET(NumEntityEmittersPlaying,numSoundsPlaying);)
|
||
}
|
||
|
||
f32 audEmitterAudioEntity::ComputeEntityEmitterConeAttenuation(u32 emitterIdx)
|
||
{
|
||
if(m_EntityEmitterList[emitterIdx].IsConed())
|
||
{
|
||
Matrix34 offset;
|
||
Matrix34 mat;
|
||
|
||
// build up matrix for this emitter
|
||
offset.d = m_EntityEmitterList[emitterIdx].offset;
|
||
offset.Identity3x3();
|
||
|
||
QuatV uncompressedQuatV;
|
||
m_EntityEmitterList[emitterIdx].rotation.Get(uncompressedQuatV);
|
||
offset.FromQuaternion(QUATV_TO_QUATERNION(uncompressedQuatV));
|
||
|
||
mat.Set(offset);
|
||
Matrix34 entityMatrix;
|
||
m_EntityEmitterList[emitterIdx].entity->GetMatrixCopy(entityMatrix);
|
||
mat.Dot(entityMatrix);
|
||
|
||
// Get the position of the microphone
|
||
// TODO: listener id is hardcoded to 0 at the mo...discuss
|
||
Vector3 listenerPosition = VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetPanningListenerPosition(0));
|
||
// Turn that into a position relative to the sound source
|
||
Vector3 translatedPosition = listenerPosition - mat.d;
|
||
|
||
Vector3 forwards = mat.b;
|
||
float dotProduct = (translatedPosition.DotV(forwards)).x;
|
||
float lengths = translatedPosition.Mag() * mat.b.Mag();
|
||
float angle = 0.0f;
|
||
if (lengths != 0.0f)
|
||
{
|
||
angle = AcosfSafe(dotProduct/lengths);
|
||
}
|
||
// This'll be in radians, turn it into degrees
|
||
angle *= RtoD;
|
||
if(!naVerifyf(FPIsFinite(angle), "Infinite angle returning 0.f"))
|
||
{
|
||
return 0.0f;
|
||
}
|
||
while (angle<0.0f)
|
||
{
|
||
angle += 360.0f;
|
||
}
|
||
|
||
return m_ConeCurve.CalculateRescaledValue(0.0f, m_EntityEmitterList[emitterIdx].emitter->ConeMaxAtten,
|
||
m_EntityEmitterList[emitterIdx].emitter->ConeInnerAngle,
|
||
m_EntityEmitterList[emitterIdx].emitter->ConeOuterAngle,
|
||
angle);
|
||
}
|
||
return 0.0f;
|
||
}
|
||
|
||
bool audEmitterAudioEntity::ShouldEntityEmitterBeRegistered(EntityEmitter *emitter, const Vector3& pos)
|
||
{
|
||
f32 probability;
|
||
// check time of day
|
||
s32 hour = CClock::GetHour();
|
||
if(hour > 9 && hour < 18)
|
||
{
|
||
probability = emitter->BusinessHoursProb;
|
||
}
|
||
|
||
else if(hour > 18 && hour < 23)
|
||
{
|
||
probability = emitter->EveningProb;
|
||
}
|
||
else
|
||
{
|
||
probability = emitter->NightProb;
|
||
}
|
||
if(AUD_GET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_ENTITYEMITTER_MORELIKELYWHENHOT) == AUD_TRISTATE_TRUE)
|
||
{
|
||
// if the temperature is greater than 20 degrees, then scale probability up by as much as 2
|
||
// up to 40 degrees
|
||
float temp = g_weather.GetTemperature(RCC_VEC3V(pos));
|
||
if(temp >= 20.f)
|
||
{
|
||
f32 scale = Clamp((temp - 20.f) / 20.f,0.0f,1.0f);
|
||
probability *= 1.0f + scale;
|
||
}
|
||
}
|
||
return audEngineUtil::ResolveProbability(probability);
|
||
}
|
||
|
||
u32 audEmitterAudioEntity::GetRadioStationForEmitter(const u32 emitterIdx) const
|
||
{
|
||
if(m_StaticEmitterList[emitterIdx].emitter->RadioStationForScore != 0 && g_InteractiveMusicManager.IsMusicPlaying())
|
||
{
|
||
const u32 radioStation = m_StaticEmitterList[emitterIdx].emitter->RadioStationForScore;
|
||
if(radioStation == ATSTRINGHASH("OFF", 0x77E9145) || audRadioStation::FindStation(radioStation))
|
||
{
|
||
return m_StaticEmitterList[emitterIdx].emitter->RadioStationForScore;
|
||
}
|
||
}
|
||
return m_StaticEmitterList[emitterIdx].emitter->RadioStation;
|
||
}
|
||
|
||
bool audEmitterAudioEntity::ShouldStaticEmitterBePlaying(u32 emitterIdx, const bool playerSwitchActive, const bool gameWorldHidden, const u16 gameTimeMinutes)
|
||
{
|
||
bool shouldPlay = true;
|
||
|
||
if(m_StaticEmitterList[emitterIdx].alarmSettings && !g_AmbientAudioEntity.IsAlarmActive(m_StaticEmitterList[emitterIdx].alarmSettings))
|
||
{
|
||
shouldPlay = false;
|
||
}
|
||
else if(m_StaticEmitterFlags[emitterIdx].bIsPlayingRadio &&
|
||
GetRadioStationForEmitter(emitterIdx) == ATSTRINGHASH("OFF", 0x77E9145))
|
||
{
|
||
shouldPlay = false;
|
||
}
|
||
else if(m_StaticEmitterFlags[emitterIdx].bIsPlayingRadio && m_StaticEmitterList[emitterIdx].playingRadioStation != 0 &&
|
||
m_StaticEmitterList[emitterIdx].playingRadioStation != GetRadioStationForEmitter(emitterIdx))
|
||
{
|
||
// Stop an emitter that is playing the wrong station
|
||
shouldPlay = false;
|
||
}
|
||
else if(m_StaticEmitterFlags[emitterIdx].bIsLinked && !m_StaticEmitterList[emitterIdx].linkedObject)
|
||
{
|
||
shouldPlay = false;
|
||
}
|
||
|
||
// Emitter must wait until the distance becomes invalid before it can switch states
|
||
if(!m_StaticEmitterFlags[emitterIdx].bStartStopImmediatelyAtTimeLimits && !m_StaticEmitterFlags[emitterIdx].bIsActive24h)
|
||
{
|
||
bool isValidDistance = true;
|
||
bool isValidTime = true;
|
||
bool isValidOnlyAtSwitchDestination = false;
|
||
|
||
u32 packedMinMaxDistance = m_StaticEmitterList[emitterIdx].position.GetWAsUnsignedInt();
|
||
u32 minDistanceSquared = (packedMinMaxDistance >> 16) * (packedMinMaxDistance >> 16);
|
||
u16 maxDistanceSquared = (packedMinMaxDistance & 0xFFFF) * (packedMinMaxDistance & 0xFFFF);
|
||
|
||
Vector3 relativePosition = m_StaticEmitterList[emitterIdx].position;
|
||
relativePosition.Subtract(MAT34V_TO_MATRIX34(g_AudioEngine.GetEnvironment().GetVolumeListenerMatrix()).d);
|
||
s32 dist2 = static_cast<s32>(relativePosition.Mag2());
|
||
|
||
if(dist2 < minDistanceSquared || dist2 > maxDistanceSquared)
|
||
{
|
||
if(!playerSwitchActive)
|
||
{
|
||
isValidDistance = false;
|
||
}
|
||
// If we're doing a camera switch, emitters are valid at the target position too
|
||
else if(g_PlayerSwitch.GetSwitchType() == CPlayerSwitchInterface::SWITCH_TYPE_LONG)
|
||
{
|
||
relativePosition = m_StaticEmitterList[emitterIdx].position;
|
||
relativePosition.Subtract(VEC3V_TO_VECTOR3(g_PlayerSwitch.GetLongRangeMgr().GetDestPos()));
|
||
dist2 = static_cast<s32>(relativePosition.Mag2());
|
||
|
||
if(dist2 < minDistanceSquared || dist2 > maxDistanceSquared)
|
||
{
|
||
isValidDistance = false;
|
||
}
|
||
else
|
||
{
|
||
isValidOnlyAtSwitchDestination = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// If the camera is faded out, we can start emitters freely. Also, emitters active at the far end of a long range switch are allowed to turn on immediately
|
||
if(gameWorldHidden || m_FirstUpdate || isValidOnlyAtSwitchDestination)
|
||
{
|
||
isValidTime = audAmbientZone::IsValidTime(m_StaticEmitterList[emitterIdx].emitter->MinTimeMinutes, m_StaticEmitterList[emitterIdx].emitter->MaxTimeMinutes, gameTimeMinutes);
|
||
shouldPlay &= isValidTime;
|
||
}
|
||
// If we're outside the max distance, then the valid time parameter is allowed to alter
|
||
if(!isValidDistance)
|
||
{
|
||
isValidTime = audAmbientZone::IsValidTime(m_StaticEmitterList[emitterIdx].emitter->MinTimeMinutes, m_StaticEmitterList[emitterIdx].emitter->MaxTimeMinutes, gameTimeMinutes);
|
||
shouldPlay = false;
|
||
}
|
||
// If not, then use our cached time
|
||
else
|
||
{
|
||
isValidTime = m_StaticEmitterFlags[emitterIdx].bWasValidTime;
|
||
shouldPlay &= isValidTime;
|
||
}
|
||
|
||
m_StaticEmitterFlags[emitterIdx].bWasValidTime = isValidTime;
|
||
}
|
||
// Emitter is allowed to switch on/off immediately when the valid time changes
|
||
else if(shouldPlay)
|
||
{
|
||
shouldPlay = audAmbientZone::IsValidTime(m_StaticEmitterList[emitterIdx].emitter->MinTimeMinutes, m_StaticEmitterList[emitterIdx].emitter->MaxTimeMinutes, gameTimeMinutes);
|
||
|
||
if(shouldPlay && !m_StaticEmitterList[emitterIdx].isScriptLinkedEmitter)
|
||
{
|
||
u32 packedMinMaxDistance = m_StaticEmitterList[emitterIdx].position.GetWAsUnsignedInt();
|
||
u32 minDistanceSquared = (packedMinMaxDistance >> 16) * (packedMinMaxDistance >> 16);
|
||
u16 maxDistanceSquared = (packedMinMaxDistance & 0xFFFF) * (packedMinMaxDistance & 0xFFFF);
|
||
|
||
Vector3 relativePosition = m_StaticEmitterList[emitterIdx].position;
|
||
relativePosition.Subtract(MAT34V_TO_MATRIX34(g_AudioEngine.GetEnvironment().GetVolumeListenerMatrix()).d);
|
||
s32 dist2 = static_cast<s32>(relativePosition.Mag2());
|
||
|
||
if(dist2 < minDistanceSquared || dist2 > maxDistanceSquared)
|
||
{
|
||
if(!playerSwitchActive)
|
||
{
|
||
shouldPlay = false;
|
||
}
|
||
// If we're doing a camera switch, emitters are valid at the target position too
|
||
else if(g_PlayerSwitch.GetSwitchType() == CPlayerSwitchInterface::SWITCH_TYPE_LONG
|
||
|| g_PlayerSwitch.GetSwitchType() == CPlayerSwitchInterface::SWITCH_TYPE_MEDIUM)
|
||
{
|
||
relativePosition = m_StaticEmitterList[emitterIdx].position;
|
||
relativePosition.Subtract(VEC3V_TO_VECTOR3(g_PlayerSwitch.GetLongRangeMgr().GetDestPos()));
|
||
dist2 = static_cast<s32>(relativePosition.Mag2());
|
||
|
||
if(dist2 < minDistanceSquared || dist2 > maxDistanceSquared)
|
||
{
|
||
shouldPlay = false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
shouldPlay = false;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if(shouldPlay && AUD_GET_TRISTATE_VALUE(m_StaticEmitterList[emitterIdx].emitter->Flags, FLAG_ID_STATICEMITTER_DISABLEDBYSCRIPT) == AUD_TRISTATE_TRUE)
|
||
{
|
||
shouldPlay = false;
|
||
}
|
||
|
||
return shouldPlay;
|
||
}
|
||
|
||
bool audEmitterAudioEntity::ShouldStaticEmitterBeStopped(u32 emitterIdx, const u16 gameTimeMinutes)
|
||
{
|
||
if(m_StaticEmitterList[emitterIdx].alarmSettings &&
|
||
!g_AmbientAudioEntity.IsAlarmActive(m_StaticEmitterList[emitterIdx].alarmSettings))
|
||
{
|
||
return true;
|
||
}
|
||
else if(m_StaticEmitterFlags[emitterIdx].bIsPlayingRadio &&
|
||
GetRadioStationForEmitter(emitterIdx) == ATSTRINGHASH("OFF", 0x77E9145))
|
||
{
|
||
return true;
|
||
}
|
||
else if(m_StaticEmitterFlags[emitterIdx].bIsPlayingRadio && m_StaticEmitterList[emitterIdx].playingRadioStation != 0 &&
|
||
m_StaticEmitterList[emitterIdx].playingRadioStation != GetRadioStationForEmitter(emitterIdx))
|
||
{
|
||
NOTFINAL_ONLY(audDisplayf("Retuning emitter %s to station %s", audNorthAudioEngine::GetMetadataManager().GetNameFromNTO_Debug(m_StaticEmitterList[emitterIdx].emitter->NameTableOffset), audRadioStation::FindStation(GetRadioStationForEmitter(emitterIdx))->GetName());)
|
||
return true;
|
||
}
|
||
else if(m_StaticEmitterFlags[emitterIdx].bIsLinked && !m_StaticEmitterList[emitterIdx].linkedObject)
|
||
{
|
||
return true;
|
||
}
|
||
else if(!audAmbientZone::IsValidTime(m_StaticEmitterList[emitterIdx].emitter->MinTimeMinutes, m_StaticEmitterList[emitterIdx].emitter->MaxTimeMinutes, gameTimeMinutes))
|
||
{
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
u32 packedMinMaxDistance = m_StaticEmitterList[emitterIdx].position.GetWAsUnsignedInt();
|
||
u16 maxDistance = (packedMinMaxDistance & 0xFFFF);
|
||
|
||
Vector3 rel(m_StaticEmitterList[emitterIdx].position);
|
||
rel.Subtract(MAT34V_TO_MATRIX34(g_AudioEngine.GetEnvironment().GetVolumeListenerMatrix()).d);
|
||
f32 dist2 = rel.Mag2();
|
||
|
||
if(dist2 > ((maxDistance * g_StaticEmitterMaxDistanceScalingForStop) * (maxDistance * g_StaticEmitterMaxDistanceScalingForStop)))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
|
||
if(AUD_GET_TRISTATE_VALUE(m_StaticEmitterList[emitterIdx].emitter->Flags, FLAG_ID_STATICEMITTER_DISABLEDBYSCRIPT) == AUD_TRISTATE_TRUE)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
bool audEmitterAudioEntity::ShouldEntityEmitterBePlaying(u32 emitterIdx, bool updateCheckTime, const bool playerSwitchActive)
|
||
{
|
||
bool active = true;
|
||
|
||
if(m_EntityEmitterList[emitterIdx].OnlyWhenRaining())
|
||
{
|
||
if(!g_weather.IsRaining())
|
||
{
|
||
active = false;
|
||
}
|
||
}
|
||
|
||
// Hacky fix for url:bugstar:5510148
|
||
if(m_EntityEmitterList[emitterIdx].entity && m_EntityEmitterList[emitterIdx].entity->GetModelNameHash() == ATSTRINGHASH("xs_propintxmas_tree_2018", 0x4665D8A) && !m_EntityEmitterList[emitterIdx].entity->IsVisible())
|
||
{
|
||
active = false;
|
||
}
|
||
|
||
if(m_EntityEmitterList[emitterIdx].ShouldStopOnRain() && g_weather.GetTimeCycleAdjustedRain() > 0.0f)
|
||
{
|
||
active = false;
|
||
}
|
||
|
||
if(audNorthAudioEngine::GetLastLoudSoundTime() + m_EntityEmitterList[emitterIdx].emitter->StopAfterLoudSound > fwTimer::GetTimeInMilliseconds())
|
||
{
|
||
active = false;
|
||
}
|
||
|
||
if(m_EntityEmitterList[emitterIdx].emitter->BrokenHealth >= 0.0f)
|
||
{
|
||
if(m_EntityEmitterList[emitterIdx].entity && m_EntityEmitterList[emitterIdx].entity->GetIsTypeObject())
|
||
{
|
||
CObject* object = (CObject*)m_EntityEmitterList[emitterIdx].entity;
|
||
f32 healthFraction = object->GetHealth()/object->GetMaxHealth();
|
||
|
||
if(healthFraction <= m_EntityEmitterList[emitterIdx].emitter->BrokenHealth)
|
||
{
|
||
active = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
if(active)
|
||
{
|
||
Vector3 emitterPosition;
|
||
GetEntityEmitterPosition(emitterIdx, emitterPosition);
|
||
|
||
// compute distance to listener
|
||
Vector3 relativePosition = emitterPosition;
|
||
relativePosition.Subtract(MAT34V_TO_MATRIX34(g_AudioEngine.GetEnvironment().GetVolumeListenerMatrix()).d);
|
||
f32 dist2 = relativePosition.Mag2();
|
||
const f32 maxDist2 = m_EntityEmitterList[emitterIdx].emitter->MaxDistance * m_EntityEmitterList[emitterIdx].emitter->MaxDistance;
|
||
f32 diff2 = dist2 - maxDist2;
|
||
active = (dist2 <= maxDist2);
|
||
|
||
// If we're doing a camera switch, emitters are valid at the target position too
|
||
if(playerSwitchActive)
|
||
{
|
||
if(g_PlayerSwitch.GetSwitchType() == CPlayerSwitchInterface::SWITCH_TYPE_LONG)
|
||
{
|
||
relativePosition = emitterPosition;
|
||
relativePosition.Subtract(VEC3V_TO_VECTOR3(g_PlayerSwitch.GetLongRangeMgr().GetDestPos()));
|
||
dist2 = relativePosition.Mag2();
|
||
diff2 = dist2 - maxDist2;
|
||
active |= (dist2 <= maxDist2);
|
||
}
|
||
}
|
||
|
||
if(updateCheckTime)
|
||
{
|
||
// Damage-able items need more frequent updates whilst they're playing
|
||
if((m_EntityEmitterList[emitterIdx].emitter->BrokenHealth >= 0.0f && m_EntityEmitterList[emitterIdx].sound) || m_EntityEmitterList[emitterIdx].frequentUpdates)
|
||
{
|
||
m_EntityCheckTimes[emitterIdx] = fwTimer::GetTimeInMilliseconds() + 250;
|
||
}
|
||
// pick a random next update time, larger if we're further away , +7s if we're playing
|
||
else
|
||
{
|
||
// scale the time to next check by the proximity to the emitter radius
|
||
f32 timeScale = Clamp(diff2 / (100.f*100.f), 0.0f, 1.0f) * 5.f;
|
||
m_EntityCheckTimes[emitterIdx] = fwTimer::GetTimeInMilliseconds() + (u32)(audEngineUtil::GetRandomNumberInRange(250.f + (timeScale * 1250.f),500.f + (timeScale * 3500.f))) + (active?7000:0);
|
||
}
|
||
}
|
||
return active;
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::UpdateLightAudio(CLightEntity* lightEntity, CEntity *entity, const C2dEffect *effect, const bool isLightOn, const u32 slotIndex)
|
||
{
|
||
// audio update doesnt run when the game is paused but prerender apparently does.
|
||
if( fwTimer::IsGamePaused())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if(!isLightOn)
|
||
{
|
||
return;
|
||
}
|
||
|
||
const CLightAttr* l = effect->GetLight();
|
||
|
||
if(l)
|
||
{
|
||
static const u16 RandomSeedRandomiser[8] = { 00000, 0064632, 0125525, 0146146,0175757, 0113045, 0052652, 0114315};
|
||
u16 localRandomSeed = (u16) (((size_t)entity) / 4);
|
||
u32 localRandomSeed2 = (u32) (((size_t)lightEntity) / 256);
|
||
localRandomSeed ^= RandomSeedRandomiser[localRandomSeed2 % 8];
|
||
const bool isFlashy = ((((localRandomSeed & 255) <= 16) && l->m_flashiness == FL_RANDOM_FLASHINESS) || l->m_flashiness == FL_RANDOM || l->m_flashiness == FL_RANDOM_OVERRIDE_IF_WET);
|
||
|
||
if((l->m_flags & LIGHTFLAG_FORCE_BUZZING) || ((l->m_flags & LIGHTFLAG_ENABLE_BUZZING) && isFlashy) || ((l->m_flags & LIGHTFLAG_ENABLE_BUZZING) && (((localRandomSeed>>8) & 255) <= 14)))
|
||
{
|
||
const bool active = !entity->m_nFlags.bRenderDamaged;
|
||
if(active)
|
||
{
|
||
Matrix34 boneMtx;
|
||
CVfxHelper::GetMatrixFromBoneTag(RC_MAT34V(boneMtx), entity, (eAnimBoneTag)l->m_boneTag);
|
||
|
||
if (!boneMtx.a.IsZero())
|
||
{
|
||
Vector3 srcpos;
|
||
effect->GetPos(srcpos);
|
||
Vector3 pos = boneMtx * srcpos;
|
||
// we know that bit 0 is set, so subtract 1 to keep in range [0,3]
|
||
u32 soundIndex = ((localRandomSeed|1) & 3) - 1;
|
||
static const u32 sounds[] = {
|
||
ATSTRINGHASH("AMBIENCE_NEON_NEON_1", 0x0944d782a),
|
||
ATSTRINGHASH("AMBIENCE_NEON_NEON_2", 0x0ac1da7ca),
|
||
ATSTRINGHASH("AMBIENCE_NEON_NEON_3", 0x078203fd0)};
|
||
fwUniqueObjId uniqueId = fwIdKeyGenerator::Get(entity, slotIndex);
|
||
|
||
g_AmbientAudioEntity.RegisterEffectAudio(sounds[soundIndex], uniqueId, pos, entity);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
void audEmitterAudioEntity::GetEntityEmitterPosition(u32 emitterIdx, Vector3 &pos)
|
||
{
|
||
// transform emitter position to world space using entity's matrix
|
||
naAssertf(emitterIdx < g_audMaxEntityEmitters, "Emitter index %d out of bounds", emitterIdx);
|
||
m_EntityEmitterList[emitterIdx].entity->TransformIntoWorldSpace(pos, m_EntityEmitterList[emitterIdx].offset);
|
||
}
|
||
void audEmitterAudioEntity::GetEntityEmitterPositionAndOrientation(u32 emitterIdx, Vector3 &pos,audCompressedQuat &orientation)
|
||
{
|
||
// transform emitter position to world space using entity's matrix
|
||
naAssertf(emitterIdx < g_audMaxEntityEmitters, "Emitter index %d out of bounds", emitterIdx);
|
||
m_EntityEmitterList[emitterIdx].entity->TransformIntoWorldSpace(pos, m_EntityEmitterList[emitterIdx].offset);
|
||
if(m_EntityEmitterList[emitterIdx].entity->GetPlaceableTracker())
|
||
{
|
||
orientation = m_EntityEmitterList[emitterIdx].entity->GetPlaceableTracker()->GetOrientation();
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::UpdateSound(audSound *UNUSED_PARAM(sound), audRequestedSettings *reqSets, u32 UNUSED_PARAM(timeInMs))
|
||
{
|
||
u32 var;
|
||
reqSets->GetClientVariable(var);
|
||
if(var & (1U<<31U))
|
||
{
|
||
Vector3 pos;
|
||
audCompressedQuat orientation;
|
||
orientation.Init();
|
||
|
||
u32 idx = var & ~(1U<<31U);
|
||
GetEntityEmitterPositionAndOrientation(idx, pos, orientation);
|
||
reqSets->SetPosition(pos);
|
||
reqSets->SetOrientation(orientation);
|
||
f32 coneVol = ComputeEntityEmitterConeAttenuation(idx);
|
||
reqSets->SetVolume(coneVol);
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::NotifyLoudSound()
|
||
{
|
||
// only do this once per frame
|
||
static u32 frame = 0;
|
||
if(g_AudioEngine.IsAudioEnabled() && frame != fwTimer::GetSystemFrameCount())
|
||
{
|
||
frame = fwTimer::GetSystemFrameCount();
|
||
for(u32 i = 0 ; i < g_audMaxEntityEmitters; i++)
|
||
{
|
||
if(m_EntityEnabledList.IsSet(i) && m_EntityEmitterList[i].sound && m_EntityEmitterList[i].emitter->StopAfterLoudSound > 0)
|
||
{
|
||
m_EntityEmitterList[i].sound->StopAndForget();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
naEnvironmentGroup* audEmitterAudioEntity::CreateEntityEmitterEnvironmentGroup(EntityEmitterEntry* entityEmitter, Vector3& pos, u8 maxPathDepth)
|
||
{
|
||
// Create a small occlusion group - try it with very little leakage, so they pop up quickly, as they're a very small visual thing.
|
||
|
||
naEnvironmentGroup* environmentGroup = NULL;
|
||
environmentGroup = naEnvironmentGroup::Allocate("EntityEmitter");
|
||
if (environmentGroup)
|
||
{
|
||
// NULL entity; will be deleted when the sound is deleted
|
||
environmentGroup->Init(NULL, 20);
|
||
environmentGroup->SetSource(VECTOR3_TO_VEC3V(pos));
|
||
environmentGroup->SetInteriorInfoWithEntity(entityEmitter->entity);
|
||
if(audNorthAudioEngine::GetOcclusionManager()->GetIsPortalOcclusionEnabled())
|
||
{
|
||
environmentGroup->SetUsePortalOcclusion(true);
|
||
environmentGroup->SetMaxPathDepth(maxPathDepth);
|
||
}
|
||
}
|
||
return environmentGroup;
|
||
}
|
||
|
||
naEnvironmentGroup* audEmitterAudioEntity::CreateStaticEmitterEnvironmentGroup(const StaticEmitter* emitter, const Vector3& pos)
|
||
{
|
||
naEnvironmentGroup* environmentGroup = NULL;
|
||
|
||
if(naVerifyf(emitter, "Passed NULL StaticEmitter* when creating an environmentGroup"))
|
||
{
|
||
environmentGroup = naEnvironmentGroup::Allocate("StaticEmitter");
|
||
if (environmentGroup)
|
||
{
|
||
environmentGroup->Init(NULL, 20, 0, 4000, 0.5f, 50);
|
||
environmentGroup->SetSource(VECTOR3_TO_VEC3V(pos));
|
||
if(audNorthAudioEngine::GetOcclusionManager()->GetIsPortalOcclusionEnabled())
|
||
{
|
||
environmentGroup->SetUsePortalOcclusion(true);
|
||
environmentGroup->SetMaxPathDepth(emitter->MaxPathDepth);
|
||
environmentGroup->SetForceMaxPathDepth(AUD_GET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_STATICEMITTER_FORCEMAXPATHDEPTH) == AUD_TRISTATE_TRUE);
|
||
}
|
||
environmentGroup->SetLeaksInTaggedCutscenes(true);
|
||
environmentGroup->SetMaxLeakage(emitter->MaxLeakage);
|
||
environmentGroup->SetMinLeakageDistance(emitter->MinLeakageDistance);
|
||
environmentGroup->SetMaxLeakageDistance(emitter->MaxLeakageDistance);
|
||
environmentGroup->SetReverbOverrideSmall(emitter->SmallReverbSend);
|
||
environmentGroup->SetReverbOverrideMedium(emitter->MediumReverbSend);
|
||
environmentGroup->SetReverbOverrideLarge(emitter->LargeReverbSend);
|
||
environmentGroup->SetFillsInteriorSpace(AUD_GET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_STATICEMITTER_FILLSINTERIORSPACE) == AUD_TRISTATE_TRUE);
|
||
}
|
||
}
|
||
return environmentGroup;
|
||
}
|
||
|
||
void audEmitterAudioEntity::SetStaticEmitterInteriorInfo(const u32 i)
|
||
{
|
||
// Set the interior information on the environmentGroup. If we have an entity then use that.
|
||
if(m_StaticEmitterList[i].occlusionGroup && m_StaticEmitterList[i].emitter)
|
||
{
|
||
bool useGOInteriorInfo = true;
|
||
if(m_StaticEmitterFlags[i].bIsLinked)
|
||
{
|
||
if(m_StaticEmitterList[i].linkedObject && m_StaticEmitterList[i].linkedObject->GetIsTypeObject())
|
||
{
|
||
m_StaticEmitterList[i].occlusionGroup->ForceSourceEnvironmentUpdate(m_StaticEmitterList[i].linkedObject);
|
||
useGOInteriorInfo = false;
|
||
}
|
||
}
|
||
|
||
if(useGOInteriorInfo)
|
||
{
|
||
if(m_StaticEmitterList[i].emitter->InteriorSettings
|
||
&& naVerifyf(m_StaticEmitterList[i].emitter->InteriorRoom, "We have InteriorSettings set on a StaticEmitter, but no InteriorRoom information"))
|
||
{
|
||
naAssertf(!m_StaticEmitterFlags[i].bIsLinked, "NULL or Non-Object entity for linked StaticEmitter, falling back to GO InteriorSettings/InteriorRoom");
|
||
m_StaticEmitterList[i].occlusionGroup->SetInteriorInfoWithInteriorHashkey(m_StaticEmitterList[i].emitter->InteriorSettings, m_StaticEmitterList[i].emitter->InteriorRoom);
|
||
}
|
||
else
|
||
{
|
||
// Set up our occlusion group to search for interior info. This is presumably relatively expensive, so only do it on creation
|
||
// We can't do this synchronously, because it involves line-checks on the game-audio tread - do a global flag that we need updated,
|
||
// and then we'll trawl the list on the next frame.
|
||
naAssertf(!m_StaticEmitterFlags[i].bIsLinked, "NULL or Non-Object entity and no InteriorSettings/InteriorRoom for linked StaticEmitter, searching for room info");
|
||
g_StaticEmitterNeedsInteriorInfo = true;
|
||
m_StaticEmitterFlags[i].bNeedsInteriorInfo = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::UpdateStaticEmitterEnvironmentGroup(const u32 i)
|
||
{
|
||
naEnvironmentGroup* environmentGroup = m_StaticEmitterList[i].occlusionGroup;
|
||
if(environmentGroup)
|
||
{
|
||
if (m_StaticEmitterList[i].nameHash == ATSTRINGHASH("SE_DLC_Hei4_Island_Beach_Party_Music_New_01_Left", 0x3C8BE84A) ||
|
||
m_StaticEmitterList[i].nameHash == ATSTRINGHASH("SE_DLC_Hei4_Island_Beach_Party_Music_New_02_Right", 0xF796B9AD))
|
||
{
|
||
f32 beachPartyInfluenceRatio = 0.f;
|
||
|
||
for (u32 i = 0; i < g_AmbientAudioEntity.GetNumActiveZones(); i++)
|
||
{
|
||
const audAmbientZone* zone = g_AmbientAudioEntity.GetActiveZone(i);
|
||
|
||
if (zone->GetNameHash() == ATSTRINGHASH("AZ_DLC_Hei4_Island_Beach_Party_Music_Leakage_Bounds", 0xDD7E1146))
|
||
{
|
||
beachPartyInfluenceRatio = zone->ComputeZoneInfluenceRatioAtPoint(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition());
|
||
}
|
||
}
|
||
|
||
environmentGroup->SetForcedSameRoomAsListenerRatio(beachPartyInfluenceRatio);
|
||
}
|
||
|
||
// I guess keep checking to update interior information if it's a linked entity, as the entity streams in and out.
|
||
// But StaticEmitters don't move, so we shouldn't be updating interior information every frame, just on creation.
|
||
if(m_StaticEmitterFlags[i].bIsLinked && m_StaticEmitterList[i].linkedObject && m_StaticEmitterList[i].linkedObject->GetIsTypeObject())
|
||
{
|
||
environmentGroup->ForceSourceEnvironmentUpdate(m_StaticEmitterList[i].linkedObject);
|
||
}
|
||
|
||
#if __BANK
|
||
const StaticEmitter* emitter = m_StaticEmitterList[i].emitter;
|
||
|
||
if(g_DebugStaticEmitterEnvironmentGroups && emitter)
|
||
{
|
||
environmentGroup->SetMaxLeakage(emitter->MaxLeakage);
|
||
environmentGroup->SetMinLeakageDistance(emitter->MinLeakageDistance);
|
||
environmentGroup->SetMaxLeakageDistance(emitter->MaxLeakageDistance);
|
||
environmentGroup->SetReverbOverrideSmall(emitter->SmallReverbSend);
|
||
environmentGroup->SetReverbOverrideMedium(emitter->MediumReverbSend);
|
||
environmentGroup->SetReverbOverrideLarge(emitter->LargeReverbSend);
|
||
if(AUD_GET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_STATICEMITTER_FILLSINTERIORSPACE)==AUD_TRISTATE_TRUE)
|
||
{
|
||
environmentGroup->SetFillsInteriorSpace(true);
|
||
}
|
||
else
|
||
{
|
||
environmentGroup->SetFillsInteriorSpace(false);
|
||
}
|
||
|
||
SetStaticEmitterInteriorInfo(i);
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::StopEmitter(u32 emitterIndex)
|
||
{
|
||
#if __BANK
|
||
if (m_StaticEmitterList[emitterIndex].IsClubEmitter())
|
||
{
|
||
audDisplayf("[CLUB EMITTERS] %s is being stopped via ::StopEmitter", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(m_StaticEmitterList[emitterIndex].emitter->NameTableOffset));
|
||
}
|
||
#endif
|
||
|
||
audSound *emitterSound = m_StaticEmitterList[emitterIndex].sound;
|
||
if(emitterSound)
|
||
{
|
||
emitterSound->StopAndForget();
|
||
|
||
//We have finished with our stream slot, so free it.
|
||
audStreamSlot *streamSlot = m_StaticEmitterList[emitterIndex].streamSlot;
|
||
if(streamSlot)
|
||
{
|
||
streamSlot->Free();
|
||
m_StaticEmitterList[emitterIndex].streamSlot = NULL;
|
||
}
|
||
}
|
||
|
||
g_RadioAudioEntity.StopStaticRadio(&(m_StaticEmitterList[emitterIndex].radioEmitter));
|
||
m_StaticEmitterFlags[emitterIndex].bIsPlayingRadio = false;
|
||
}
|
||
|
||
bool audEmitterAudioEntity::IsEmitterActive(u32 emitterIndex)
|
||
{
|
||
return (m_StaticEmitterList[emitterIndex].sound) != NULL;
|
||
}
|
||
|
||
bool audEmitterAudioEntity::OnStopCallback(u32 emitterIndex)
|
||
{
|
||
//We are losing our stream slot, so stop this emitter.
|
||
g_EmitterAudioEntity.StopEmitter(emitterIndex);
|
||
return true;
|
||
}
|
||
|
||
bool audEmitterAudioEntity::HasStoppedCallback(u32 emitterIndex)
|
||
{
|
||
return !g_EmitterAudioEntity.IsEmitterActive(emitterIndex);
|
||
}
|
||
|
||
void audEmitterAudioEntity::SetEmitterRadioStation(const char *emitterName, const char *radioStationName)
|
||
{
|
||
StaticEmitter *emitter = audNorthAudioEngine::GetObject<StaticEmitter>(emitterName);
|
||
if(audVerifyf(emitter, "Failed to find static emitter %s", emitterName))
|
||
{
|
||
const u32 nameHash = atStringHash(radioStationName);
|
||
if(audVerifyf(nameHash == ATSTRINGHASH("OFF", 0x77E9145) || audRadioStation::FindStation(radioStationName), "Invalid radio station name: %s (trying to retune emitter %s)", radioStationName, emitterName))
|
||
{
|
||
audDisplayf("Retuning %s to %s", emitterName, radioStationName);
|
||
emitter->RadioStation = nameHash;
|
||
}
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::SetEmitterLinkedObject(const char *emitterName, CEntity* entity, bool isScriptLinkedEmitter)
|
||
{
|
||
StaticEmitter *emitter = audNorthAudioEngine::GetObject<StaticEmitter>(emitterName);
|
||
if(audVerifyf(emitter, "Failed to find static emitter %s", emitterName))
|
||
{
|
||
for(u32 loop = 0; loop < m_CurrentStaticEmitterIndex; loop++)
|
||
{
|
||
if(m_StaticEmitterList[loop].emitter == emitter)
|
||
{
|
||
m_StaticEmitterList[loop].linkedObject = entity;
|
||
m_StaticEmitterList[loop].isScriptLinkedEmitter = isScriptLinkedEmitter;
|
||
|
||
if(isScriptLinkedEmitter)
|
||
{
|
||
UpdateScriptPositionedEmitter(loop);
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::UpdateScriptPositionedEmitter(u32 emitterIndex)
|
||
{
|
||
if(m_StaticEmitterList[emitterIndex].linkedObject)
|
||
{
|
||
const Vector3 objectPosition = VEC3V_TO_VECTOR3(m_StaticEmitterList[emitterIndex].linkedObject->GetTransform().GetPosition());
|
||
m_StaticEmitterList[emitterIndex].position = objectPosition;
|
||
m_StaticEmitterList[emitterIndex].emitter->Position.x = objectPosition.x;
|
||
m_StaticEmitterList[emitterIndex].emitter->Position.y = objectPosition.y;
|
||
m_StaticEmitterList[emitterIndex].emitter->Position.z = objectPosition.z;
|
||
|
||
if(m_StaticEmitterList[emitterIndex].occlusionGroup)
|
||
{
|
||
m_StaticEmitterList[emitterIndex].occlusionGroup->SetSource(VECTOR3_TO_VEC3V(objectPosition));
|
||
}
|
||
}
|
||
}
|
||
|
||
bool audEmitterAudioEntity::SetEmitterEnabled(const char *emitterName, const bool enabled)
|
||
{
|
||
StaticEmitter *emitter = audNorthAudioEngine::GetObject<StaticEmitter>(emitterName);
|
||
if(audVerifyf(emitter, "Failed to find static emitter %s", emitterName))
|
||
{
|
||
if(enabled && AUD_GET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_STATICEMITTER_DISABLEDBYSCRIPT) == AUD_TRISTATE_TRUE)
|
||
{
|
||
audDisplayf("Enabling static emitter %s", emitterName);
|
||
emitter->Flags &= ~(0x03 << (FLAG_ID_STATICEMITTER_DISABLEDBYSCRIPT * 2));
|
||
AUD_SET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_STATICEMITTER_DISABLEDBYSCRIPT, AUD_TRISTATE_FALSE);
|
||
return true;
|
||
}
|
||
else if(!enabled && AUD_GET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_STATICEMITTER_DISABLEDBYSCRIPT) != AUD_TRISTATE_TRUE)
|
||
{
|
||
audDisplayf("Disabling static emitter %s", emitterName);
|
||
emitter->Flags &= ~(0x03 << (FLAG_ID_STATICEMITTER_DISABLEDBYSCRIPT * 2));
|
||
AUD_SET_TRISTATE_VALUE(emitter->Flags, FLAG_ID_STATICEMITTER_DISABLEDBYSCRIPT, AUD_TRISTATE_TRUE);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
#if __BANK
|
||
char g_EmitterStationNameBuf[64]={0};
|
||
void audEmitterAudioEntity::DrawDebug()
|
||
{
|
||
const bool playerSwitchActive = g_PlayerSwitch.IsActive();
|
||
const bool gameWorldHidden = camInterface::IsFadedOut() || CLoadingScreens::AreActive();
|
||
const u16 gameTimeMinutes = static_cast<u16>((CClock::GetHour() * 60) + CClock::GetMinute());
|
||
const bool filterNames = strlen(g_DrawStaticEmitterFilter) > 0;
|
||
|
||
if(sm_ShouldDrawStaticEmitters)
|
||
{
|
||
for(u32 i = 0; i < m_CurrentStaticEmitterIndex; i++)
|
||
{
|
||
const char *emitterName = audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(m_StaticEmitterList[i].emitter->NameTableOffset);
|
||
|
||
if(filterNames && !stristr(emitterName, g_DrawStaticEmitterFilter))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if(m_StaticEmitterFlags[i].bIsEnabled)
|
||
{
|
||
Color32 color, sphereColor;
|
||
Vector3 pos = Vector3(m_StaticEmitterList[i].emitter->Position.x,m_StaticEmitterList[i].emitter->Position.y,m_StaticEmitterList[i].emitter->Position.z);
|
||
|
||
if(ShouldStaticEmitterBePlaying(i, playerSwitchActive, gameWorldHidden, gameTimeMinutes))
|
||
{
|
||
color = sphereColor = Color32(1.0f,0.0f,0.0f,0.8f);
|
||
}
|
||
else
|
||
{
|
||
color = sphereColor = Color32(1.0f,0.3f,0.3f,0.5f);
|
||
}
|
||
|
||
grcDebugDraw::Sphere(pos, 0.5f, color, true);
|
||
grcDebugDraw::Sphere(pos, m_StaticEmitterList[i].emitter->MaxDistance, color, false);
|
||
|
||
char nameString[128];
|
||
const char *assetName = "";
|
||
if(m_StaticEmitterList[i].sound)
|
||
{
|
||
assetName = m_StaticEmitterList[i].sound->GetName();
|
||
}
|
||
else
|
||
{
|
||
audRadioStation *station = audRadioStation::FindStation(m_StaticEmitterList[i].playingRadioStation);
|
||
if(station)
|
||
{
|
||
assetName = station->GetName();
|
||
}
|
||
}
|
||
|
||
if(m_StaticEmitterFlags[i].bIsLinked && m_StaticEmitterList[i].linkedObject)
|
||
{
|
||
f32 health = -1.f;
|
||
if(m_StaticEmitterList[i].linkedObject && m_StaticEmitterList[i].linkedObject->GetType() == ENTITY_TYPE_OBJECT)
|
||
{
|
||
health = ((CObject*)m_StaticEmitterList[i].linkedObject.Get())->GetHealth();
|
||
}
|
||
|
||
grcDebugDraw::Line(RCC_VEC3V(pos), m_StaticEmitterList[i].linkedObject->GetTransform().GetPosition(), Color32(255,0,0), Color32(0,255,0));
|
||
formatf(nameString, "%s: %s%s\nlinkedObj: %p\nhealth: %f", emitterName, assetName, AUD_GET_TRISTATE_VALUE(m_StaticEmitterList[i].emitter->Flags, FLAG_ID_STATICEMITTER_DISABLEDBYSCRIPT) == AUD_TRISTATE_TRUE ? " (disabled)" : "", m_StaticEmitterList[i].linkedObject.Get(), health);
|
||
}
|
||
else
|
||
{
|
||
formatf(nameString, "%s: %s%s", emitterName, assetName, AUD_GET_TRISTATE_VALUE(m_StaticEmitterList[i].emitter->Flags, FLAG_ID_STATICEMITTER_DISABLEDBYSCRIPT) == AUD_TRISTATE_TRUE ? " (disabled)" : "");
|
||
}
|
||
|
||
grcDebugDraw::Text(pos, color, nameString);
|
||
}
|
||
}
|
||
}
|
||
|
||
char buf[256];
|
||
|
||
if(sm_ShouldDrawEntityEmitters)
|
||
{
|
||
for(u32 i = 0; i < g_audMaxEntityEmitters; i++)
|
||
{
|
||
if(m_EntityEnabledList.IsSet(i))
|
||
{
|
||
const char *name = audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(m_EntityEmitterList[i].emitter->NameTableOffset);
|
||
|
||
if(filterNames && !stristr(name, g_DrawStaticEmitterFilter))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
Color32 color;
|
||
const f32 r = 1.0f - m_EntityEmitterList[i].randomSeed;
|
||
const f32 g = m_EntityEmitterList[i].randomSeed;
|
||
const bool shouldPlay = ShouldEntityEmitterBePlaying(i, false, playerSwitchActive);
|
||
if(shouldPlay)
|
||
{
|
||
color = Color32(r,g,1.0f,1.0f);
|
||
}
|
||
else
|
||
{
|
||
color = Color32(r,g,1.0f,0.5f);
|
||
}
|
||
|
||
if(m_EntityEmitterList[i].IsWindAffected())
|
||
{
|
||
color = Color32(color.GetBlue(), color.GetGreen(), color.GetRed());
|
||
}
|
||
|
||
Vector3 pos;
|
||
GetEntityEmitterPosition(i, pos);
|
||
|
||
Vector3 v1, v2, v3;
|
||
|
||
v1.x = pos.x - 0.5f;
|
||
v1.y = pos.y;
|
||
v1.z = pos.z - 0.5f;
|
||
|
||
v2.x = pos.x + 0.5f;
|
||
v2.y = pos.y;
|
||
v2.z = pos.z - 0.5f;
|
||
|
||
v3.x = pos.x;
|
||
v3.y = pos.y;
|
||
v3.z = pos.z + 0.5f;
|
||
|
||
grcDebugDraw::Poly(RCC_VEC3V(v1),RCC_VEC3V(v2),RCC_VEC3V(v3),color);
|
||
grcDebugDraw::Poly(RCC_VEC3V(v3),RCC_VEC3V(v2),RCC_VEC3V(v1),color);
|
||
|
||
if(m_EntityEmitterList[i].IsConed())
|
||
{
|
||
Matrix34 offset;
|
||
Matrix34 mat;
|
||
|
||
offset.d = m_EntityEmitterList[i].offset;
|
||
offset.Identity3x3();
|
||
|
||
QuatV uncompressedQuatV;
|
||
m_EntityEmitterList[i].rotation.Get(uncompressedQuatV);
|
||
offset.FromQuaternion(QUATV_TO_QUATERNION(uncompressedQuatV));
|
||
|
||
mat.Set(offset);
|
||
Matrix34 entityMatrix;
|
||
m_EntityEmitterList[i].entity->GetMatrixCopy(entityMatrix);
|
||
mat.Dot(entityMatrix);
|
||
|
||
Vector3 end = mat.d + mat.b;
|
||
grcDebugDraw::Line(mat.d, end, color);
|
||
}
|
||
|
||
// compute distance to listener
|
||
Vector3 listenerToPos = pos;
|
||
listenerToPos.Subtract(MAT34V_TO_MATRIX34(g_AudioEngine.GetEnvironment().GetVolumeListenerMatrix()).d);
|
||
f32 dist = listenerToPos.Mag();
|
||
|
||
u32 timeToCheck = m_EntityCheckTimes[i] - fwTimer::GetTimeInMilliseconds();
|
||
|
||
if(dist < 50.f)
|
||
{
|
||
f32 healthFraction = 1.0f;
|
||
|
||
if(m_EntityEmitterList[i].emitter->BrokenHealth >= 0.0f)
|
||
{
|
||
if(m_EntityEmitterList[i].entity && m_EntityEmitterList[i].entity->GetIsTypeObject())
|
||
{
|
||
CObject* object = (CObject*)m_EntityEmitterList[i].entity;
|
||
healthFraction = object->GetHealth()/object->GetMaxHealth();
|
||
}
|
||
}
|
||
|
||
if(m_EntityEmitterList[i].IsConed())
|
||
{
|
||
formatf(buf, sizeof(buf), "ca: %.2f d: %f ttc: %u [%u] rs: %f health: %f", ComputeEntityEmitterConeAttenuation(i), dist, timeToCheck, m_EntityCheckTimes[i], m_EntityEmitterList[i].randomSeed, healthFraction);
|
||
}
|
||
else
|
||
{
|
||
formatf(buf, sizeof(buf), "d: %.2f ttc: %u [%u] rs: %f health: %f", dist, timeToCheck, m_EntityCheckTimes[i], m_EntityEmitterList[i].randomSeed, healthFraction);
|
||
}
|
||
grcDebugDraw::Text(pos, color, buf);
|
||
}
|
||
if(dist < 400.f)
|
||
{
|
||
grcDebugDraw::Text(pos + Vector3(0.f,0.f,1.f), color, const_cast<char*>(name));
|
||
}
|
||
}
|
||
}
|
||
|
||
for(u32 i = 0; i < m_EntityEmitterDebugInfo.GetCount(); i++)
|
||
{
|
||
bool emitterEnabled = false;
|
||
|
||
for(u32 j = 0; j < g_audMaxEntityEmitters; j++)
|
||
{
|
||
if(m_EntityEnabledList.IsSet(j))
|
||
{
|
||
if(m_EntityEmitterDebugInfo[i].entity == m_EntityEmitterList[i].entity)
|
||
{
|
||
emitterEnabled = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Draw extra entries for emitters that exist but aren't explicitly linked to a static emitter
|
||
if(!emitterEnabled)
|
||
{
|
||
const char* name = audNorthAudioEngine::GetMetadataManager().GetObjectName(m_EntityEmitterDebugInfo[i].hash);
|
||
|
||
if(name)
|
||
{
|
||
if(filterNames && !stristr(name, g_DrawStaticEmitterFilter))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if(CEntity* pEntity = m_EntityEmitterDebugInfo[i].entity)
|
||
{
|
||
Color32 color = Color_red;
|
||
Vector3 pos;
|
||
pEntity->TransformIntoWorldSpace(pos, m_EntityEmitterDebugInfo[i].offset);
|
||
|
||
Vector3 v1, v2, v3;
|
||
|
||
v1.x = pos.x - 0.5f;
|
||
v1.y = pos.y;
|
||
v1.z = pos.z - 0.5f;
|
||
|
||
v2.x = pos.x + 0.5f;
|
||
v2.y = pos.y;
|
||
v2.z = pos.z - 0.5f;
|
||
|
||
v3.x = pos.x;
|
||
v3.y = pos.y;
|
||
v3.z = pos.z + 0.5f;
|
||
|
||
grcDebugDraw::Poly(RCC_VEC3V(v1),RCC_VEC3V(v2),RCC_VEC3V(v3),color);
|
||
grcDebugDraw::Poly(RCC_VEC3V(v3),RCC_VEC3V(v2),RCC_VEC3V(v1),color);
|
||
|
||
// compute distance to listener
|
||
Vector3 listenerToPos = pos;
|
||
listenerToPos.Subtract(MAT34V_TO_MATRIX34(g_AudioEngine.GetEnvironment().GetVolumeListenerMatrix()).d);
|
||
f32 dist = listenerToPos.Mag();
|
||
|
||
if(dist < 400.f)
|
||
{
|
||
for(u32 j = 0; j < m_CurrentStaticEmitterIndex; j++)
|
||
{
|
||
if(m_StaticEmitterFlags[j].bIsLinked && m_StaticEmitterList[j].linkedObject && m_StaticEmitterList[j].linkedObject == m_EntityEmitterDebugInfo[i].entity)
|
||
{
|
||
const f32 r = 1.0f - m_EntityEmitterList[i].randomSeed;
|
||
const f32 g = m_EntityEmitterList[i].randomSeed;
|
||
formatf(buf, sizeof(buf), "%s (%s)", const_cast<char*>(name), m_StaticEmitterList[j].linkedObject->GetModelName());
|
||
grcDebugDraw::Text(pos, Color32(r,g,1.0f,1.0f), buf);
|
||
emitterEnabled = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if(!emitterEnabled)
|
||
{
|
||
formatf(buf, sizeof(buf), "%s (UNLINKED)", const_cast<char*>(name));
|
||
grcDebugDraw::Text(pos, color, buf);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void RetuneEmitter()
|
||
{
|
||
g_EmitterAudioEntity.SetEmitterRadioStation(g_DrawStaticEmitterFilter, g_EmitterStationNameBuf);
|
||
}
|
||
|
||
bool g_DebugEmitterEnabled = false;
|
||
void SetEnabledEmitter()
|
||
{
|
||
g_EmitterAudioEntity.SetEmitterEnabled(g_DrawStaticEmitterFilter, g_DebugEmitterEnabled);
|
||
}
|
||
|
||
extern bool g_WarpToStaticEmitter;
|
||
void audEmitterAudioEntity::AddWidgets(bkBank &bank)
|
||
{
|
||
bank.PushGroup("audEmitterAudioEntity",false);
|
||
bank.AddToggle("WarpToStaticEmitter", &g_WarpToStaticEmitter);
|
||
bank.AddToggle("Draw Entity Emitters", &sm_ShouldDrawEntityEmitters);
|
||
bank.AddToggle("Draw Static Emitters", &sm_ShouldDrawStaticEmitters);
|
||
bank.AddToggle("Draw Static Emitter Openness Values", &sm_DrawStaticEmitterOpennessValues);
|
||
bank.AddText("Filter by Name", g_DrawStaticEmitterFilter, sizeof(g_DrawStaticEmitterFilter), false);
|
||
|
||
bank.AddSlider("Space Filling Interior Ducking Volume (linear)", &g_SpaceFillingInteriorDuckingVolumeLin, 0.0f, 1.0, 0.1f);
|
||
bank.AddSlider("Interior Ducking Smoother Rate", &g_EmitterInteriorDuckingSmootherRate, 0.0001f, 10.f, 0.1f);
|
||
|
||
bank.AddText("Radio Station Name", g_EmitterStationNameBuf, sizeof(g_EmitterStationNameBuf),false);
|
||
bank.AddButton("Retune emitter", CFA(RetuneEmitter));
|
||
bank.AddToggle("Enabled?", &g_DebugEmitterEnabled);
|
||
bank.AddButton("SetEnabled", CFA(SetEnabledEmitter));
|
||
|
||
bank.AddText("Emitter Name", g_CreateEmitterName, sizeof(g_CreateEmitterName));
|
||
bank.AddButton("Create New Static Emitter At Camera Coords", CFA(audAmbientAudioEntity::CreateEmitterAtCurrentCoords));
|
||
bank.AddButton("Move Existing Static Emitter To Camera Coords", CFA(audAmbientAudioEntity::MoveEmitterToCurrentCoords));
|
||
bank.AddText("Emitter To Duplicate", g_DuplicateEmitterName, sizeof(g_DuplicateEmitterName));
|
||
bank.AddButton("Create Duplicate At Current Coords", CFA(audAmbientAudioEntity::DuplicateEmitterAtCurrentCoords));
|
||
|
||
bank.AddToggle("UpdateStaticEmitterEnvGroupsEveryFrame", &g_DebugStaticEmitterEnvironmentGroups);
|
||
bank.PopGroup();
|
||
}
|
||
|
||
void audEmitterAudioEntity::HandleRaveStaticEmitterCreatedNotification(u32 nameHash)
|
||
{
|
||
CreateOrUpdateEmitter(nameHash);
|
||
}
|
||
|
||
void audEmitterAudioEntity::RetuneEmittersToStation(audRadioStation* station)
|
||
{
|
||
if (station)
|
||
{
|
||
for (u32 loop = 0; loop < m_CurrentStaticEmitterIndex; loop++)
|
||
{
|
||
if (m_StaticEmitterList[loop].playingRadioStation != 0u)
|
||
{
|
||
g_RadioAudioEntity.RequestStaticRadio(&(m_StaticEmitterList[loop].radioEmitter), station->GetNameHash(), m_StaticEmitterList[loop].GetRequestedPCMChannel());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void audEmitterAudioEntity::CreateOrUpdateEmitter(u32 nameHash)
|
||
{
|
||
StaticEmitter* emitter = audNorthAudioEngine::GetObject<StaticEmitter>(nameHash);
|
||
|
||
if (emitter)
|
||
{
|
||
for (u32 loop = 0; loop < m_CurrentStaticEmitterIndex; loop++)
|
||
{
|
||
if (m_StaticEmitterList[loop].emitter == emitter)
|
||
{
|
||
UpdateStaticEmitterPosition(emitter);
|
||
return;
|
||
}
|
||
}
|
||
|
||
const u16 gameTimeMinutes = static_cast<u16>((CClock::GetHour() * 60) + CClock::GetMinute());
|
||
AddStaticEmitter(nameHash, gameTimeMinutes);
|
||
}
|
||
}
|
||
#endif
|