Files
gta5_build_patches/game/audio/environment/environment.cpp
T
Akkariin a4f85d47ac Init
2024-01-01 01:47:09 +08:00

6147 lines
235 KiB
C++

//
// audio/gtaaudioenvironment.cpp
//
// Copyright (C) 1999-2006 Rockstar Games. All Rights Reserved.
//
#include "audio/ambience/ambientaudioentity.h"
#include "audio/cutsceneaudioentity.h"
#include "audio/debugaudio.h"
#include "audio/environment/environment.h"
#include "audio/environment/environmentgroup.h"
#include "audio/frontendaudioentity.h"
#include "audio/gameobjects.h"
#include "audio/northaudioengine.h"
#include "audio/radiostation.h"
#include "audio/scannermanager.h"
#include "audio/scriptaudioentity.h"
#include "audio/weatheraudioentity.h"
#include "audioeffecttypes/biquadfiltereffect.h"
#include "audioeffecttypes/delayeffect.h"
#include "audioeffecttypes/reverbeffect.h"
#include "audiosoundtypes/sounddefs.h"
#include "audioengine/engine.h"
#include "audioengine/engineutil.h"
#include "audioengine/environment.h"
#include "audiohardware/driverutil.h"
#include "audiohardware/submix.h"
#include "audwarpmanager.h"
#include "camera/CamInterface.h"
#include "camera/scripted/ScriptDirector.h"
#include "camera/viewports/ViewportManager.h"
#include "cutscene/CutSceneManagerNew.h"
#include "debug/debugglobals.h"
#include "debug/DebugScene.h"
#include "grcore/debugdraw.h"
#include "game/weather.h"
#include "game/zones.h"
#include "math/amath.h"
#include "modelinfo/MloModelInfo.h"
#include "objects/door.h"
#include "peds/PedIntelligence.h"
#include "peds/ped.h"
#include "peds/PopCycle.h"
#include "physics/WorldProbe/worldprobe.h"
#include "scene/portals/portal.h"
#include "scene/portals/portalInst.h"
#include "scene/WarpManager.h"
#include "scene/world/GameWorldWaterHeight.h"
#include "fwscene/search/Search.h"
#include "fwscene/search/SearchVolumes.h"
#include "fwscene/world/WorldRepMulti.h"
#include "fwsys/timer.h"
#include "task/Vehicle/TaskEnterVehicle.h"
#include "task/Vehicle/TaskExitVehicle.h"
#include "vector/geometry.h"
#include "vfx/Systems/VfxWeather.h"
#include "fwscene/world/WorldLimits.h"
#include "scene/world/GameWorldHeightMap.h"
#include "vectormath/vectormath.h"
#include "vehicleAi/pathfind.h"
AUDIO_ENVIRONMENT_OPTIMISATIONS()
// game-thread, gta-specific
f32 g_NumTreesForRain = 25.f;
f32 g_NumTreesForRainBuiltUp = 200.f;
f32 g_OverrideBuiltUpFactor = 0.5f;
f32 g_DeafeningEarLeak = 0.2f;
f32 g_DeafeningRolloff = 1.5f;
f32 g_MassiveDeafeningVolume = 16.0f;
f32 g_MinHeightForTree = 3.0f;
f32 g_MinBuildingHeight = 5.0f;
f32 g_MinBuildingFootprintArea = 1000.0f;
f32 g_MaxBuildingFootprintArea = 10000.0f;
f32 g_SourceEnvironmentRadius = 20.0f;
f32 g_OpenSpaceReverbSize = 0.3f;
f32 g_OpenSpaceReverbWet = 0.08f;
f32 g_SpeakerReverbSmootherRate = 0.001f;
f32 g_SpeakerReverbDamping = 0.525f;
f32 g_FitnessReduced = 0.01f;
f32 g_VolSmootherRate = 0.001f;
u32 g_DeafeningRampUpTime = 1500;
u32 g_DeafeningHoldTime = 1500;
u32 g_DeafeningRampDownTime = 4500;
u32 g_SpecialAbilitySlowMoTimeout = 250;
bool g_DebugListenerReverb = false;
bool g_ShouldOverrideBuiltUpFactor = false;
bool g_UseInteriorCarFilter = false;
bool g_EnableLocalEnvironmentMetrics = true;
u16 naEnvironment::sm_ObjOcclusionUpdateTime = 500;
bool naEnvironment::sm_ProcessSectors = false;
audCurve naEnvironment::sm_HDotToSlowmoResoVol;
audCurve naEnvironment::sm_VDotToSlowmoResoVol;
audCurve naEnvironment::sm_SpecialAbToFrequency;
audCurve naEnvironment::sm_CityGustDotToVolSpikeForBgTrees;
audCurve naEnvironment::sm_CountrySideGustDotToVolSpikeForBgTrees;
f32 naEnvironment::sm_SlowMoLFOFrequencyMin = 0.5f;
f32 naEnvironment::sm_SlowMoLFOFrequencyMax = 2.f;
f32 naEnvironment::sm_RadiusScale = 2.5f;
f32 naEnvironment::sm_RainOnPropsVolumeOffset = 0.f;
u32 naEnvironment::sm_RainOnPopsPreDealy = 0;
u32 naEnvironment::sm_MaxNumRainProps = 5;
u32 naEnvironment::sm_MinWindObjTime = 15000;
u32 naEnvironment::sm_MaxWindObjtTime = 25000;
u32 naEnvironment::sm_WindTimeVariance = 10000;
u32 naEnvironment::sm_MinTimeToRetrigger = 25000;
u32 naEnvironment::sm_MinNumberOfTreesToTriggerGustEnd = 5;
#if __BANK
bool g_DrawWeaponEchoes = false;
bool g_audDrawLocalObjects = false;
bool g_audDrawShockwaves = false;
bool g_ForceResonanceTracksListener = false;
bool g_ForceRainLoopTracksListener = false;
bool g_ForceRainLoopPlayFromTopOfBounds = false;
f32 naEnvironment::sm_NumWaterProbeSets = 10.f;
bool naEnvironment::sm_UseCoverAproximation = false;
bool naEnvironment::sm_DebugCoveredObjects = false;
bool naEnvironment::sm_DebugGustRustle = false;
bool naEnvironment::sm_DebugGustWhistle = false;
bool naEnvironment::sm_DrawAudioWorldSectors = false;
bool naEnvironment::sm_IsWaitingToInit = true;
bool naEnvironment::sm_LogicStarted = false;
bool naEnvironment::sm_DisplayResoIntensity = false;
bool naEnvironment::sm_DrawBgTreesInfo = false;
bool naEnvironment::sm_MuteBgFL = false;
bool naEnvironment::sm_MuteBgFR = false;
bool naEnvironment::sm_MuteBgRL = false;
bool naEnvironment::sm_MuteBgRR = false;
u32 naEnvironment::sm_CurrentSectorX = 0;
u32 naEnvironment::sm_CurrentSectorY = 0;
#endif
bank_bool g_UpdateBuiltUpFactor = false;
bool naEnvironment::ms_localObjectSearchLaunched = false;
fwSearch naEnvironment::ms_localObjectSearch(1900);
f32 naEnvironment::sm_HeliShockwaveRadius = 30.f;
f32 naEnvironment::sm_TrainDistanceThreshold = 300.f;
f32 naEnvironment::sm_TrainShockwaveRadius = 30.f;
f32 naEnvironment::sm_MeleeResoImpulse = 0.3f;
f32 naEnvironment::sm_PistolResoImpulse = 0.2f;
f32 naEnvironment::sm_RifleResoImpulse = 0.35f;
f32 naEnvironment::sm_WeaponsResonanceDistance = 15.f;
f32 naEnvironment::sm_ExplosionsResonanceImpulse = 0.2f;
f32 naEnvironment::sm_ExplosionsResonanceDistance = 100.f;
f32 naEnvironment::sm_VehicleCollisionResonanceImpulse = 0.05f;
f32 naEnvironment::sm_VehicleCollisionResonanceDistance = 5.f;
f32 naEnvironment::sm_DistancePlateau = 3.f;
// Echo metrics
f32 g_MaxEchoLength = 40.0f;
f32 g_MinEchoLength = 15.0f;
// BuiltUp stuff
f32 g_BuiltUpSmootherRate = 1.0f;
bool g_DebugBuiltUpInfo = false;
bool g_DisplayBuiltUpFactors = false;
bool g_UseProperBuiltUpFactor = true;
f32 g_NSEW_Offset = 0.49f;
f32 g_BuiltUpDampingFactorSmoothUpRate = 1.0f;
f32 g_BuiltUpDampingFactorSmoothDownRate = 1.0f;
f32 g_BuiltUpDampingFactorOverride = 1.0f;
f32 g_InCarRatioSmoothRate = 1.0f;
f32 g_InteriorRatioSmoothRate = 1.0f;
f32 g_windWhistleTreshold = 3.0f;
f32 naEnvironment::sm_SqdDistanceToPlayRainSounds = 225.f;
f32 naEnvironment::sm_BushesMinRadius = 1.5f;
f32 naEnvironment::sm_SmallTreesMinRadius = 4.5f;
f32 naEnvironment::sm_BigTreesMinRadius = 10.f;
f32 naEnvironment::sm_TreesSoundDist = 30.f;
f32 naEnvironment::sm_BigTreesFitness = 1.f;
f32 naEnvironment::sm_TreesFitness = 0.6f;
f32 naEnvironment::sm_BushesFitness = 0.3f;
u8 naEnvironment::sm_NumTreesForAvgDist = 5;
#if 1 //!USE_AUDMESH_SECTORS
atRangeArray<audSector, g_audNumWorldSectors> g_AudioWorldSectors;
#endif
bool g_audDrawWorldSectorDebug = false;
bool g_audResetWorldSectorRoadInfo = false;
Vector3 g_Directions[4];
// Interior stuff
bool g_UseDebugRoomSettings = false;
bool g_TryRoomsEveryTime = false;
InteriorSettings* g_DebugRoomSettings;
f32 g_DebugRoomSettingsReverbSmall = 0.0f;
f32 g_DebugRoomSettingsReverbMedium = 0.0f;
f32 g_DebugRoomSettingsReverbLarge = 0.0f;
bool g_UsePlayer = true;
audCurve g_ShockwaveDistToVol;
char g_InteriorInfoDebug[64] = "";
char g_RoomInfoDebug[64] = "";
extern CPathFind ThePaths;
extern audScannerManager g_AudioScannerManager;
// only used on audio thread
PARAM(headphones, "[Audio] Mix for headphones.");
// only used on game thread
PARAM(echoes, "[Audio] Enables local environment scanning and turns echoes on.");
enum
{
AUD_DEAFENING_OFF,
AUD_DEAFENING_RAMP_UP,
AUD_DEAFENING_HOLD,
AUD_DEAFENING_RAMP_DOWN
};
naEnvironment::naEnvironment()
{
}
naEnvironment::~naEnvironment()
{
}
void naEnvironment::Init()
{
g_Directions[0] = Vector3(0.0f, 1.0f, 0.0f); //N
g_Directions[1] = Vector3(1.0f, 0.0f, 0.0f); //E
g_Directions[2] = Vector3(0.0f, -1.0f, 0.0f); //S
g_Directions[3] = Vector3(-1.0f, 0.0f, 0.0f); //W
if(PARAM_echoes.Get())
{
g_EnableLocalEnvironmentMetrics = true;
}
m_BuiltUpToRolloff.Init(ATSTRINGHASH("BUILT_UP_TO_ROLLOFF", 0x7F6DB253));
m_DeafeningState[0] = m_DeafeningState[1] = AUD_DEAFENING_OFF;
m_DeafeningPhaseEndTime[0] = m_DeafeningPhaseEndTime[1] = 0;
m_DeafeningMaxStrength[0] = m_DeafeningMaxStrength[1] = 0.0f;
m_DeafeningMinStrength[0] = m_DeafeningMinStrength[1] = 0.0f;
m_DeafeningAttenuationCurve.Init(ATSTRINGHASH("DEAFENING_STRENGTH_TO_ATTENUATION", 0x65D9D83E));
m_DeafeningFilterCurve.Init(ATSTRINGHASH("DEAFENING_STRENGTH_TO_FILTER", 0xA8DE551F));
m_DeafeningReverbCurve.Init(ATSTRINGHASH("DEAFENING_STRENGTH_TO_REVERB", 0x5D6B04BC));
m_DeafeningVolumeToStrengthCurve.Init(ATSTRINGHASH("DEAFENING_VOLUME_TO_STRENGTH_CURVE", 0xE4309D3));
m_DeafeningDistanceRolloffCurve.Init(ATSTRINGHASH("DEFAULT_ROLLOFF", 0x3BD35057));
g_ShockwaveDistToVol.Init(ATSTRINGHASH("SHOCKWAVE_DIST_TO_VOL", 0x26002533));
m_IsListenerInHead = false;
m_ForceAmbientMetricsUpdate = false;
g_DebugRoomSettings = audNorthAudioEngine::GetObject<InteriorSettings>(ATSTRINGHASH("DEBUG_INTERIOR", 0x044a9a7e2));
ClearLinkedRooms();
m_NumRainPropSounds = 0;
m_EarStrength[0] = m_EarStrength[1] = 0.0f;
for (s32 i=0; i<g_audNumLevelProbeRadials; i++)
{
m_LevelReflectionNormal[i].Zero();
}
for (s32 i=0; i<g_audNumMidProbeRadials; i++)
{
m_UpperReflectionNormal[i].Zero();
m_EchoDistance[i] = 0.0f;
m_EchoSuitability[i] = 0.0f;
m_EchoPosition[i].Zero();
m_EchoDirection[i].Set(1.0f);
}
m_ListenerInteriorInstance = NULL;
m_InteriorRoomId = 0;
m_ListenerInteriorSettings = NULL;
m_ListenerInteriorRoom = NULL;
m_DistanceToEchoCurve.Init(ATSTRINGHASH("DISTANCE_TO_ECHO_GOODNESS", 0x4883D0D7));
m_SilentWhenEchoCoLocatedCurve.Init(ATSTRINGHASH("ECHO_COLOCATED_ATTENUATION", 0xDB040C16));
m_EchoGoodnessLinearVolumeCurve.Init(ATSTRINGHASH("ECHO_GOODNESS_TO_LINEAR_VOLUME", 0x4D2F84CA));
m_EchoGoodnessDbVolumeCurve.Init(ATSTRINGHASH("ECHO_GOODNESS_TO_DB_VOLUME", 0x59B4D084));
m_DistanceToDelayCurve.Init(ATSTRINGHASH("ECHO_DISTANCE_TO_PREDELAY", 0xE42B577A));
m_AngleOfIncidenceToSuitabilityCurve.Init(ATSTRINGHASH("ECHO_ANGLE_TO_SUITABILITY", 0x9972AF32));
m_OutdoorInteriorWeaponVolCurve.Init(ATSTRINGHASH("REV_WET_TO_OUTDOOR_INTERIOR_WEAPON_VOLUME", 0xBD8ED687));
m_OutdoorInteriorWeaponHoldCurve.Init(ATSTRINGHASH("REV_SIZE_TO_OUTDOOR_INTERIOR_WEAPON_HOLD", 0x6D07F377));
BANK_ONLY(m_CoveredPolys.Reset());
m_NumValidPolys = 0;
for (u32 i=0; i<MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES; i++)
{
m_SourceEnvironmentMetrics[i] = 0;
m_SourceEnvironmentPolyAreas[i] = 0.0f;
m_SourceEnvironmentPolyCentroids[i].Zero();
for (u32 j=0; j<g_NumSourceReverbs; j++)
{
m_SourceEnvironmentPolyReverbWet[i][j] = 0.0f;
}
}
m_ListenerPositionLastSourceEnvironmentUpdate.Zero();
m_SourceEnvironmentTimeLastUpdated = 0;
m_SourceEnvironmentPathHandle = PATH_HANDLE_NULL;
m_SourceEnvironmentToReverbWetCurve.Init(ATSTRINGHASH("SOURCE_ENVIRONMENT_TO_REVERB_WET", 0x069607de3));
m_SourceEnvironmentToReverbSizeCurve.Init(ATSTRINGHASH("SOURCE_ENVIRONMENT_TO_REVERB_SIZE", 0x0fd1b7244));
m_SourceEnvironmentSizeToWetScaling.Init(ATSTRINGHASH("SOURCE_ENVIRONMENT_SIZE_TO_WET_SCALING", 0x033ef2c0b));
m_DistanceToListenerReverbCurve.Init(ATSTRINGHASH("DISTANCE_TO_LISTENER_REVERB", 0x49BAD9E5));
m_AreaToListenerReverbCurve.Init(ATSTRINGHASH("AREA_TO_LISTENER_REVERB", 0x2AD353B));
for (u32 i=0; i<4; i++)
{
for (u32 j=0; j<3; j++)
{
m_SpeakerReverbWetSmoothers[i][j].Init(g_SpeakerReverbSmootherRate, true);
}
}
//Prepare crossfade curves for source reverb submixes.
for(u32 i=0; i<g_NumSourceReverbs; i++)
{
m_SourceReverbCrossfadeCurves[i].Init(g_SourceReverbCurveNames[i]);
}
m_NumBuildingAddsThisFrame = m_NumBuildingRemovesThisFrame = m_NumTreeAddsThisFrame = m_NumTreeRemovesThisFrame = 0;
m_BuiltUpFactor = 0.0f;
m_BuildingDensityFactor = 0.0f;
m_TreeDensityFactor = 0.0f;
m_WaterFactor = 0.0f;
m_HighwayFactor = 0.0f;
m_AveragePedZPos = 0.0f;
m_PedFractionMale = 0.0f;
m_WaterHeightAtListener = 0.f;
m_HeightToBuiltUpFactor.Init(ATSTRINGHASH("HEIGHT_TO_BUILT_UP_FACTOR", 0x1ADE3D7F));
m_BuildingNumToDensityFactor.Init(ATSTRINGHASH("BUILDING_COUNT_TO_DENSITY_FACTOR", 0x6E08E885));
m_TreeNumToDensityFactor.Init(ATSTRINGHASH("TREE_COUNT_TO_DENSITY_FACTOR", 0x7DD816BE));
m_WaterAmountToWaterFactor.Init(ATSTRINGHASH("WATER_AMOUNT_TO_WATER_FACTOR", 0xCD234F5D));
m_HighwayAmountToHighwayFactor.Init(ATSTRINGHASH("HIGHWAY_AMOUNT_TO_HIGHWAY_FACTOR", 0x7446E468));
m_VehicleAmountToVehicleDensity.Init(ATSTRINGHASH("VEHICLE_COUNT_TO_DENSITY_FACTOR", 0x4F5ECEE5));
m_VehicleDistanceSqToVehicleAmount.Init(ATSTRINGHASH("VEHICLE_DISTANCE_SQ_TO_VEHICLE_AMOUNT", 0x81F1EBC2));
m_VehicleSpeedToVehicleAmount.Init(ATSTRINGHASH("VEHICLE_SPEED_TO_VEHICLE_AMOUNT", 0xB181AEFB));
m_BuiltUpSmoother.Init(g_BuiltUpSmootherRate, true);
m_BuildingDensitySmoother.Init(g_BuiltUpSmootherRate, true);
m_TreeDensitySmoother.Init(g_BuiltUpSmootherRate, true);
m_WaterSmoother.Init(g_BuiltUpSmootherRate, true);
m_HighwaySmoother.Init(g_BuiltUpSmootherRate, true);
m_BuiltUpDampingFactorSmoother.Init(g_BuiltUpDampingFactorSmoothUpRate, g_BuiltUpDampingFactorSmoothDownRate, true);
for (u32 i=0; i<AUD_NUM_SECTORS; i++)
{
m_BuildingHeights[i] = 0.0f;
m_BuildingCount[i] = 0.0f;
m_TreeCount[i] = 0.0f;
m_BuiltUpFactorPerZone[i] = 0.0f;
}
for (u32 i=0; i<4; i++)
{
m_BuiltUpDirectionalSmoother[i].Init(g_BuiltUpSmootherRate, true);
m_BuildingDensityDirectionalSmoother[i].Init(g_BuiltUpSmootherRate, true);
m_TreeDensityDirectionalSmoother[i].Init(g_BuiltUpSmootherRate, true);
m_WaterDirectionalSmoother[i].Init(g_BuiltUpSmootherRate, true);
m_HighwayDirectionalSmoother[i].Init(g_BuiltUpSmootherRate, true);
m_DirectionalBuiltUpFactor[i] = 0.0f;
m_DirectionalBuildingDensity[i] = 0.0f;
m_DirectionalTreeDensity[i] = 0.0f;
m_DirectionalWaterFactor[i] = 0.0f;
m_DirectionalHighwayFactor[i] = 0.0f;
}
for (u32 i=0; i<4; i++)
{
m_SpeakerBuiltUpSmoother[i].Init(g_BuiltUpSmootherRate, true);
m_SpeakerBuiltUpFactors[i] = 0.0f;
m_SpeakerBlockedFactor[i] = 0.0f;
m_SpeakerBlockedLinearVolume[i] = 0.0f;
m_SpeakerOWOFactor[i] = 0.0f;
}
m_CarInteriorFilterSmoother.Init(40.f,40.f,2000.f,24000.f);
// On the first update it'll jump to the right spot and then start smoothing after that
m_CarInteriorFilterCutoff = 24000.f;
m_PlayerInCarRatio = 0.0f;
m_IsPlayerInVehicle = false;
m_IsPlayerVehicleATrain = false;
m_PlayerInCarSmoother.Init(g_InCarRatioSmoothRate/1000.0f, true);
m_PlayerIsInASubwayStationOrSubwayTunnel = false;
m_PlayerIsInASubwayOrTunnel = false;
m_PlayerWasInASubwayOrTunnelLastFrame = false;
m_PlayerIsInAnInterior = false;
m_PlayerInteriorRatioSmoother.Init(g_InteriorRatioSmoothRate/1000.0f, true);
m_PlayerInteriorRatio = 0.0f;
m_RoomToneHash = g_NullSoundHash;
m_RainType = RAIN_TYPE_NONE;
m_NavmeshInformationUpdatedThisFrame = false;
m_LastTimeWeWereAPassengerInATaxi = 0;
for(u32 i = 0; i < g_MaxInterestingLocalObjects; i++)
{
m_InterestingLocalObjects[i].object = NULL;
m_InterestingLocalObjects[i].envGroup = NULL;
m_InterestingLocalObjects[i].rainSound = NULL;
m_InterestingLocalObjects[i].windSound = NULL;
m_InterestingLocalObjects[i].randomAmbientSound = NULL;
m_InterestingLocalObjects[i].macsComponent = -1;
m_InterestingLocalObjects[i].stillValid = false;
m_InterestingLocalObjects[i].resoSoundInfo.resonanceSound = NULL;
m_InterestingLocalObjects[i].resoSoundInfo.resonanceIntensity = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.attackTime = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.releaseTime = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.resoSoundRef = g_NullSoundRef;
m_InterestingLocalObjects[i].resoSoundInfo.slowmoResoVolLin = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.slowmoResoPhase = 0.f;
m_InterestingLocalObjects[i].shockwaveSoundInfo.attackTime = -1.f;
m_InterestingLocalObjects[i].shockwaveSoundInfo.releaseTime = -1.f;
m_InterestingLocalObjects[i].shockwaveSoundInfo.prevVol = 0.f;
BANK_ONLY(m_InterestingLocalObjects[i].underCover = true);
m_InterestingLocalObjectsCached[i].object = NULL;
m_InterestingLocalObjectsCached[i].envGroup = NULL;
m_InterestingLocalObjectsCached[i].rainSound = NULL;
m_InterestingLocalObjectsCached[i].windSound = NULL;
m_InterestingLocalObjectsCached[i].randomAmbientSound = NULL;
m_InterestingLocalObjectsCached[i].macsComponent = -1;
m_InterestingLocalObjectsCached[i].stillValid = false;
m_InterestingLocalObjectsCached[i].resoSoundInfo.resonanceSound = NULL;
m_InterestingLocalObjectsCached[i].resoSoundInfo.resonanceIntensity = 0.f;
m_InterestingLocalObjectsCached[i].resoSoundInfo.attackTime = 0.f;
m_InterestingLocalObjectsCached[i].resoSoundInfo.releaseTime = 0.f;
m_InterestingLocalObjectsCached[i].resoSoundInfo.resoSoundRef = g_NullSoundRef;
m_InterestingLocalObjectsCached[i].resoSoundInfo.slowmoResoVolLin = 0.f;
m_InterestingLocalObjectsCached[i].resoSoundInfo.slowmoResoPhase = 0.f;
m_InterestingLocalObjectsCached[i].shockwaveSoundInfo.attackTime = -1.f;
m_InterestingLocalObjectsCached[i].shockwaveSoundInfo.releaseTime = -1.f;
m_InterestingLocalObjectsCached[i].shockwaveSoundInfo.prevVol = 0.f;
BANK_ONLY(m_InterestingLocalObjectsCached[i].underCover = true);
m_NextInterestingObjectRandTime[i] = audEngineUtil::GetRandomNumberInRange(5000, 30000);
}
for(u32 i = 0; i < g_MaxWindSoundsHistory; i++)
{
m_WindTriggeredSoundsHistory[i].soundRef = g_NullSoundRef;
m_WindTriggeredSoundsHistory[i].time = 0;
}
m_NextWindObjectTime = 0;
for (u8 i = 0; i < g_MaxInterestingLocalTrees; i++ )
{
m_LocalTrees[i].tree = NULL;
m_LocalTrees[i].sector = AUD_SECTOR_FL;
m_LocalTrees[i].type = AUD_BUSHES;
m_LocalTrees[i].windSound = NULL;
}
for (u8 i = 0; i < MAX_FB_SECTORS; i++)
{
m_BackGroundTreesSounds[0][i].sound = NULL;
m_BackGroundTreesSounds[1][i].sound = NULL;
m_BackGroundTreesSounds[0][i].fitness = 0.f;
m_BackGroundTreesSounds[1][i].fitness = 0.f;
BANK_ONLY(m_BackGroundTreesSounds[0][i].linVol = 0.f;)
BANK_ONLY(m_BackGroundTreesSounds[1][i].linVol = 0.f;)
m_BackGroundTreesSounds[0][i].avgDistance = 0.f;
m_BackGroundTreesSounds[1][i].avgDistance = 0.f;
m_BackGroundTreesSounds[0][i].volSmoother.Init(0.01f);
m_BackGroundTreesSounds[1][i].volSmoother.Init(0.01f);
m_BackGroundTrees[i].bigTreesFitness = 0.f;
m_BackGroundTrees[i].treesFitness = 0.f;
m_BackGroundTrees[i].bushesFitness = 0.f;
m_BackGroundTrees[i].numBigTrees = 0;
m_BackGroundTrees[i].numTrees = 0;
m_BackGroundTrees[i].numBushes = 0;
m_CurrentSoundIdx[i] = 0;
}
//for (u8 i = 0; i < MAX_AUD_TREE_TYPES; i++)
//{
m_BackGroundTreesFitness[AUD_BIG_TREES] = 1.f;
m_BackGroundTreesFitness[AUD_SMALL_TREES] = 0.6f;
m_BackGroundTreesFitness[AUD_BUSHES] = 0.3f;
//}
m_WindBushesSoundset.Init(ATSTRINGHASH("WIND_BUSH_SOUNDSET", 0x4AF50222));
m_WindTreesSoundset.Init(ATSTRINGHASH("WIND_TREE_SMALL_SOUNDSET", 0xDA99FD95));
m_WindBigTreesSoundset.Init(ATSTRINGHASH("WIND_TREE_LARGE_SOUNDSET", 0x377B6059));
// BIG TREES
m_NumTreesToVolume[AUD_BIG_TREES].Init(ATSTRINGHASH("NUM_BIG_TREES_TO_BG_VOLUME", 0xA19220F8));
m_TreesDistanceToVolume[AUD_BIG_TREES].Init(ATSTRINGHASH("BIG_TREES_DIST_TO_ATTENUATION", 0x30D49CAD));
m_NumTreesToVolume[AUD_SMALL_TREES].Init(ATSTRINGHASH("NUM_SMALL_TREES_TO_BG_VOLUME", 0xB2B37BAD));
m_TreesDistanceToVolume[AUD_SMALL_TREES].Init(ATSTRINGHASH("SMALL_TREES_DIST_TO_ATTENUATION", 0x7E5C39CE));
m_NumTreesToVolume[AUD_BUSHES].Init(ATSTRINGHASH("NUM_BUSHES_TO_BG_VOLUME", 0x86F51D84));
m_TreesDistanceToVolume[AUD_BUSHES].Init(ATSTRINGHASH("BUSHES_DIST_TO_ATTENUATION", 0x602A042F));
for(u32 i = 0; i < g_MaxResonatingObjects; i++)
{
m_ResonatingObjects[i].Clear();
}
m_RegisteredShockwavesAllocation.Init(kMaxRegisteredShockwaves);
m_UsePortalInfoForWeather = false;
//Load audio world sectors info
// TODO: Fix file endian for PC.
if(!sm_ProcessSectors)
{
fiStream *dataFile = fiStream::Open("common:/data/levels/gta5/AudioWorldSectorsInfo.dat");
if(dataFile)
{
dataFile->Read(&g_AudioWorldSectors,sizeof(g_AudioWorldSectors));
dataFile->Close();
}
else
{
naWarningf("Fail opening audio world sectors data file");
}
}
else
{
for(u32 sector = 0; sector < g_AudioWorldSectors.GetMaxCount(); sector++)
{
audSector initSector;
initSector.numHighwayNodes = 0;
initSector.tallestBuilding = 0;
initSector.numBuildings = 0;
initSector.numTrees = 0;
initSector.isWaterSector = false;
g_AudioWorldSectors[sector] = initSector;
}
}
m_InterestingObjectRainSmoother.Init(0.001f, true);
m_ScriptedSpecialEffectMode = kSpecialEffectModeNormal;
sm_HDotToSlowmoResoVol.Init(ATSTRINGHASH("HDOT_TO_SLOWMO_RESO_VOL", 0xEE5D047A));
sm_VDotToSlowmoResoVol.Init(ATSTRINGHASH("VDOT_TO_SLOWMO_RESO_VOL", 0x97E459ED));
sm_SpecialAbToFrequency.Init(ATSTRINGHASH("SPECIAL_ABILITY_TO_FREQUENCY", 0xD0974421));
sm_CityGustDotToVolSpikeForBgTrees.Init(ATSTRINGHASH("CITY_GUST_DOT_TO_VOL", 0x5322FF8F));
sm_CountrySideGustDotToVolSpikeForBgTrees.Init(ATSTRINGHASH("COUNTRYSIDE_GUST_DOT_TO_VOL", 0x805E6E0));
m_PlayerUnderCoverFactor = 0.f;
m_PlayerUnderCoverSmoother.Init(0.03f,0.01f,0.f,100.f);
#if RSG_BANK
m_OverrideSpecialEffectMode = false;
m_SpecialEffectModeOverride = kSpecialEffectModeNormal;
#endif
}
void naEnvironment::Shutdown()
{
for(u32 i = 0; i < g_MaxInterestingLocalObjects; i++)
{
m_InterestingLocalObjects[i].object = NULL;
m_InterestingLocalObjects[i].envGroup = NULL;
m_InterestingLocalObjects[i].macsComponent = -1;
m_InterestingLocalObjects[i].stillValid = false;
BANK_ONLY( m_InterestingLocalObjects[i].underCover = true);
m_InterestingLocalObjectsCached[i].object = NULL;
m_InterestingLocalObjectsCached[i].envGroup = NULL;
m_InterestingLocalObjectsCached[i].macsComponent = -1;
m_InterestingLocalObjectsCached[i].stillValid = false;
BANK_ONLY(m_InterestingLocalObjectsCached[i].underCover = true);
}
for(u32 i = 0; i < g_MaxWindSoundsHistory; i++)
{
m_WindTriggeredSoundsHistory[i].soundRef = g_NullSoundRef;
m_WindTriggeredSoundsHistory[i].time = 0;
}
for (u8 i = 0; i < g_MaxInterestingLocalTrees; i++ )
{
m_LocalTrees[i].tree = NULL;
m_LocalTrees[i].sector = AUD_SECTOR_FL;
m_LocalTrees[i].type = AUD_BUSHES;
}
for(u32 i = 0; i < g_MaxResonatingObjects; i++)
{
m_ResonatingObjects[i].Clear();
}
}
void naEnvironment::PurgeInteriors()
{
m_ListenerInteriorInstance = NULL;
m_InteriorRoomId = 0;
m_ListenerInteriorSettings = NULL;
m_ListenerInteriorRoom = NULL;
}
//****************************************************************************************************************************************
// Game-thread GTA-specific functions
//****************************************************************************************************************************************
bool naEnvironment::ParseNodeCallback(CPathNode * pNode, void * pData)
{
audParseNodeCallbackData* callbackData = (audParseNodeCallbackData*)pData;
Vector3 position;
pNode->GetCoors(position);
if(position.x > callbackData->minPos.x &&
position.y > callbackData->minPos.y &&
position.x <= callbackData->maxPos.x &&
position.y <= callbackData->maxPos.y)
{
if(!pNode->m_address.IsEmpty() && ThePaths.IsRegionLoaded(pNode->m_address))
{
if(pNode->IsHighway())
{
callbackData->numHighwayNodes = (callbackData->numHighwayNodes) + 1;
}
callbackData->numNodes = (callbackData->numNodes) + 1;
}
}
return true;
}
void naEnvironment::Update()
{
// Update the water height at the listener position.
m_WaterHeightAtListener = CGameWorldWaterHeight::GetWaterHeightAtPos(g_AudioEngine.GetEnvironment().GetPanningListenerPosition());
// Update the metrics about how built-up the area is based on the world sector info
UpdateMetrics();
// Map our directional built-up metrics to speaker directions, to save an inverse mapping on every sound
MapDirectionalMetricsToSpeakers();
if (g_EnableLocalEnvironmentMetrics)
{
// Update our possible echo positions - on the basis of our local environment
UpdateEchoPositions();
// Update our view of the local reverb environment - based on our navmesh info, and the real-time wallscanning.
UpdateLocalReverbEnvironment();
}
UpdateInsideCarMetric();
bool passenegerInATaxi = AreWeAPassengerInATaxi();
if (passenegerInATaxi)
{
m_LastTimeWeWereAPassengerInATaxi = fwTimer::GetTimeInMilliseconds();
}
bool isInteriorCam = camInterface::IsRenderedCameraInsideVehicle();
if(!isInteriorCam && passenegerInATaxi && camInterface::GetScriptDirector().IsRendering())
{
isInteriorCam = true;
}
const f32 filterCutoff = (isInteriorCam?5000.f:24000.f);
m_CarInteriorFilterCutoff = m_CarInteriorFilterSmoother.CalculateValue(filterCutoff,fwTimer::GetTimeInMilliseconds());
// Update deafening state
UpdateDeafeningState();
#if __BANK
if(sm_ProcessSectors)
{
// world sector stuff
ProcessCachedWorldSectorOps();
}
#endif
// trigger random ambients from local objects
for(s32 i = g_MaxInterestingLocalObjects -1; i >= 0; i--)
{
if(m_InterestingLocalObjects[i].object && GetInterestingLocalObjectMaterial(i) && GetInterestingLocalObjectMaterial(i)->RandomAmbient != g_NullSoundHash &&
fwTimer::GetTimeInMilliseconds() > m_NextInterestingObjectRandTime[i] && !m_InterestingLocalObjects[i].randomAmbientSound && !IsObjectDestroyed(m_InterestingLocalObjects[i].object))
{
audSoundInitParams initParams;
initParams.Tracker = m_InterestingLocalObjects[i].object->GetPlaceableTracker();
if (!initParams.Tracker)
{
initParams.Position = VEC3V_TO_VECTOR3(m_InterestingLocalObjects[i].object->GetMatrix().GetCol3());
}
initParams.EnvironmentGroup = m_InterestingLocalObjects[i].envGroup;
const u32 interiorTrashRustle = ATSTRINGHASH("RANDOM_TRASH_RUSTLE", 0x04cfd6fb4);
u32 soundHash = GetInterestingLocalObjectMaterial(i)->RandomAmbient;
if(soundHash == interiorTrashRustle)
{
s32 roomId = m_InterestingLocalObjects[i].object->GetInteriorLocation().GetRoomIndex();
if(!m_InterestingLocalObjects[i].object->m_nFlags.bInMloRoom || roomId < 0 || roomId == INTLOC_INVALID_INDEX)
{
const u32 exteriorTrashRustle = ATSTRINGHASH("TRASH_RUSTLE_EXTERIOR_RANDOM", 0x0e8c5b77a);
soundHash = exteriorTrashRustle;
}
}
g_AmbientAudioEntity.CreateAndPlaySound_Persistent(soundHash, &m_InterestingLocalObjects[i].randomAmbientSound, &initParams);
if(audEngineUtil::ResolveProbability(0.2f))
{
// occasionally play two in quick succession
m_NextInterestingObjectRandTime[i] = fwTimer::GetTimeInMilliseconds() + audEngineUtil::GetRandomNumberInRange(3000,10000);
}
else
{
m_NextInterestingObjectRandTime[i] = fwTimer::GetTimeInMilliseconds() + audEngineUtil::GetRandomNumberInRange(15000,45000);
}
}
if(m_InterestingLocalObjects[i].object && m_InterestingLocalObjects[i].envGroup)
{
phInst * pInst = ((CObject*)((CEntity*)m_InterestingLocalObjects[i].object))->GetCurrentPhysicsInst();
if(pInst && pInst->GetLevelIndex() != phInst::INVALID_INDEX && CPhysics::GetLevel()->IsActive(pInst->GetLevelIndex()))
{
m_InterestingLocalObjects[i].envGroup->SetSource(pInst->GetCenterOfMass());
m_InterestingLocalObjects[i].envGroup->SetUpdateTime(sm_ObjOcclusionUpdateTime);
}
else if(m_InterestingLocalObjects[i].envGroup->HasGotUnderCoverResults())
{
m_InterestingLocalObjects[i].envGroup->SetSource(m_InterestingLocalObjects[i].object->GetTransform().GetPosition());
m_InterestingLocalObjects[i].envGroup->SetUpdateTime(0);
}
}
}
BANK_ONLY(DrawDebug());
#if RSG_BANK
if(sm_DrawAudioWorldSectors)
{
DrawAudioWorldSectors();
}
//GenerateAudioWorldSectorsInfo();
#endif
#if RSG_BANK
if(m_OverrideSpecialEffectMode)
{
g_AudioEngine.GetEnvironment().SetSpecialEffectMode(m_SpecialEffectModeOverride);
return;
}
#endif // RSG_BANK
// Special effect mode - underwater takes precedence over script
if(g_FrontendAudioEntity.IsMenuMusicPlaying())
{
g_AudioEngine.GetEnvironment().SetSpecialEffectMode(kSpecialEffectModePauseMenu);
}
else if(Water::IsCameraUnderwater() && !audRadioStation::IsPlayingEndCredits())
{
g_AudioEngine.GetEnvironment().SetSpecialEffectMode(kSpecialEffectModeUnderwater);
}
else
{
g_AudioEngine.GetEnvironment().SetSpecialEffectMode(m_ScriptedSpecialEffectMode);
}
// Auto-reset scripted mode; for safety script must set every frame
m_ScriptedSpecialEffectMode = kSpecialEffectModeNormal;
}
bool naEnvironment::IsObjectDestroyed(CEntity* pEntity)
{
if(pEntity && pEntity->GetIsTypeObject())
{
if(((CObject*)pEntity)->GetHealth() == 0.f)
{
return true;
}
}
return false;
}
#if __BANK
void naEnvironment::DrawAudioWorldSectors(bool draw)
{
sm_DrawAudioWorldSectors = draw;
g_audDrawWorldSectorDebug = draw;
}
void naEnvironment::AbortAudWorldSectorsGeneration()
{
sm_ProcessSectors = false;
sm_DrawAudioWorldSectors = false;
sm_IsWaitingToInit = true;
audWarpManager::WaitUntilTheSceneIsLoaded(false);
audWarpManager::FadeCamera(true);
fiStream *rescueFile = fiStream::Open("C:/AudioSectorInfoGeneratorRescue",false);
if(rescueFile)
{
sm_CurrentSectorX = 0;
sm_CurrentSectorY = 0;
rescueFile->WriteInt(&sm_CurrentSectorX,1);
rescueFile->WriteInt(&sm_CurrentSectorY,1);
rescueFile->Close();
}
}
void naEnvironment::InitAudioWorldSectorsGenerator()
{
sm_IsWaitingToInit = false;
naDisplayf("Initializing process");
naEnvironment::sm_DrawAudioWorldSectors = true;
naDisplayf("Making player invincible");
CPed * pPlayer = CGameWorld::FindLocalPlayer();
if (naVerifyf(pPlayer,"Couldn't find local player, aborting process"))
{
pPlayer->m_nPhysicalFlags.bNotDamagedByAnything = true;
}
else
{
AbortAudWorldSectorsGeneration();
}
naDisplayf("Checking if we have to resume the process or start a new one.");
sm_CurrentSectorX = 0;
sm_CurrentSectorY = 0;
CleanUpWaterTest();
fiStream *dataFile = fiStream::Open("C:/AudioWorldSectorsInfo.dat",false);
if(!dataFile)
{
dataFile = fiStream::Create("C:/AudioWorldSectorsInfo.dat");
if(naVerifyf(dataFile,"Fail opening/creating the file, aborting the process."))
{
//Reset the array in memory and write it in one go to the file, so we reset the file as well to the size it will have.
naDisplayf("%d",g_AudioWorldSectors.GetMaxCount());
for(u32 sector = 0; sector < g_AudioWorldSectors.GetMaxCount(); sector++)
{
audSector initSector;
initSector.numHighwayNodes = 0;
initSector.tallestBuilding = 0;
initSector.numBuildings = 0;
initSector.numTrees = 0;
initSector.isWaterSector = false;
dataFile->Write(&initSector,sizeof(audSector));
}
}
else
{
AbortAudWorldSectorsGeneration();
}
}
else
{
//Check if we are resuming a process.
fiStream *rescueFile = fiStream::Open("C:/AudioSectorInfoGeneratorRescue");
if(rescueFile)
{
rescueFile->ReadInt(&sm_CurrentSectorX,1);
rescueFile->ReadInt(&sm_CurrentSectorY,1);
rescueFile->Close();
}
}
dataFile->Close();
}
void naEnvironment::WriteAudioWorldSectorsData()
{
// The players has been warpped and the scene is being completely loaded, write the file
naDisplayf("Scene loaded : Writing rescue file");
fiStream *rescueFile = fiStream::Open("C:/AudioSectorInfoGeneratorRescue",false);
if(!rescueFile)
{
rescueFile = fiStream::Create("C:/AudioSectorInfoGeneratorRescue");
}
if(naVerifyf(rescueFile,"Fail opening/creating the file, aborting the process."))
{
rescueFile->WriteInt(&sm_CurrentSectorX,1);
rescueFile->WriteInt(&sm_CurrentSectorY,1);
rescueFile->Close();
}
else
{
AbortAudWorldSectorsGeneration();
}
naDisplayf("Scene loaded : Writing data file");
fiStream *dataFile = fiStream::Open("C:/AudioWorldSectorsInfo.dat",false);
if(naVerifyf(dataFile,"Fail opening/creating the file, aborting the process."))
{
u32 idx = (sm_CurrentSectorY * g_audNumWorldSectorsX) + sm_CurrentSectorX;
// Seek the last position
dataFile->Seek(idx * sizeof(audSector) );
dataFile->Write(&g_AudioWorldSectors[idx],sizeof(audSector));
dataFile->Close();
}
else
{
AbortAudWorldSectorsGeneration();
}
}
void naEnvironment::GenerateAudioWorldSectorsInfo()
{
if(sm_ProcessSectors && sm_IsWaitingToInit)
{
InitAudioWorldSectorsGenerator();
}
else if(sm_ProcessSectors)
{
if(audWarpManager::IsActive())
{
// Waiting for the scene to be loaded.
CheckForWaterSector();
return;
}
else if(sm_LogicStarted) // need to check that we have made a loop already, otherwise we have nothing to write on the file.
{
CheckForWaterSector();
WriteAudioWorldSectorsData();
sm_CurrentSectorX ++;
if(sm_CurrentSectorX == g_audNumWorldSectorsX )
{
sm_CurrentSectorX = 0;
sm_CurrentSectorY ++;
}
if(sm_CurrentSectorY == g_audNumWorldSectorsY )
{
naDisplayf("All sectors have been processed, terminating...");
fiStream *rescueFile = fiStream::Open("C:/AudioSectorInfoGeneratorRescue");
if(rescueFile)
{
sm_CurrentSectorX = 0;
sm_CurrentSectorY = 0;
rescueFile->WriteInt(&sm_CurrentSectorX,1);
rescueFile->WriteInt(&sm_CurrentSectorY,1);
rescueFile->Close();
}
sm_ProcessSectors = false;
audWarpManager::WaitUntilTheSceneIsLoaded(false);
audWarpManager::FadeCamera(true);
sm_IsWaitingToInit = false;
naDisplayf("Done");
return;
}
}
Vector3 centreFirstSector;
centreFirstSector.SetX(g_audWorldSectorsMinX + 0.5f * g_audWidthOfSector);
centreFirstSector.SetY(g_audWorldSectorsMinY + 0.5f * g_audDepthOfSector);
centreFirstSector.SetZ(100.f);
sm_LogicStarted = true;
naDisplayf("processing sector %d", (sm_CurrentSectorY * g_audNumWorldSectorsX) + sm_CurrentSectorX);
Vector3 sectorCentre = centreFirstSector;
sectorCentre.SetX(sectorCentre.GetX() + sm_CurrentSectorX*g_audWidthOfSector);
sectorCentre.SetY(sectorCentre.GetY() + sm_CurrentSectorY*g_audDepthOfSector);
naDisplayf("Wrapping player to the center sector");
naDisplayf("Loading scene....");
audWarpManager::WaitUntilTheSceneIsLoaded(true);
audWarpManager::FadeCamera(false);
audWarpManager::SetWarp(sectorCentre, sectorCentre, 0.f, true, true, 600.f);
CheckForWaterSector();
}
}
//-------------------------------------------------------------------------------------------------------------------
void naEnvironment::CleanUpWaterTest()
{
for(s32 i=0; i<NUMBER_OF_WATER_TEST; i++)
{
m_WaterTestLines[i] = Vector3(0.f,0.f,0.f);
}
m_WaterLevel = 0.f;
}
//-------------------------------------------------------------------------------------------------------------------
void naEnvironment::ProcessWaterTest(const Vector3 &startPoint, const Vector3 &deltaMovement)
{
naAssert(sm_NumWaterProbeSets < NUMBER_OF_WATER_TEST);
bool lineHitWater[NUMBER_OF_WATER_TEST];
m_WaterTestLines[0] = startPoint;
lineHitWater[0] = false;
for (s32 i = 1; i < sm_NumWaterProbeSets; i++)
{
lineHitWater[i] = false;
m_WaterTestLines[i] = m_WaterTestLines[i -1] + deltaMovement;
}
// Process them
for (s32 i = 0; i < sm_NumWaterProbeSets ; ++i)
{
// first look for a map collision
WorldProbe::CShapeTestProbeDesc probeTest;
WorldProbe::CShapeTestHitPoint result;
WorldProbe::CShapeTestResults results(result);
probeTest.SetResultsStructure(&results);
probeTest.SetContext(WorldProbe::EAudioLocalEnvironment);
probeTest.SetIsDirected(false);
probeTest.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
probeTest.SetStartAndEnd(m_WaterTestLines[i], Vector3(m_WaterTestLines[i].GetX(),m_WaterTestLines[i].GetY(),-1.f));
bool foundCollision = WorldProbe::GetShapeTestManager()->SubmitTest(probeTest);
Vec3V vWaterPos = Vec3V(V_ZERO);
if (CVfxHelper::TestLineAgainstWater(VECTOR3_TO_VEC3V(m_WaterTestLines[i]),Vec3V(m_WaterTestLines[i].GetX(),m_WaterTestLines[i].GetY(),-1.f), vWaterPos))
{
if(!foundCollision || (foundCollision && vWaterPos.GetZf() > result.GetHitPosition().GetZ()))
{
m_WaterLevel += 100.f/(sm_NumWaterProbeSets * sm_NumWaterProbeSets);// (sm_NumWaterProbeSets) because is the number of line sets we send ( sests of 12 linnes ( NUMBER_OF_WATER_TESTS))
lineHitWater[i] = true;
}
}
}
for (s32 i = 0; i < NUMBER_OF_WATER_TEST ; ++i)
{
Color32 color = Color_red;
if(lineHitWater[i])
{
color = Color_green;
}
grcDebugDraw::Line(m_WaterTestLines[i],Vector3(m_WaterTestLines[i].GetX(),m_WaterTestLines[i].GetY(),-1.f), color,1);
}
}
//-------------------------------------------------------------------------------------------------------------------
void naEnvironment::CheckForWaterSector()
{
CleanUpWaterTest();
// Calculate all the lines we need to process
Vector3 centreFirstSector;
centreFirstSector.SetX(g_audWorldSectorsMinX + 0.5f * g_audWidthOfSector);
centreFirstSector.SetY(g_audWorldSectorsMinY + 0.5f * g_audDepthOfSector);
centreFirstSector.SetZ(810.f);
Vector3 sectorCentre = centreFirstSector;
sectorCentre.SetX(sectorCentre.GetX() + sm_CurrentSectorX*g_audWidthOfSector);
sectorCentre.SetY(sectorCentre.GetY() + sm_CurrentSectorY*g_audDepthOfSector);
Vector3 startPoint = sectorCentre;
Vector3 deltaMovement = Vector3(0.f,0.f,0.f);
if(sm_NumWaterProbeSets > 1)
{
deltaMovement = Vector3((g_audWidthOfSector/(sm_NumWaterProbeSets - 1)),0.f,0.f);
}
startPoint = sectorCentre;
startPoint.SetX(sectorCentre.GetX() - (0.5f * g_audWidthOfSector));
startPoint.SetY(sectorCentre.GetY() - (0.5f * g_audDepthOfSector));
for (f32 i = 0.f; i < sm_NumWaterProbeSets; i ++)
{
// iterate over y
ProcessWaterTest(startPoint,deltaMovement);
startPoint.SetX(sectorCentre.GetX() - (0.5f * g_audWidthOfSector));
if( sm_NumWaterProbeSets > 1)
{
startPoint.SetY(startPoint.GetY() + (g_audDepthOfSector/(sm_NumWaterProbeSets - 1)));
}
}
m_WaterLevel = Min(m_WaterLevel, 100.f);
naDisplayf("m_WaterLevel %f",m_WaterLevel);
u32 idx = (sm_CurrentSectorY * g_audNumWorldSectorsX) + sm_CurrentSectorX;
if(m_WaterLevel > 40.f)
{
g_AudioWorldSectors[idx].isWaterSector = true;
}
else
{
g_AudioWorldSectors[idx].isWaterSector = false;
}
}
#endif
void naEnvironment::UpdateInsideCarMetric()
{
BANK_ONLY(m_PlayerInCarSmoother.SetRate(g_InCarRatioSmoothRate/1000.0f));
bool isPlayerVehicleATrain = false;
bool isInACar = true;
CVehicle *veh = CGameWorld::FindLocalPlayerVehicle();
if(veh)
{
CPed *player = CGameWorld::FindLocalPlayer();
if(player->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_ENTER_VEHICLE))
{
CTaskEnterVehicle *task = (CTaskEnterVehicle*)player->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_ENTER_VEHICLE);
if(task && task->GetState() < CTaskEnterVehicle::State_EnterSeat)
{
isInACar = false;
}
}
else if(player->GetPedIntelligence()->GetQueriableInterface()->IsTaskCurrentlyRunning(CTaskTypes::TASK_EXIT_VEHICLE))
{
CTaskExitVehicle *task = (CTaskExitVehicle*)player->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_EXIT_VEHICLE);
if(task && task->GetState() >= CTaskExitVehicle::State_ExitSeat)
{
isInACar = false;
}
}
if (veh->GetVehicleType()==VEHICLE_TYPE_TRAIN && isInACar)
{
isPlayerVehicleATrain = true;
}
}
else
{
if(audPedAudioEntity::GetBJVehicle())
{
isInACar = true;
}
else
{
isInACar = false;
}
}
m_IsPlayerVehicleATrain = isPlayerVehicleATrain;
m_IsPlayerInVehicle = isInACar;
m_PlayerInCarRatio = m_PlayerInCarSmoother.CalculateValue((isInACar?1.0f:0.0f), audNorthAudioEngine::GetCurrentTimeInMs());
// grcDebugDraw::AddDebugOutput("PlayerInCarRatio: %f", m_PlayerInCarRatio);
}
bool naEnvironment::IsPlayerInVehicle(f32 *inCarRatio, bool *isPlayerVehicleATrain) const
{
if (inCarRatio)
{
*inCarRatio = m_PlayerInCarRatio;
}
if (isPlayerVehicleATrain)
{
*isPlayerVehicleATrain = m_IsPlayerVehicleATrain;
}
return m_IsPlayerInVehicle;
}
bool naEnvironment::AreWeInAnInterior(bool *isInteriorASubway, f32 *playerInteriorRatio)
{
if (isInteriorASubway && m_PlayerIsInAnInterior)
{
*isInteriorASubway = m_PlayerIsInASubwayStationOrSubwayTunnel;
}
if (playerInteriorRatio)
{
*playerInteriorRatio = m_PlayerInteriorRatio;
}
return m_PlayerIsInAnInterior;
}
bool naEnvironment::AreWeAPassengerInATaxi() const
{
CPed* player = CGameWorld::FindLocalPlayer();
if(player && player->GetVehiclePedInside())
{
if(CVehicle::IsTaxiModelId(player->GetVehiclePedInside()->GetModelId()))
{
// see if the player has a car drive task, otherwise they are just a passenger
if(!player->GetIsDrivingVehicle() && !player->GetPedResetFlag(CPED_RESET_FLAG_IsEnteringOrExitingVehicle))
{
return true;
}
}
}
return false;
}
void naEnvironment::RegisterDeafeningSound(Vector3 &pos, f32 volume)
{
// Work out how loud it is at the listener.
Vector3 positionRelativeToListener = VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().ComputePositionRelativeToPanningListener(VECTOR3_TO_VEC3V(pos)));
// TODO: Need to convert the curve to a registered curve Id if its required
f32 volumeAtListener = volume + g_AudioEngine.GetEnvironment().GetDistanceAttenuation(0,/*&m_DeafeningDistanceRolloffCurve, */g_DeafeningRolloff, g_AudioEngine.GetEnvironment().ComputeDistanceRelativeToVolumeListener(pos));
// Map our volume into a deafening strength factor. I'm so making this shit up, with no idea where it's going...
// f32 linearVolume = audDriverUtil::ComputeLinearVolumeFromDb(volumeAtListener);
f32 deafeningStrength = m_DeafeningVolumeToStrengthCurve.CalculateValue(volumeAtListener);//m_VolumeToDeafeningCurve(linearVolume);
// Work out how much we hear in each made up ear.
Vector3 normalisedPosition = positionRelativeToListener;
normalisedPosition.NormalizeSafe();
f32 rightEarScaling = 0.5f * (1.0f + normalisedPosition.x); // 1 when full right, 0 when full left.
f32 leftEarScaling = 1.0f - rightEarScaling;
rightEarScaling = g_DeafeningEarLeak + rightEarScaling * (1.0f - g_DeafeningEarLeak);
leftEarScaling = g_DeafeningEarLeak + leftEarScaling * (1.0f - g_DeafeningEarLeak);
f32 rightEarStrength = deafeningStrength * rightEarScaling;
f32 leftEarStrength = deafeningStrength * leftEarScaling;
// Warningf("RES:: %f; LES: %f; ", rightEarStrength, leftEarStrength);
f32 earStrength[2];
earStrength[0] = leftEarStrength;
earStrength[1] = rightEarStrength;
// For each independent ear, work out what state we're currently in, and transition as necessary
u32 currentTime = fwTimer::GetTimeInMilliseconds();
for (s32 i=0; i<2; i++)
{
switch (m_DeafeningState[i])
{
case AUD_DEAFENING_OFF:
// Easy peasy - nothing to check
m_DeafeningState[i] = AUD_DEAFENING_RAMP_UP;
m_DeafeningMaxStrength[i] = earStrength[i];
m_DeafeningMinStrength[i] = 0.0f;
m_DeafeningPhaseEndTime[i] = currentTime + g_DeafeningRampUpTime;
break;
case AUD_DEAFENING_RAMP_UP:
// If we're going to be louder, override, kicking off a new ramp-up phase, with old level as new min. If not, do nothing.
if (earStrength[i] > m_DeafeningMaxStrength[i])
{
m_DeafeningState[i] = AUD_DEAFENING_RAMP_UP;
// Get our current volume, to use as the min for the new ramp
f32 proportionThrough = 1.0f - (((f32)m_DeafeningPhaseEndTime[i] - (f32)currentTime) / (f32)g_DeafeningRampUpTime);
f32 min = (m_DeafeningMaxStrength[i] - m_DeafeningMinStrength[i]) * proportionThrough + m_DeafeningMinStrength[i];
m_DeafeningMaxStrength[i] = earStrength[i];
m_DeafeningMinStrength[i] = min;
m_DeafeningPhaseEndTime[i] = currentTime + g_DeafeningRampUpTime;
}
break;
case AUD_DEAFENING_HOLD:
// If we're louder, override, kicking off a new ramp-up phase, with old level as new min. If not, do nothing.
if (earStrength[i] > m_DeafeningMaxStrength[i])
{
m_DeafeningState[i] = AUD_DEAFENING_RAMP_UP;
m_DeafeningMinStrength[i] = m_DeafeningMaxStrength[i];
m_DeafeningMaxStrength[i] = earStrength[i];
m_DeafeningPhaseEndTime[i] = currentTime + g_DeafeningRampUpTime;
}
break;
case AUD_DEAFENING_RAMP_DOWN:
// if we're louder than we are currently, kick off a new ramp up phase, otherwise ignore (which is a little cheap, but whatever)
f32 proportionThrough = 1.0f - (((f32)m_DeafeningPhaseEndTime[i] - (f32)currentTime) / (f32)g_DeafeningRampDownTime);
f32 currentStrength = m_DeafeningMaxStrength[i] * (1.0f - proportionThrough);
if (earStrength[i] > currentStrength)
{
m_DeafeningState[i] = AUD_DEAFENING_RAMP_UP;
m_DeafeningMinStrength[i] = currentStrength;
m_DeafeningMaxStrength[i] = earStrength[i];
m_DeafeningPhaseEndTime[i] = currentTime + g_DeafeningRampUpTime;
}
}
}
}
void naEnvironment::UpdateDeafeningState()
{
u32 currentTime = audNorthAudioEngine::GetCurrentTimeInMs();// This timer takes pausing into account fwTimer::GetTimeInMilliseconds();
f32 earStrength[2];
// Instantly stop any of this if we're in slowmo, or paused, or the screen's fully faded out
f32 timeWarp = fwTimer::GetTimeWarp();
f32 fadeLevel = camInterface::GetFadeLevel();
bool playingCutscene = gVpMan.AreWidescreenBordersActive() ||
(CutSceneManager::GetInstance() && CutSceneManager::GetInstance()->IsStreamedCutScene());
if (timeWarp<0.9f || audNorthAudioEngine::IsAudioPaused() || fadeLevel>=1.0f || playingCutscene)
{
m_DeafeningState[0] = AUD_DEAFENING_OFF;
m_DeafeningState[1] = AUD_DEAFENING_OFF;
}
if(fadeLevel >= 1.0f)
{
audNorthAudioEngine::ResetLoudSoundTime();
}
for (s32 i=0; i<2; i++)
{
switch (m_DeafeningState[i])
{
case AUD_DEAFENING_OFF:
earStrength[i] = 0.0f;
break;
case AUD_DEAFENING_RAMP_UP:
// Are we done with our ramping yet?
if (currentTime >= m_DeafeningPhaseEndTime[i])
{
// Go into Hold phase, and set strength to max
m_DeafeningState[i] = AUD_DEAFENING_HOLD;
m_DeafeningPhaseEndTime[i] = currentTime + g_DeafeningHoldTime;
earStrength[i] = m_DeafeningMaxStrength[i];
}
else
{
f32 proportionThrough = 1.0f - (((f32)m_DeafeningPhaseEndTime[i] - (f32)currentTime) / (f32)g_DeafeningRampUpTime);
earStrength[i] = (m_DeafeningMaxStrength[i] - m_DeafeningMinStrength[i]) * proportionThrough + m_DeafeningMinStrength[i];
}
break;
case AUD_DEAFENING_HOLD:
// Are we done with the hold phase yet?
if (currentTime >= m_DeafeningPhaseEndTime[i])
{
// Go into Ramp Down phase
m_DeafeningState[i] = AUD_DEAFENING_RAMP_DOWN;
m_DeafeningPhaseEndTime[i] = currentTime + g_DeafeningRampDownTime;
}
earStrength[i] = m_DeafeningMaxStrength[i];
break;
case AUD_DEAFENING_RAMP_DOWN:
// Are we done with the ramp down phase yet?
if (currentTime >= m_DeafeningPhaseEndTime[i])
{
// Go into nothing phase
m_DeafeningState[i] = AUD_DEAFENING_OFF;
earStrength[i] = 0.0f;
}
else
{
f32 proportionThrough = 1.0f - (((f32)m_DeafeningPhaseEndTime[i] - (f32)currentTime) / (f32)g_DeafeningRampDownTime);
earStrength[i] = m_DeafeningMaxStrength[i] * (1.0f - proportionThrough);
}
break;
}
}
g_AudioEngine.GetEnvironment().SetEarAttenuation(m_DeafeningAttenuationCurve.CalculateValue(earStrength[0]),m_DeafeningAttenuationCurve.CalculateValue(earStrength[1]));
m_EarStrength[0] = earStrength[0];
m_EarStrength[1] = earStrength[1];
f32 deafeningStrength = Max(earStrength[0], earStrength[1]);
// use the most restrictive filter cutoff from deafening computation and the car interior filter
if(g_UseInteriorCarFilter)
{
g_AudioEngine.GetEnvironment().SetDeafeningFilterCutoff(Min<f32>(m_CarInteriorFilterCutoff, m_DeafeningFilterCurve.CalculateValue(deafeningStrength)));
}
else
{
g_AudioEngine.GetEnvironment().SetDeafeningFilterCutoff(m_DeafeningFilterCurve.CalculateValue(deafeningStrength));
}
g_AudioEngine.GetEnvironment().SetDeafeningReverbWetness(m_DeafeningReverbCurve.CalculateValue(deafeningStrength));
if(m_DeafeningState[0] == AUD_DEAFENING_OFF && m_DeafeningState[1] == AUD_DEAFENING_OFF)
{
g_AudioEngine.GetEnvironment().SetDeafeningAffectsEverything(false);
}
else
{
// when we're doing real deafening we want to affect all sounds regardless of effect route
g_AudioEngine.GetEnvironment().SetDeafeningAffectsEverything(true);
}
}
void naEnvironment::GetDeafeningStrength(f32* leftEar, f32* rightEar)
{
naAssertf (leftEar, "Null ptr leftEar (f32*) passed into GetDeafeningStrength");
naAssertf (rightEar, "Null ptr rightEar (f32*) passed into GetDeafeningStrength");
if (leftEar && rightEar)
{
*leftEar = m_EarStrength[0];
*rightEar = m_EarStrength[1];
}
}
bool naEnvironment::IsDeafeningOn()
{
if (m_DeafeningState[0] != AUD_DEAFENING_OFF || m_DeafeningState[1] != AUD_DEAFENING_OFF)
{
return true;
}
return false;
}
bool naEnvironment::FindCloseObjectsCB(fwEntity* entity, void* )
{
CEntity* pEntity = (CEntity*) entity;
if ( naVerifyf(pEntity, "Null ptr pEntity (CEntity*) passed into FindClosestObjectsCB"))
{
CBaseModelInfo *modelInfo = pEntity->GetBaseModelInfo();
if(modelInfo)
{
if(modelInfo->GetIsTree())
{
audNorthAudioEngine::GetGtaEnvironment()->AddLocalTree(pEntity);
}
else
{
const ModelAudioCollisionSettings * materialSettings = NULL;
s32 component = -1;
if(pEntity->GetType() == ENTITY_TYPE_OBJECT || pEntity->GetType() == ENTITY_TYPE_BUILDING)
{
if(pEntity->GetFragInst() && pEntity->GetFragInst()->GetPartBroken())
{
u32 numGroups = (pEntity->GetFragInst()) ? pEntity->GetFragInst()->GetTypePhysics()->GetNumChildGroups() : 0;
bool isInteresting = false;
component = 0;
for(u32 j=0; j < numGroups; j++)
{
if(!pEntity->GetFragInst()->GetGroupBroken(j))
{
u32 numComponents = (pEntity->GetFragInst()) ? pEntity->GetFragInst()->GetTypePhysics()->GetGroup(j)->GetNumChildren() : 0;
for(u32 k=0; k < numComponents; k++)
{
materialSettings = audCollisionAudioEntity::GetFragComponentMaterialSettings(pEntity, component,true);
if(materialSettings && (materialSettings->RainLoop != g_NullSoundHash || materialSettings->RandomAmbient != g_NullSoundHash || materialSettings->ShockwaveSound != g_NullSoundHash || materialSettings->EntityResonance != g_NullSoundHash || materialSettings->WindSounds != 0))
{
isInteresting = true;
break;
}
component++;
}
}
else
{
component += (pEntity->GetFragInst()) ? pEntity->GetFragInst()->GetTypePhysics()->GetGroup(j)->GetNumChildren() : 0;
}
if(isInteresting)
{
break;
}
}
}
else
{
materialSettings = pEntity->GetBaseModelInfo()->GetAudioCollisionSettings();
}
if(materialSettings && (materialSettings->RainLoop != g_NullSoundHash || materialSettings->RandomAmbient != g_NullSoundHash || materialSettings->ShockwaveSound != g_NullSoundHash || materialSettings->EntityResonance != g_NullSoundHash || materialSettings->WindSounds != 0))
{
// add this to the list
audNorthAudioEngine::GetGtaEnvironment()->AddInterestingObject(pEntity, materialSettings->ShockwaveSound, materialSettings->EntityResonance,component, materialSettings->RainLoop != g_NullSoundHash);
}
}
}
}
}
return true;
}
void naEnvironment::UpdateSectorFitness(const u8 sector)
{
// FIrst of all update each fitness for the current sector.
m_BackGroundTrees[sector].bushesFitness = (f32)(MAX_FB_SECTORS * m_BackGroundTrees[sector].numBushes)/(f32)g_MaxInterestingLocalTrees;
m_BackGroundTrees[sector].bushesFitness *= m_BackGroundTreesFitness[AUD_BUSHES];
m_BackGroundTrees[sector].treesFitness = (f32)(MAX_FB_SECTORS * m_BackGroundTrees[sector].numTrees)/(f32)g_MaxInterestingLocalTrees;
m_BackGroundTrees[sector].treesFitness *= m_BackGroundTreesFitness[AUD_SMALL_TREES];
m_BackGroundTrees[sector].bigTreesFitness = (f32)(MAX_FB_SECTORS * m_BackGroundTrees[sector].numBigTrees)/(f32)g_MaxInterestingLocalTrees;
m_BackGroundTrees[sector].bigTreesFitness *= m_BackGroundTreesFitness[AUD_BIG_TREES];
// update current sector fitness
switch((audTreeTypes)m_BackGroundTreesSounds[m_CurrentSoundIdx[sector]][sector].type)
{
case AUD_BUSHES:
m_BackGroundTreesSounds[m_CurrentSoundIdx[sector]][sector].fitness = m_BackGroundTrees[sector].bushesFitness;
break;
case AUD_SMALL_TREES:
m_BackGroundTreesSounds[m_CurrentSoundIdx[sector]][sector].fitness = m_BackGroundTrees[sector].treesFitness;
break;
case AUD_BIG_TREES:
m_BackGroundTreesSounds[m_CurrentSoundIdx[sector]][sector].fitness = m_BackGroundTrees[sector].bigTreesFitness;
break;
default:
break;
}
}
void naEnvironment::PlayBgTreesWind(const u8 sector, const u8 soundIdx, const u8 newSoundIdx,const audMetadataRef soundRef,const u8 type,const f32 /*fitness*/,const f32 avgDistance,const u32 numTrees,Vec3V_In soundDir,bool change)
{
if(g_FrontendAudioEntity.GetSpecialAbilityMode() == audFrontendAudioEntity::kSpecialAbilityModeTrevor)
{
return;
}
u32 currentIdx = !change ? soundIdx : newSoundIdx;
//m_BackGroundTreesFitness[type] *= g_FitnessReduced;
//m_BackGroundTreesSounds[soundIdx][sector].fitness = fitness;
m_BackGroundTreesSounds[currentIdx][sector].type = (audTreeTypes)type;
f32 playerInteriorRatio = 0.0f;
audNorthAudioEngine::GetGtaEnvironment()->AreWeInAnInterior(NULL, &playerInteriorRatio);
playerInteriorRatio = ClampRange(playerInteriorRatio,0.f,1.f);
f32 linVol = m_NumTreesToVolume[type].CalculateValue((f32)numTrees) * m_TreesDistanceToVolume[type].CalculateValue(avgDistance) * (1.f - playerInteriorRatio);
m_BackGroundTreesSounds[currentIdx][sector].volSmoother.SetRate(g_VolSmootherRate);
linVol = m_BackGroundTreesSounds[currentIdx][sector].volSmoother.CalculateValue(linVol,g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0));
BANK_ONLY(m_BackGroundTreesSounds[currentIdx][sector].linVol = linVol;)
f32 vol = audDriverUtil::ComputeDbVolumeFromLinear(linVol);
if(vol > g_SilenceVolume)
{
audSoundInitParams initParams;
initParams.Volume = vol;
initParams.Position = VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPositionLastFrame() + soundDir * ScalarV(sm_TreesSoundDist));
if(!change)
{
g_WeatherAudioEntity.CreateAndPlaySound_Persistent(soundRef,&m_BackGroundTreesSounds[soundIdx][sector].sound,&initParams);
}
else
{
g_WeatherAudioEntity.CreateAndPlaySound_Persistent(soundRef,&m_BackGroundTreesSounds[newSoundIdx][sector].sound,&initParams);
//Stop the old one
if( m_BackGroundTreesSounds[soundIdx][sector].sound)
{
m_BackGroundTreesSounds[soundIdx][sector].sound->StopAndForget();
BANK_ONLY(m_BackGroundTreesSounds[soundIdx][sector].linVol = 0;)
}
m_BackGroundTreesSounds[soundIdx][sector].volSmoother.Reset();
m_CurrentSoundIdx[sector] = newSoundIdx;
}
}
}
void naEnvironment::UpdateBgTreeSound(const u8 soundIdx, const u8 sector, const u32 numTrees,const u8 type, const f32 avgDistance,Vec3V_In soundDir)
{
if (m_BackGroundTreesSounds[soundIdx][sector].sound)
{
f32 playerInteriorRatio = 0.0f;
audNorthAudioEngine::GetGtaEnvironment()->AreWeInAnInterior(NULL, &playerInteriorRatio);
playerInteriorRatio = ClampRange(playerInteriorRatio,0.f,1.f);
f32 linVol = m_NumTreesToVolume[type].CalculateValue((f32)numTrees) * m_TreesDistanceToVolume[type].CalculateValue(avgDistance) * (1.f - playerInteriorRatio);
f32 volSpike = 0.f;
if(g_WeatherAudioEntity.IsGustInProgress())
{
Vec3V pos = m_BackGroundTreesSounds[soundIdx][sector].sound->GetRequestedPosition();
Vector3 dirToGust = g_WeatherAudioEntity.GetGustMiddlePoint() - VEC3V_TO_VECTOR3(pos);
dirToGust.SetZ(0.f);
Vector3 gustLine = g_WeatherAudioEntity.GetGustDirection();
gustLine.RotateZ(90.f*DtoR);
gustLine.SetZ(0.f);
dirToGust.Normalize();
gustLine.Normalize();
f32 dotProduct = gustLine.Dot(dirToGust);
if(g_AmbientAudioEntity.IsPlayerInTheCity())
{
volSpike = sm_CityGustDotToVolSpikeForBgTrees.CalculateValue(fabs(dotProduct));
}
else
{
volSpike = sm_CountrySideGustDotToVolSpikeForBgTrees.CalculateValue(fabs(dotProduct));
}
#if __BANK
if(sm_DrawBgTreesInfo)
{
char text[256];
formatf(text,sizeof(text),"DOT %f Vol %f", fabs(dotProduct),volSpike);
grcDebugDraw::Text(pos + Vec3V(0.0f,0.0f,1.6f), Color_white,text);
grcDebugDraw::Line(VEC3V_TO_VECTOR3(pos), VEC3V_TO_VECTOR3(pos) + gustLine *2.f, Color32(255, 0, 0, 255));
grcDebugDraw::Line(VEC3V_TO_VECTOR3(pos), VEC3V_TO_VECTOR3(pos) + dirToGust *2.f, Color32(0, 255, 0, 255));
}
#endif
}
linVol *= audDriverUtil::ComputeLinearVolumeFromDb(volSpike);
m_BackGroundTreesSounds[soundIdx][sector].volSmoother.SetRate(g_VolSmootherRate);
linVol = m_BackGroundTreesSounds[soundIdx][sector].volSmoother.CalculateValue(linVol,g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0));
BANK_ONLY(m_BackGroundTreesSounds[soundIdx][sector].linVol = linVol);
f32 vol = audDriverUtil::ComputeDbVolumeFromLinear(linVol);
if(g_FrontendAudioEntity.GetSpecialAbilityMode() == audFrontendAudioEntity::kSpecialAbilityModeTrevor)
{
vol = g_SilenceVolume;
}
if(vol > g_SilenceVolume)
{
m_BackGroundTreesSounds[soundIdx][sector].sound->SetRequestedVolume(vol);
#if __BANK
if(sm_MuteBgFL && sector == AUD_SECTOR_FL)
{
m_BackGroundTreesSounds[soundIdx][sector].sound->SetRequestedVolume(-100.f);
m_BackGroundTreesSounds[soundIdx][sector].linVol = 0.f;
}
if(sm_MuteBgFR && sector == AUD_SECTOR_FR)
{
m_BackGroundTreesSounds[soundIdx][sector].sound->SetRequestedVolume(-100.f);
m_BackGroundTreesSounds[soundIdx][sector].linVol = 0.f;
}
if(sm_MuteBgRL && sector == AUD_SECTOR_RL)
{
m_BackGroundTreesSounds[soundIdx][sector].sound->SetRequestedVolume(-100.f);
m_BackGroundTreesSounds[soundIdx][sector].linVol = 0.f;
}
if(sm_MuteBgRR && sector == AUD_SECTOR_RR)
{
m_BackGroundTreesSounds[soundIdx][sector].sound->SetRequestedVolume(-100.f);
m_BackGroundTreesSounds[soundIdx][sector].linVol = 0.f;
}
#endif
m_BackGroundTreesSounds[soundIdx][sector].sound->SetRequestedPosition(VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPositionLastFrame() + soundDir * ScalarV(sm_TreesSoundDist)));
}
else
{
m_BackGroundTreesSounds[soundIdx][sector].sound->StopAndForget();
}
}
}
void naEnvironment::GetWindTreeSectorInfo(audMetadataRef &soundRef, const u8 sector, audTreeTypes &type,f32 &fitness,f32 &avgDistance,Vec3V_InOut soundDir,u32 &numTrees)
{
if( m_BackGroundTrees[sector].bushesFitness > 0.f)
{
type = AUD_BUSHES;
fitness = m_BackGroundTrees[sector].bushesFitness;
soundRef = m_WindBushesSoundset.Find(ATSTRINGHASH("Background1", 0x6FE7F076));
}
if ((m_BackGroundTrees[sector].treesFitness > 0.f) && (m_BackGroundTrees[sector].treesFitness > fitness))
{
type = AUD_SMALL_TREES;
fitness = m_BackGroundTrees[sector].treesFitness;
soundRef = m_WindTreesSoundset.Find(ATSTRINGHASH("Background1", 0x6FE7F076));
}
if(m_BackGroundTrees[sector].bigTreesFitness > 0.f && m_BackGroundTrees[sector].bigTreesFitness > fitness)
{
type = AUD_BIG_TREES;
fitness = m_BackGroundTrees[sector].bigTreesFitness;
soundRef = m_WindBigTreesSoundset.Find(ATSTRINGHASH("Background1", 0x6FE7F076));
}
audSoundInitParams initParams;
switch((audFBSectors)sector)
{
case AUD_SECTOR_FL :
//north west
soundDir = Vec3V(-0.707f,0.707f,0.0f);
break;
case AUD_SECTOR_FR :
// north east
soundDir = Vec3V(0.707f,0.707f,0.0f);
break;
case AUD_SECTOR_RL :
// south west
soundDir = Vec3V(-0.707f,-0.707f,0.0f);
break;
case AUD_SECTOR_RR :
// south east
soundDir = Vec3V(0.707f,-0.707f,0.0f);
break;
default:
break;
}
m_BackGroundTreesFitness[type] *= g_FitnessReduced;
switch((audTreeTypes)type)
{
case AUD_BUSHES:
numTrees = m_BackGroundTrees[sector].numBushes;
break;
case AUD_SMALL_TREES:
numTrees = m_BackGroundTrees[sector].numTrees;
break;
case AUD_BIG_TREES:
numTrees = m_BackGroundTrees[sector].numBigTrees;
break;
default:
break;
}
f32 clostestDist[5];//sm_NumTreesForAvgDist
s8 clostestIdx[5];//sm_NumTreesForAvgDist
u8 index = 0;
for ( u8 k= 0; k < sm_NumTreesForAvgDist ; k++)
{
clostestDist[k] = LARGE_FLOAT;
clostestIdx[k] = -1;
}
for(u8 treeIdx = 0; treeIdx < m_NumLocalTrees; treeIdx ++)
{
if(m_LocalTrees[treeIdx].tree)
{
if((m_LocalTrees[treeIdx].sector == (audFBSectors)sector) && (m_LocalTrees[treeIdx].type == type))
{
Vec3V treePos2D = m_LocalTrees[treeIdx].tree->GetTransform().GetPosition();
treePos2D.SetZ(0);
Vec3V listenerPos2D = g_AudioEngine.GetEnvironment().GetVolumeListenerMatrix().GetCol3();
listenerPos2D.SetZ(0);
Vec3V dirToTree = Subtract(treePos2D, listenerPos2D);
f32 distance = fabs(((ScalarV)Mag(dirToTree)).Getf());
if(index < sm_NumTreesForAvgDist)
{
clostestDist[index] = distance;
clostestIdx[index ++] = treeIdx;
}
else
{
// replace for the bigger distance
f32 biggerDist = 0.f;
s8 biggerIndex = -1;
for ( u8 k= 0; k < sm_NumTreesForAvgDist ; k++)
{
if(clostestDist[k] > biggerDist)
{
biggerIndex = k;
biggerDist = clostestDist[k];
}
}
if (biggerIndex != -1)
{
naAssertf(biggerIndex < sm_NumTreesForAvgDist, "wrong index when computing avg distance to trees");
clostestDist[biggerIndex] = distance;
clostestIdx[biggerIndex] = treeIdx;
}
}
}
}
}
for ( u8 k= 0; k < index ; k++)
{
avgDistance += clostestDist[k];
}
avgDistance /= sm_NumTreesForAvgDist;
m_BackGroundTreesSounds[m_CurrentSoundIdx[sector]][sector].avgDistance = avgDistance;
}
void naEnvironment::UpdateInterestingObjects()
{
#if __BANK
u32 numRainObjects = 0;
#endif
u32 numWindValidIdx = 0;
s32 windObjectValidIdx[g_MaxInterestingLocalObjects];
for(u8 i = 0; i < g_MaxInterestingLocalObjects; i ++)
{
windObjectValidIdx[i] = -1;
}
UpdateShockwaves();
for(s32 i = g_MaxInterestingLocalObjects -1 ; i >=0; i--)
{
if(m_InterestingLocalObjects[i].object )
{
UpdateResonance(i);
UpdateRainObject(i);
if(!audNorthAudioEngine::GetGtaEnvironment()->AreWeInAnInterior() && !m_InterestingLocalObjects[i].object->GetIsInInterior())
{
if( audNorthAudioEngine::GetCurrentTimeInMs() > m_NextWindObjectTime )
{
const ModelAudioCollisionSettings* material = GetInterestingLocalObjectMaterial(i);
if(material && material->WindSounds != 0)
{
Vec3V pos = m_InterestingLocalObjects[i].object->GetTransform().GetPosition();
ScalarV distSqd = MagSquared(Subtract(pos,g_AudioEngine.GetEnvironment().GetVolumeListenerPositionLastFrame()));
if(IsTrue(distSqd >= ScalarV(100)))
{
windObjectValidIdx[numWindValidIdx++] = i;
}
}
}
}
#if __BANK
if(m_InterestingLocalObjects[i].rainSound)
{
numRainObjects ++;
}
#endif
}
}
s32 indexToUse = windObjectValidIdx[(u32)audEngineUtil::GetRandomNumberInRange(0.f,(f32)numWindValidIdx)];
if(indexToUse != -1)
{
const ModelAudioCollisionSettings* material = GetInterestingLocalObjectMaterial(indexToUse);
if(material && material->WindSounds != 0)
{
// HL, hacky fix for url:bugstar:6743217 - Beach party : Lots of bottle rolling / rattle assets playing from the bar
const bool beachPartyZoneActive = CNetwork::IsGameInProgress() && g_AmbientAudioEntity.IsZoneActive(ATSTRINGHASH("AZ_DLC_H4_IH_BEACH_01", 0x17265575));
const bool suppressPropWindSound = beachPartyZoneActive && (material->WindSounds == ATSTRINGHASH("WIND_PROPS_BOTTLES_RATTLE", 0x6B1C1499) || material->WindSounds == ATSTRINGHASH("WIND_PROPS_BOTTLE_ROLL", 0x9818D730));
audSoundSet windSounds;
windSounds.Init(material->WindSounds);
audMetadataRef soundRef = windSounds.Find(ATSTRINGHASH("WIND_REACTION", 0xD238F5E5));
if(soundRef != g_NullSoundRef)
{
bool found = false;
s32 index = -1;
// look in the history :
for(u32 i = 0 ; i < g_MaxWindSoundsHistory; i++)
{
if(m_WindTriggeredSoundsHistory[i].soundRef == soundRef)
{
found = true;
if(m_WindTriggeredSoundsHistory[i].time + sm_MinTimeToRetrigger > audNorthAudioEngine::GetCurrentTimeInMs() )
{
index = i;
}
break;
}
else if (m_WindTriggeredSoundsHistory[i].soundRef == g_NullSoundRef )
{
index = i;
}
}
if (!found && index == -1)
{
u32 oldestTime = 0;
for(u32 i = 0 ; i < g_MaxWindSoundsHistory; i++)
{
if (oldestTime <= m_WindTriggeredSoundsHistory[i].time)
{
oldestTime = m_WindTriggeredSoundsHistory[i].time;
index = i;
}
}
}
if(index != -1)
{
if (!suppressPropWindSound)
{
audSoundInitParams initParams;
initParams.Tracker = m_InterestingLocalObjects[indexToUse].object->GetPlaceableTracker();
initParams.EnvironmentGroup = m_InterestingLocalObjects[indexToUse].envGroup;
initParams.UpdateEntity = true;
initParams.u32ClientVar = AUD_ENV_SOUND_WIND;
g_AmbientAudioEntity.CreateAndPlaySound(soundRef, &initParams);
}
u32 windTimeVariance = (u32)(sm_WindTimeVariance * g_WeatherAudioEntity.GetWindStrength());
m_NextWindObjectTime = audNorthAudioEngine::GetCurrentTimeInMs() + (u32)audEngineUtil::GetRandomNumberInRange((f32)(sm_MinWindObjTime - windTimeVariance),(f32)(sm_MaxWindObjtTime - windTimeVariance));
m_WindTriggeredSoundsHistory[index].soundRef = soundRef;
m_WindTriggeredSoundsHistory[index].time = audNorthAudioEngine::GetCurrentTimeInMs();
}
}
}
}
#if __BANK
if(g_audDrawLocalObjects)
{
char txt[128];
formatf(txt,"NUM RAIN OBJECTS : %u",numRainObjects);
grcDebugDraw::AddDebugOutput(Color_green,txt);
}
#endif
UpdateInterestingTrees();
}
void naEnvironment::UpdateInterestingTrees()
{
m_BackGroundTreesFitness[AUD_BIG_TREES] = sm_BigTreesFitness;
m_BackGroundTreesFitness[AUD_SMALL_TREES] = sm_TreesFitness;
m_BackGroundTreesFitness[AUD_BUSHES] = sm_BushesFitness;
for (u8 i = 0; i < MAX_FB_SECTORS; i ++)
{
UpdateSectorFitness(i);
audMetadataRef soundRef = g_NullSoundRef;
audTreeTypes type = AUD_BUSHES;
f32 fitness = 0.f;
f32 avgDistance = 0.f;
u32 numTrees = 0;
Vec3V soundDir = Vec3V(V_ZERO);
GetWindTreeSectorInfo(soundRef,i,type,fitness,avgDistance,soundDir,numTrees);
u8 nextIndex = (m_CurrentSoundIdx[i] + 1) & 1;
if( !m_BackGroundTreesSounds[m_CurrentSoundIdx[i]][i].sound && !m_BackGroundTreesSounds[nextIndex][i].sound)
{
// if we are not playing any sound for the current sector yet, play it if we have to.
PlayBgTreesWind(i,m_CurrentSoundIdx[i],nextIndex,soundRef,(u8)type,fitness,avgDistance,numTrees,soundDir,false);
}
else if (m_BackGroundTreesSounds[m_CurrentSoundIdx[i]][i].sound)
{
if( (type != m_BackGroundTreesSounds[m_CurrentSoundIdx[i]][i].type) && (fitness > m_BackGroundTreesSounds[m_CurrentSoundIdx[i]][i].fitness))
{
PlayBgTreesWind(i,m_CurrentSoundIdx[i],nextIndex,soundRef,(u8)type,fitness,avgDistance,numTrees,soundDir,true);
}
}
UpdateBgTreeSound(m_CurrentSoundIdx[i], i,numTrees,(u8)type,avgDistance,soundDir);
}
#if __USEDEBUGAUDIO
if(sm_DrawBgTreesInfo)
{
for (u32 i = 0; i < m_NumLocalTrees; i ++)
{
if(m_LocalTrees[i].tree)
{
grcDebugDraw::Sphere(m_LocalTrees[i].tree->GetTransform().GetPosition(),2.f,Color32(255,0,0,128));
char text[256];
formatf(text,sizeof(text),"Size %f", m_LocalTrees[i].tree->GetBoundRadius());
grcDebugDraw::Text(m_LocalTrees[i].tree->GetTransform().GetPosition()+ Vec3V(0.0f,0.0f,1.6f), Color_white,text);
}
}
static audMeterList meterLists[8];
static const char *namesList[24][1]={{"FL0: B"},{"FL0: ST"},{"FL0: T"},{"FL1: B"},{"FL1: ST"},{"FL1: T"},
{"FR0: B"},{"FR0: ST"},{"FR0: T"},{"FR1: B"},{"FR1: ST"},{"FR1: T"},
{"RL0: B"},{"RL0: ST"},{"RL0: T"},{"RL1: B"},{"RL1: ST"},{"RL1: T"},
{"RR0: B"},{"RR0: ST"},{"RR0: T"},{"RR1: B"},{"RR1: ST"},{"RR1: T"}};
for (u32 i = 0; i < MAX_FB_SECTORS; i ++)
{
char text[256];
formatf(text,sizeof(text),"SECTOR : %u, Dist :%f [BUSHES %u] [TREES:%u] [BIG TREES %u]", i, m_BackGroundTreesSounds[m_CurrentSoundIdx[i]][i].avgDistance, m_BackGroundTrees[i].numBushes,m_BackGroundTrees[i].numTrees,m_BackGroundTrees[i].numBigTrees);
grcDebugDraw::AddDebugOutput(Color_green,text);
if( m_BackGroundTreesSounds[0][i].sound)
{
grcDebugDraw::Sphere(m_BackGroundTreesSounds[0][i].sound->GetRequestedPosition(),2.f,Color32(0,0,255,128));
char text[256];
formatf(text,sizeof(text),"[Sector: %u] [Sound %s] [Volume %f]",i, g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(m_BackGroundTreesSounds[0][i].sound->GetNameTableOffset(),0),m_BackGroundTreesSounds[0][i].sound->GetRequestedVolume());
grcDebugDraw::AddDebugOutput(Color_white,text);
}
if( m_BackGroundTreesSounds[1][i].sound)
{
grcDebugDraw::Sphere(m_BackGroundTreesSounds[1][i].sound->GetRequestedPosition(),2.f,Color32(0,0,255,128));
char text[256];
formatf(text,sizeof(text),"[Sector: %u] [Sound %s] [Volume %f]",i, g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(m_BackGroundTreesSounds[1][i].sound->GetNameTableOffset(),0),m_BackGroundTreesSounds[1][i].sound->GetRequestedVolume());
grcDebugDraw::AddDebugOutput(Color_white,text);
}
meterLists[i*2].left = 90.f;
meterLists[i*2].width = 50.f;
meterLists[i*2].bottom = 350.f + 100.f * i;
meterLists[i*2].height = 50.f;
meterLists[i*2].names = namesList[i*6 + m_BackGroundTreesSounds[0][i].type];
meterLists[i*2].values = &m_BackGroundTreesSounds[0][i].linVol;
meterLists[i*2].numValues = 1;
audNorthAudioEngine::DrawLevelMeters(&meterLists[i*2]);
meterLists[i*2 + 1].left = 200.f;
meterLists[i*2 + 1].width = 50.f;
meterLists[i*2 + 1].bottom = 350.f + 100.f *i;
meterLists[i*2 + 1].height = 50.f;
meterLists[i*2 + 1].names = namesList[i*6 + MAX_AUD_TREE_TYPES + m_BackGroundTreesSounds[1][i].type];
meterLists[i*2 + 1].values = &m_BackGroundTreesSounds[1][i].linVol;
meterLists[i*2 + 1].numValues = 1;
audNorthAudioEngine::DrawLevelMeters(&meterLists[i*2 + 1]);
}
}
#endif
}
bool naEnvironment::ShouldTriggerWindGustEnd()
{
Vec3V gustPos2D = VECTOR3_TO_VEC3V(g_WeatherAudioEntity.GetGustMiddlePoint());
gustPos2D.SetZ(0);
Vec3V listenerPos2D = g_AudioEngine.GetEnvironment().GetVolumeListenerMatrix().GetCol3();
listenerPos2D.SetZ(0);
Vec3V dirToGust = Subtract(gustPos2D, listenerPos2D);
dirToGust = Normalize(dirToGust);
float frontDot = Dot( Vec3V(0.0f,1.f,0.0f), dirToGust).Getf();
float rightDot = Dot( Vec3V(1.f,0.0f,0.0f), dirToGust).Getf();
audFBSectors sector = AUD_SECTOR_FL;
if((frontDot >= 0.f))
{
if(rightDot >= 0.f)
sector = AUD_SECTOR_FR;
else
sector = AUD_SECTOR_FL;
}
else
{
if(rightDot >= 0.f)
sector = AUD_SECTOR_RR;
else
sector = AUD_SECTOR_RL;
}
u32 numTreesInSector = m_BackGroundTrees[sector].numBigTrees + m_BackGroundTrees[sector].numTrees + m_BackGroundTrees[sector].numBushes;
if( numTreesInSector >= sm_MinNumberOfTreesToTriggerGustEnd )
{
return true;
}
return false;
}
s32 naEnvironment::AllocateShockwave()
{
for(s32 i = 0; i < kMaxRegisteredShockwaves; i++)
{
if(m_RegisteredShockwavesAllocation.IsClear(i))
{
m_RegisteredShockwavesAllocation.Set(i);
return i;
}
}
return kInvalidShockwaveId;
}
void naEnvironment::FreeShockwave(const s32 shockwaveId)
{
naAssertf(shockwaveId != kInvalidShockwaveId, "Invalid shockwave id (%d) passed into FreeShockwave", shockwaveId);
m_RegisteredShockwavesAllocation.Clear(shockwaveId);
}
void naEnvironment::UpdateShockwave(s32 &shockwaveId, const audShockwave &shockwaveData)
{
if(shockwaveId == kInvalidShockwaveId)
{
shockwaveId = AllocateShockwave();
}
else
{
audAssertf(m_RegisteredShockwavesAllocation.IsSet(shockwaveId), "Updating a shockwave but we don't have one registered!");
}
if(shockwaveId != kInvalidShockwaveId && shockwaveData.intensity > 0.f)
{
m_RegisteredShockwaves[shockwaveId] = shockwaveData;
m_RegisteredShockwaves[shockwaveId].lastUpdateTime = fwTimer::GetTimeInMilliseconds();
}
else if(shockwaveId != kInvalidShockwaveId)
{
FreeShockwave(shockwaveId);
shockwaveId = kInvalidShockwaveId;
}
}
void naEnvironment::UpdateShockwaves()
{
f32 objectShockwaveVolumes[g_MaxInterestingLocalObjects];
sysMemSet(&objectShockwaveVolumes, 0, sizeof(objectShockwaveVolumes));
const f32 timeStep = fwTimer::GetTimeStep();
for(u32 shockwaveId = 0; shockwaveId < kMaxRegisteredShockwaves; shockwaveId++)
{
if(m_RegisteredShockwavesAllocation.IsSet(shockwaveId))
{
m_RegisteredShockwaves[shockwaveId].delay -= timeStep;
if(m_RegisteredShockwaves[shockwaveId].delay <= 0.f)
{
// update shockwave radius and intensity
m_RegisteredShockwaves[shockwaveId].radius += m_RegisteredShockwaves[shockwaveId].radiusChangeRate * timeStep;
m_RegisteredShockwaves[shockwaveId].intensity += m_RegisteredShockwaves[shockwaveId].intensityChangeRate * timeStep;
}
// compute per object shockwave volumes
for(s32 objId = g_MaxInterestingLocalObjects -1 ; objId >= 0; objId--)
{
if(m_InterestingLocalObjects[objId].object && GetInterestingLocalObjectMaterial(objId) && GetInterestingLocalObjectMaterial(objId)->ShockwaveSound != g_NullSoundHash)
{
Vec3V objectPos = m_InterestingLocalObjects[objId].object->GetTransform().GetPosition();
if(m_InterestingLocalObjects[objId].object->GetCurrentPhysicsInst())
{
objectPos = m_InterestingLocalObjects[objId].object->GetCurrentPhysicsInst()->GetCenterOfMass();
}
const f32 distToShockwaveCentre = (VEC3V_TO_VECTOR3(objectPos) - m_RegisteredShockwaves[shockwaveId].centre).Mag();
f32 distToShockwave = distToShockwaveCentre * m_RegisteredShockwaves[shockwaveId].distanceToRadiusFactor;
f32 linVol = 1.f - Min((distToShockwave/m_RegisteredShockwaves[shockwaveId].radius), 1.f);
linVol *= m_RegisteredShockwaves[shockwaveId].intensity;
if (!m_RegisteredShockwaves[shockwaveId].centered)
{
distToShockwave = Abs((distToShockwaveCentre - m_RegisteredShockwaves[shockwaveId].radius)) * m_RegisteredShockwaves[shockwaveId].distanceToRadiusFactor;
linVol = m_RegisteredShockwaves[shockwaveId].intensity * g_ShockwaveDistToVol.CalculateValue(distToShockwave);
}
const f32 attackTime = m_InterestingLocalObjects[objId].shockwaveSoundInfo.attackTime;
const f32 releaseTime = m_InterestingLocalObjects[objId].shockwaveSoundInfo.releaseTime;
if (attackTime >= 0.f && releaseTime >= 0.f)
{
objectShockwaveVolumes[objId] = Max(ProcessResoVolume(linVol, attackTime, releaseTime, m_InterestingLocalObjects[objId].shockwaveSoundInfo.prevVol), objectShockwaveVolumes[objId]);
m_InterestingLocalObjects[objId].shockwaveSoundInfo.prevVol = objectShockwaveVolumes[objId];
}
else
{
objectShockwaveVolumes[objId] = Max(linVol, objectShockwaveVolumes[objId]);
}
}
if(m_InterestingLocalObjects[objId].object && GetInterestingLocalObjectMaterial(objId) && GetInterestingLocalObjectMaterial(objId)->EntityResonance != g_NullSoundHash)
{
if (!m_RegisteredShockwaves[shockwaveId].centered)
{
Vec3V objectPos = m_InterestingLocalObjects[objId].object->GetTransform().GetPosition();
if(m_InterestingLocalObjects[objId].object->GetCurrentPhysicsInst())
{
objectPos = m_InterestingLocalObjects[objId].object->GetCurrentPhysicsInst()->GetCenterOfMass();
}
const f32 distToShockwaveCentre = (VEC3V_TO_VECTOR3(objectPos) - m_RegisteredShockwaves[shockwaveId].centre).Mag();
f32 distToShockwave = Abs((distToShockwaveCentre - m_RegisteredShockwaves[shockwaveId].radius)) * m_RegisteredShockwaves[shockwaveId].distanceToRadiusFactor;
f32 linVol = m_RegisteredShockwaves[shockwaveId].intensity * g_ShockwaveDistToVol.CalculateValue(distToShockwave);
const f32 attackTime = m_InterestingLocalObjects[objId].resoSoundInfo.attackTime;
const f32 releaseTime = m_InterestingLocalObjects[objId].resoSoundInfo.releaseTime;
m_InterestingLocalObjects[objId].resoSoundInfo.resonanceIntensity = ProcessResoVolume(linVol,attackTime,releaseTime,m_InterestingLocalObjects[objId].resoSoundInfo.resonanceIntensity);
}
}
}
// player shockwave
if(audNorthAudioEngine::ShouldTriggerPulseHeadset() && !m_RegisteredShockwaves[shockwaveId].centered)
{
CPed* pPlayer = CGameWorld::FindLocalPlayer();
if(pPlayer)
{
const f32 distToShockwaveCentre = (VEC3V_TO_VECTOR3(pPlayer->GetTransform().GetPosition()) - m_RegisteredShockwaves[shockwaveId].centre).Mag();
f32 distToShockwave = Abs((distToShockwaveCentre - m_RegisteredShockwaves[shockwaveId].radius)) * m_RegisteredShockwaves[shockwaveId].distanceToRadiusFactor;
f32 intensity = m_RegisteredShockwaves[shockwaveId].intensity * g_ShockwaveDistToVol.CalculateValue(distToShockwave);
if(distToShockwave <= 5.f)
{
audSoundInitParams initParams;
initParams.SetVariableValue(ATSTRINGHASH("Intensity", 0xEFCD993D),intensity);
g_FrontendAudioEntity.CreateAndPlaySound(g_FrontendAudioEntity.GetPulseHeadsetSounds().Find(ATSTRINGHASH("ExplosionShockwave", 0xAAB11A2A)),&initParams);
}
}
}
if((m_RegisteredShockwaves[shockwaveId].radiusChangeRate > 0.0f && m_RegisteredShockwaves[shockwaveId].radius > m_RegisteredShockwaves[shockwaveId].maxRadius) ||
(m_RegisteredShockwaves[shockwaveId].intensityChangeRate < 0.0f && m_RegisteredShockwaves[shockwaveId].intensity <= 0.1f))
{
m_RegisteredShockwavesAllocation.Clear(shockwaveId);
}
}
}
for(s32 objId = g_MaxInterestingLocalObjects -1 ; objId >= 0; objId--)
{
if(m_InterestingLocalObjects[objId].object && GetInterestingLocalObjectMaterial(objId)->ShockwaveSound != g_NullSoundHash)
{
Vec3V objectPos = m_InterestingLocalObjects[objId].object->GetTransform().GetPosition();
if(m_InterestingLocalObjects[objId].object->GetCurrentPhysicsInst())
{
objectPos = m_InterestingLocalObjects[objId].object->GetCurrentPhysicsInst()->GetCenterOfMass();
}
const f32 dbVol = audDriverUtil::ComputeDbVolumeFromLinear(objectShockwaveVolumes[objId]);
if(dbVol > g_SilenceVolume)
{
fwUniqueObjId effectUniqueID = fwIdKeyGenerator::Get(m_InterestingLocalObjects[objId].object, (s32)objId);
g_AmbientAudioEntity.RegisterEffectAudio(GetInterestingLocalObjectMaterial(objId)->ShockwaveSound, effectUniqueID, VEC3V_TO_VECTOR3(objectPos), m_InterestingLocalObjects[objId].object, dbVol);
}
}
}
}
void naEnvironment::UpdateResonance(const u32 objectIndex)
{
const ModelAudioCollisionSettings* material = GetInterestingLocalObjectMaterial(objectIndex);
if(material && material->EntityResonance != g_NullSoundHash)
{
const f32 attackTime = m_InterestingLocalObjects[objectIndex].resoSoundInfo.attackTime;
const f32 releaseTime = m_InterestingLocalObjects[objectIndex].resoSoundInfo.releaseTime;
m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceIntensity = ProcessResoVolume(0.f,attackTime,releaseTime,m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceIntensity);
f32 resoLinVol = m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceIntensity;
UpdateMichaelSpeacialAbilityForResoObjects(objectIndex,resoLinVol);
if(resoLinVol > 0.001f)
{
const bool resonanceLoopTracksListener = (AUD_GET_TRISTATE_VALUE(material->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_RESONANCELOOPTRACKSLISTENER) == AUD_TRISTATE_TRUE) BANK_ONLY(|| g_ForceResonanceTracksListener);
Vec3V objectPos = m_InterestingLocalObjects[objectIndex].object->GetTransform().GetPosition();
if(m_InterestingLocalObjects[objectIndex].object->GetCurrentPhysicsInst())
{
objectPos = m_InterestingLocalObjects[objectIndex].object->GetCurrentPhysicsInst()->GetCenterOfMass();
}
if(!m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceSound)
{
audSoundInitParams initParams;
initParams.EnvironmentGroup = m_InterestingLocalObjects[objectIndex].envGroup;
if (!resonanceLoopTracksListener)
{
initParams.Position = VEC3V_TO_VECTOR3(objectPos);
initParams.Tracker = m_InterestingLocalObjects[objectIndex].object->GetPlaceableTracker();
}
const f32 dbVol = audDriverUtil::ComputeDbVolumeFromLinear(resoLinVol);
initParams.Volume = dbVol;
g_AmbientAudioEntity.CreateAndPlaySound_Persistent(m_InterestingLocalObjects[objectIndex].resoSoundInfo.resoSoundRef, &m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceSound, &initParams);
}
if(m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceSound)
{
const f32 dbVol = audDriverUtil::ComputeDbVolumeFromLinear(resoLinVol);
m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceSound->SetRequestedVolume(dbVol);
if (resonanceLoopTracksListener)
{
spdAABB boundingBox = m_InterestingLocalObjects[objectIndex].object->GetBaseModelInfo()->GetBoundingBox();
const Mat34V objectMatrix = m_InterestingLocalObjects[objectIndex].object->GetMatrix();
const Vec3V listenerPos = g_AudioEngine.GetEnvironment().GetVolumeListenerPosition();
const Vec3V objectRelativePos = UnTransformOrtho(m_InterestingLocalObjects[objectIndex].object->GetMatrix(), listenerPos);
const Vec3V closestPosition = FindClosestPointOnAABB(boundingBox, objectRelativePos);
const Vec3V finalPosition = Transform(objectMatrix, closestPosition);
m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceSound->SetRequestedPosition(finalPosition);
}
}
}
else
{
if(m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceSound)
{
m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceSound->StopAndForget();
m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceSound = NULL;
}
}
#if __BANK
if(sm_DisplayResoIntensity && m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceSound )
{
Vec3V objectPos = m_InterestingLocalObjects[objectIndex].object->GetTransform().GetPosition();
if(m_InterestingLocalObjects[objectIndex].object->GetCurrentPhysicsInst())
{
objectPos = m_InterestingLocalObjects[objectIndex].object->GetCurrentPhysicsInst()->GetCenterOfMass();
}
char txt[64];
formatf(txt, "[Intensity : %f] [Vol: %f] ", m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceIntensity,m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceSound->GetRequestedVolume());
grcDebugDraw::Text(objectPos,Color_white,txt);
}
#endif
}
}
void naEnvironment::UpdateMichaelSpeacialAbilityForResoObjects(const u32 objectIndex,f32 &resoLinVol)
{
bool failed = true;
CPed *pPlayer = CGameWorld::FindLocalPlayer();
if( pPlayer && (g_FrontendAudioEntity.GetSpecialAbilityMode() == audFrontendAudioEntity::kSpecialAbilityModeMichael || g_FrontendAudioEntity.GetSpecialAbilityMode() == audFrontendAudioEntity::kSpecialAbilityModeMultiplayer))
{
const CPlayerInfo* playerInfo = pPlayer->GetPlayerInfo();
if(playerInfo)
{
const CEntity* target = playerInfo->GetTargeting().GetTarget();
if(target && (target == m_InterestingLocalObjects[objectIndex].object) )
{
Vec3V objectPos = target->GetTransform().GetPosition();
if(target->GetCurrentPhysicsInst())
{
objectPos = target->GetCurrentPhysicsInst()->GetCenterOfMass();
}
failed = false;
m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoVolLin = 1.f;
// Workout the intensity based on the angular distance to the camera front.
Vector3 front = camInterface::GetFront();
front.SetZ(0.f);
front.Normalize();
Vector3 cameraPos = camInterface::GetPos();
Vector3 dirToObject = VEC3V_TO_VECTOR3(objectPos) - cameraPos;
dirToObject.SetZ(0);
dirToObject.Normalize();
f32 horizontalDot = front.Dot(dirToObject);
m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoVolLin *= sm_HDotToSlowmoResoVol.CalculateValue(horizontalDot);
//naDisplayf("HDot %f V %f ",horizontalDot,slomoResoIntensity);
front = camInterface::GetUp();
front.SetY(0.f);
front.Normalize();
cameraPos = camInterface::GetPos();
dirToObject = VEC3V_TO_VECTOR3(objectPos) - cameraPos;
dirToObject.SetY(0);
dirToObject.Normalize();
f32 verticalDot = front.Dot(dirToObject);
verticalDot *= (verticalDot >=0.f ? -1.f : 1.f);
verticalDot += 1.f;
m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoVolLin *= sm_VDotToSlowmoResoVol.CalculateValue(verticalDot);
//naDisplayf("VDot %f V %f",verticalDot,slomoResoIntensity);
f32 frequency = sm_SpecialAbToFrequency.CalculateValue(pPlayer->GetSpecialAbility()->GetRemainingNormalized());//,sm_SlowMoLFOFrequencyMin,sm_SlowMoLFOFrequencyMax);
m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoPhase += audNorthAudioEngine::GetTimeStep() * (1.f/frequency);
m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoPhase = fmodf(m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoPhase,1);
m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoVolLin *= ((rage::Sin(ScalarV(2.f * PI * m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoPhase)).Getf() + 1 ) * 0.5f);
// Get the max
resoLinVol = Max(resoLinVol,m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoVolLin);
}
}
}
if(failed && m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoPhase != 0.f)
{
m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoPhase = 0.f;
m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceIntensity = Max(resoLinVol,m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoVolLin);
resoLinVol = m_InterestingLocalObjects[objectIndex].resoSoundInfo.resonanceIntensity;
m_InterestingLocalObjects[objectIndex].resoSoundInfo.slowmoResoVolLin = 0.f;
}
}
f32 naEnvironment::ProcessResoVolume(const f32 inputSignal, const f32 attack, const f32 release, const f32 state)
{
if(naVerifyf(attack * release > 0.f,"Neither attack or release times can't be 0, please check the Reso envelope sound"))
{
float prevEnvelope = state;
const f32 input = Abs(inputSignal);
if(naVerifyf(fwTimer::GetTimeStep() > 0.f,"Got a 0 time step, aborting reso volume computation."))
{
const f32 effectiveSampleRate = 1.f / fwTimer::GetTimeStep();
// prevent dbz
const f32 attackTime = Max(0.00001f, attack);
const f32 releaseTime = Max(0.00001f, release);
const f32 a = Powf(0.01f, 1.0f/(attackTime * effectiveSampleRate));
const f32 r = Powf(0.01f, 1.0f/(releaseTime * effectiveSampleRate));
// envelope >= input ? release : attack
const f32 factor = Selectf(input - prevEnvelope, a, r);
// don't allow output to go denormal; clamp to 0
const f32 unclampedVal = factor * (prevEnvelope - input) + input;
prevEnvelope = unclampedVal <= 1e-17f ? 0.f : unclampedVal;
return prevEnvelope;
}
}
return 0.f;
}
void naEnvironment::CalculateUnderCoverFactor()
{
f32 underCoverFactor = 0.f;
for (u32 i = 0; i < MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES; i++)
{
if(m_CoveredPolys.IsSet(i))
{
underCoverFactor += sqrt(m_SourceEnvironmentPolyAreas[i]/PI) * sm_RadiusScale;
}
}
f32 factor = 0.f;
CPed* pPlayer = CGameWorld::FindLocalPlayer();
if (pPlayer)
{
// This was added as a way to detect when in a vehicle and going undercover.
if (audNorthAudioEngine::GetGtaEnvironment()->IsPlayerInVehicle())
{
CVehicle* vehicle = pPlayer->GetVehiclePedInside();
if (vehicle && vehicle->GetAudioEnvironmentGroup())
{
if (((naEnvironmentGroup*)vehicle->GetAudioEnvironmentGroup())->IsUnderCover())
{
factor = underCoverFactor;
}
}
}
if (pPlayer->GetAudioEnvironmentGroup())
{
if (((naEnvironmentGroup*)pPlayer->GetAudioEnvironmentGroup())->IsUnderCover())
{
factor = underCoverFactor;
}
}
}
m_PlayerUnderCoverFactor = m_PlayerUnderCoverSmoother.CalculateValue(factor,audNorthAudioEngine::GetCurrentTimeInMs());
}
void naEnvironment::CreateRainObjectEnvironmentGroup(const u32 objectIndex)
{
BANK_ONLY(if(!sm_UseCoverAproximation))
{
Vec3V objectPos = m_InterestingLocalObjects[objectIndex].object->GetTransform().GetPosition();
if(m_InterestingLocalObjects[objectIndex].object->GetCurrentPhysicsInst())
{
objectPos = m_InterestingLocalObjects[objectIndex].object->GetCurrentPhysicsInst()->GetCenterOfMass();
}
if(!m_InterestingLocalObjects[objectIndex].envGroup)
{
m_InterestingLocalObjects[objectIndex].envGroup = naEnvironmentGroup::Allocate("InterestingObj");
if(m_InterestingLocalObjects[objectIndex].envGroup)
{
m_InterestingLocalObjects[objectIndex].envGroup->Init(NULL, 20.0f,sm_ObjOcclusionUpdateTime);
m_InterestingLocalObjects[objectIndex].envGroup->SetSource(objectPos);
m_InterestingLocalObjects[objectIndex].envGroup->ForceSourceEnvironmentUpdate(m_InterestingLocalObjects[objectIndex].object);
m_InterestingLocalObjects[objectIndex].envGroup->SetUsePortalOcclusion(true);
m_InterestingLocalObjects[objectIndex].envGroup->SetUseInteriorDistanceMetrics(true);
m_InterestingLocalObjects[objectIndex].envGroup->SetMaxPathDepth(2);
m_InterestingLocalObjects[objectIndex].envGroup->AddSoundReference();
}
}
}
#if __BANK
else
{
m_InterestingLocalObjects[objectIndex].underCover = false;
for (u32 i = 0; i < MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES; i++)
{
if(m_CoveredPolys.IsSet(i))
{
f32 distToObj = fabs((VEC3V_TO_VECTOR3(m_InterestingLocalObjects[objectIndex].object->GetTransform().GetPosition()) - m_SourceEnvironmentPolyCentroids[i]).Mag());
f32 approximatedRadius = sqrt(m_SourceEnvironmentPolyAreas[i]/PI) * sm_RadiusScale;
if(distToObj < approximatedRadius)
{
m_InterestingLocalObjects[objectIndex].underCover = true;
break;
}
}
}
}
#endif
}
void naEnvironment::UpdateRainObject(const u32 objectIndex)
{
if (g_weather.GetTimeCycleAdjustedRain() != 0 && m_InterestingLocalObjects[objectIndex].object && !audNorthAudioEngine::GetGtaEnvironment()->AreWeInAnInterior())
{
Vec3V objectPos = m_InterestingLocalObjects[objectIndex].object->GetTransform().GetPosition();
if (m_InterestingLocalObjects[objectIndex].object->GetCurrentPhysicsInst())
{
objectPos = m_InterestingLocalObjects[objectIndex].object->GetCurrentPhysicsInst()->GetCenterOfMass();
}
const ModelAudioCollisionSettings* material = GetInterestingLocalObjectMaterial(objectIndex);
const bool rainLoopTracksListener = (AUD_GET_TRISTATE_VALUE(material->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_RAINLOOPTRACKSLISTENER) == AUD_TRISTATE_TRUE) BANK_ONLY(|| g_ForceRainLoopTracksListener);
const bool placeRainLoopOnTopOfBounds = (AUD_GET_TRISTATE_VALUE(material->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_PLACERAINLOOPONTOPOFBOUNDS) == AUD_TRISTATE_TRUE) BANK_ONLY(|| g_ForceRainLoopPlayFromTopOfBounds);
if (rainLoopTracksListener)
{
spdAABB boundingBox = m_InterestingLocalObjects[objectIndex].object->GetBaseModelInfo()->GetBoundingBox();
if (placeRainLoopOnTopOfBounds)
{
Vec3V min = boundingBox.GetMin();
min.SetZ(boundingBox.GetMax().GetZ());
boundingBox.SetMin(min);
}
const Mat34V objectMatrix = m_InterestingLocalObjects[objectIndex].object->GetMatrix();
const Vec3V listenerPos = g_AudioEngine.GetEnvironment().GetVolumeListenerPosition();
const Vec3V objectRelativePos = UnTransformOrtho(m_InterestingLocalObjects[objectIndex].object->GetMatrix(), listenerPos);
const Vec3V closestPosition = FindClosestPointOnAABB(boundingBox, objectRelativePos);
const Vec3V finalPosition = Transform(objectMatrix, closestPosition);
objectPos = finalPosition;
}
else if (placeRainLoopOnTopOfBounds)
{
const spdAABB boundingBox = m_InterestingLocalObjects[objectIndex].object->GetBaseModelInfo()->GetBoundingBox();
const Mat34V objectMatrix = m_InterestingLocalObjects[objectIndex].object->GetMatrix();
const Vec3V finalPosition = Transform(objectMatrix, Vec3V(ScalarV(V_ZERO), ScalarV(V_ZERO), boundingBox.GetMax().GetZ()));
objectPos = finalPosition;
}
ScalarV sqdDistance = MagSquared(objectPos - g_AudioEngine.GetEnvironment().GetVolumeListenerPositionLastFrame());
if (sqdDistance.Getf() <= sm_SqdDistanceToPlayRainSounds)
{
if(material && material->RainLoop != g_NullSoundHash)
{
bool allowPlay = (m_NumRainPropSounds < sm_MaxNumRainProps);
if(allowPlay)
{
CreateRainObjectEnvironmentGroup(objectIndex);
if(m_InterestingLocalObjects[objectIndex].envGroup)
{
allowPlay = m_InterestingLocalObjects[objectIndex].envGroup->GetLastUpdateTime() != 0;
}
}
if( allowPlay && !m_InterestingLocalObjects[objectIndex].rainSound && !IsObjectUnderCover(objectIndex))
{
audSoundInitParams initParams;
initParams.AttackTime = 1000;
initParams.EnvironmentGroup = m_InterestingLocalObjects[objectIndex].envGroup;
initParams.Position = VEC3V_TO_VECTOR3(objectPos);
if (!rainLoopTracksListener && !placeRainLoopOnTopOfBounds)
{
initParams.Tracker = m_InterestingLocalObjects[objectIndex].object->GetPlaceableTracker();
}
// By default don't modulate the props rain Vol through code.
f32 vol = g_WeatherAudioEntity.GetRainVolume();
if(audNorthAudioEngine::GetGtaEnvironment()->AreWeInAnInterior())// || audNorthAudioEngine::GetGtaEnvironment()->IsCameraShelteredFromRain())
{
vol = -100.f;
}
f32 volLinear = audDriverUtil::ComputeLinearVolumeFromDb(vol);
f32 smoothedVolLinear = m_InterestingObjectRainSmoother.CalculateValue(volLinear, g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0));
f32 smoothedVolDb = audDriverUtil::ComputeDbVolumeFromLinear(smoothedVolLinear);
if(AUD_GET_TRISTATE_VALUE(material->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_TURNOFFRAINVOLONPROPS) != AUD_TRISTATE_TRUE)
{
initParams.Volume = smoothedVolDb;
initParams.Volume += sm_RainOnPropsVolumeOffset;
initParams.Predelay = sm_RainOnPopsPreDealy;
}
m_NumRainPropSounds++;
g_WeatherAudioEntity.CreateAndPlaySound_Persistent(material->RainLoop, &m_InterestingLocalObjects[objectIndex].rainSound, &initParams);
}
else if(IsObjectUnderCover(objectIndex))
{
if( m_InterestingLocalObjects[objectIndex].rainSound)
{
if (m_NumRainPropSounds > 0)
{
m_NumRainPropSounds--;
}
m_InterestingLocalObjects[objectIndex].rainSound->StopAndForget();
m_InterestingLocalObjects[objectIndex].rainSound = NULL;
}
}
if( m_InterestingLocalObjects[objectIndex].rainSound)
{
// By default don't modulate the props rain Vol through code.
f32 vol = g_WeatherAudioEntity.GetRainVolume();
if(audNorthAudioEngine::GetGtaEnvironment()->AreWeInAnInterior())
{
vol = -100.f;
}
f32 volLinear = audDriverUtil::ComputeLinearVolumeFromDb(vol);
f32 smoothedVolLinear = m_InterestingObjectRainSmoother.CalculateValue(volLinear, g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0));
f32 smoothedVolDb = audDriverUtil::ComputeDbVolumeFromLinear(smoothedVolLinear);
if(AUD_GET_TRISTATE_VALUE(material->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_TURNOFFRAINVOLONPROPS) != AUD_TRISTATE_TRUE)
{
m_InterestingLocalObjects[objectIndex].rainSound->SetRequestedVolume(smoothedVolDb +sm_RainOnPropsVolumeOffset);
}
if (rainLoopTracksListener || placeRainLoopOnTopOfBounds)
{
m_InterestingLocalObjects[objectIndex].rainSound->SetRequestedPosition(objectPos);
}
}
}
}
else
{
if(m_InterestingLocalObjects[objectIndex].rainSound)
{
if (m_NumRainPropSounds > 0)
{
m_NumRainPropSounds--;
}
m_InterestingLocalObjects[objectIndex].rainSound->StopAndForget();
m_InterestingLocalObjects[objectIndex].rainSound = NULL;
}
if( m_InterestingLocalObjects[objectIndex].envGroup )
{
m_InterestingLocalObjects[objectIndex].envGroup->RemoveSoundReference();
m_InterestingLocalObjects[objectIndex].envGroup = NULL;
}
}
}
else
{
if(m_InterestingLocalObjects[objectIndex].rainSound)
{
if (m_NumRainPropSounds > 0)
{
m_NumRainPropSounds--;
}
m_InterestingLocalObjects[objectIndex].rainSound->StopAndForget();
m_InterestingLocalObjects[objectIndex].rainSound = NULL;
}
if( m_InterestingLocalObjects[objectIndex].envGroup )
{
m_InterestingLocalObjects[objectIndex].envGroup->RemoveSoundReference();
m_InterestingLocalObjects[objectIndex].envGroup = NULL;
}
}
}
void naEnvironment::UpdateLocalEnvironment()
{
// JB: I moved this stuff into a separate function so it can be shared whatever async/sync approach we use above.
UpdateLocalEnvironmentObjects();
}
void naEnvironment::LocalObjectsList_StartAsyncUpdate()
{
if(g_EnableLocalEnvironmentMetrics && !(fwTimer::GetSystemFrameCount() & 15))
{
PF_PUSH_TIMEBAR("LocalObjectsList_StartAsyncUpdate");
spdSphere searchSphere(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition(), ScalarV(30.f));
fwIsSphereIntersecting searchVol(searchSphere);
// start spu search
ms_localObjectSearch.Start(
fwWorldRepMulti::GetSceneGraphRoot(), // root scene graph node
&searchVol, // search volume
ENTITY_TYPE_MASK_OBJECT | ENTITY_TYPE_MASK_BUILDING, // entity types
SEARCH_LOCATION_EXTERIORS | SEARCH_LOCATION_INTERIORS, // location flags
SEARCH_LODTYPE_HIGHDETAIL, // lod flags
SEARCH_OPTION_SKIP_PROCEDURAL_ENTITIES, // option flags
WORLDREP_SEARCHMODULE_AUDIO, // module
sysDependency::kPriorityCritical // priority
);
ms_localObjectSearchLaunched = true;
PF_POP_TIMEBAR();
}
}
void naEnvironment::LocalObjectsList_EndAsyncUpdate()
{
if(ms_localObjectSearchLaunched)
{
PF_PUSH_TIMEBAR("LocalObjectsList_EndAsyncUpdate");
ms_localObjectSearch.Finalize();
for (u32 i = 0 ; i < m_NumLocalTrees; i ++)
{
m_LocalTrees[i].tree = NULL;
m_LocalTrees[i].sector = AUD_SECTOR_FL;
}
m_NumLocalTrees = 0;
for (u32 i = 0 ; i < MAX_FB_SECTORS; i ++)
{
m_BackGroundTrees[i].numBushes = 0;
m_BackGroundTrees[i].numTrees = 0;
m_BackGroundTrees[i].numBigTrees = 0;
m_BackGroundTrees[i].bushesFitness = 0.f;
m_BackGroundTrees[i].treesFitness = 0.f;
m_BackGroundTrees[i].bigTreesFitness = 0.f;
}
// Before recalculating the new objects, all the current ones are marked up to delete.
for (u32 i = 0; i < g_MaxInterestingLocalObjects; i ++ )
{
m_InterestingLocalObjects[i].stillValid = false;
if( !m_InterestingLocalObjects[i].object )
{
m_InterestingLocalObjects[i].macsComponent = -1;
if( m_InterestingLocalObjects[i].envGroup )
{
m_InterestingLocalObjects[i].envGroup->RemoveSoundReference();
m_InterestingLocalObjects[i].envGroup = NULL;
}
BANK_ONLY(m_InterestingLocalObjects[i].underCover = true);
if(m_InterestingLocalObjects[i].rainSound)
{
m_InterestingLocalObjects[i].rainSound->StopAndForget();
m_InterestingLocalObjects[i].rainSound = NULL;
if (m_NumRainPropSounds > 0)
{
m_NumRainPropSounds--;
}
}
if (m_InterestingLocalObjects[i].randomAmbientSound)
{
m_InterestingLocalObjects[i].randomAmbientSound->StopAndForget();
m_InterestingLocalObjects[i].randomAmbientSound = NULL;
}
if(m_InterestingLocalObjects[i].resoSoundInfo.resonanceSound)
{
m_InterestingLocalObjects[i].resoSoundInfo.resonanceSound->StopAndForget();
m_InterestingLocalObjects[i].resoSoundInfo.resonanceSound = NULL;
}
m_InterestingLocalObjects[i].resoSoundInfo.resonanceIntensity = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.attackTime = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.releaseTime = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.slowmoResoPhase = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.slowmoResoVolLin = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.resoSoundRef = g_NullSoundRef;
}
else
{
if (m_InterestingLocalObjects[i].randomAmbientSound && IsObjectDestroyed(m_InterestingLocalObjects[i].object))
{
m_InterestingLocalObjects[i].randomAmbientSound->StopAndForget();
m_InterestingLocalObjects[i].randomAmbientSound = NULL;
}
}
}
for(u32 i = 0; i < m_NumInterestingLocalObjectsCached; i++)
{
m_InterestingLocalObjectsCached[i].object = NULL;
m_InterestingLocalObjectsCached[i].envGroup = NULL;
m_InterestingLocalObjectsCached[i].rainSound = NULL;
m_InterestingLocalObjectsCached[i].windSound = NULL;
m_InterestingLocalObjectsCached[i].randomAmbientSound = NULL;
m_InterestingLocalObjectsCached[i].macsComponent = -1;
BANK_ONLY(m_InterestingLocalObjectsCached[i].underCover = true);
m_InterestingLocalObjectsCached[i].resoSoundInfo.resonanceSound = NULL;
m_InterestingLocalObjectsCached[i].resoSoundInfo.resonanceIntensity = 0.f;
m_InterestingLocalObjectsCached[i].resoSoundInfo.attackTime = 0.f;
m_InterestingLocalObjectsCached[i].resoSoundInfo.releaseTime = 0.f;
m_InterestingLocalObjectsCached[i].resoSoundInfo.slowmoResoVolLin = 0.f;
m_InterestingLocalObjectsCached[i].resoSoundInfo.slowmoResoPhase = 0.f;
m_InterestingLocalObjectsCached[i].resoSoundInfo.resoSoundRef = g_NullSoundRef;
}
m_NumInterestingLocalObjectsCached = 0;
ms_localObjectSearch.ExecuteCallbackOnResult(FindCloseObjectsCB, NULL);
ms_localObjectSearchLaunched = false;
// Go through the cached objects and remove the occlusion group for those that aren't valid anymore.
for(u32 i = 0; i < m_NumInterestingLocalObjectsCached; i++)
{
bool alreadyAdded = false;
s32 freeIdx = -1;
for (u32 j = 0; j < g_MaxInterestingLocalObjects; j++)
{
if(m_InterestingLocalObjectsCached[i].object == m_InterestingLocalObjects[j].object)
{
m_InterestingLocalObjects[j].stillValid = true;
alreadyAdded = true;
break;
}
else if (!m_InterestingLocalObjects[j].object)
{
freeIdx = j;
}
}
if(!alreadyAdded && freeIdx != -1 && freeIdx < g_MaxInterestingLocalObjects)
{
m_InterestingLocalObjects[freeIdx] = m_InterestingLocalObjectsCached[i];
m_InterestingLocalObjects[freeIdx].stillValid = true;
}
}
// Free up the objects that aren't valid.
for (u32 i = 0; i < g_MaxInterestingLocalObjects; i++)
{
if(!m_InterestingLocalObjects[i].stillValid)
{
m_InterestingLocalObjects[i].object = NULL;
m_InterestingLocalObjects[i].macsComponent = -1;
if( m_InterestingLocalObjects[i].envGroup )
{
m_InterestingLocalObjects[i].envGroup->RemoveSoundReference();
m_InterestingLocalObjects[i].envGroup = NULL;
}
BANK_ONLY(m_InterestingLocalObjects[i].underCover = true);
if(m_InterestingLocalObjects[i].rainSound)
{
m_InterestingLocalObjects[i].rainSound->StopAndForget();
m_InterestingLocalObjects[i].rainSound = NULL;
if (m_NumRainPropSounds > 0)
{
m_NumRainPropSounds--;
}
}
if(m_InterestingLocalObjects[i].resoSoundInfo.resonanceSound)
{
m_InterestingLocalObjects[i].resoSoundInfo.resonanceSound->StopAndForget();
m_InterestingLocalObjects[i].resoSoundInfo.resonanceSound = NULL;
}
m_InterestingLocalObjects[i].resoSoundInfo.resonanceIntensity = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.attackTime = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.releaseTime = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.slowmoResoPhase = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.slowmoResoVolLin = 0.f;
m_InterestingLocalObjects[i].resoSoundInfo.resoSoundRef = g_NullSoundRef;
}
}
PF_POP_TIMEBAR();
}
}
void naEnvironment::UpdateLocalEnvironmentObjects()
{
if(g_EnableLocalEnvironmentMetrics)
{
//Offset the resonance objects processing from the interesting objects
if(!((fwTimer::GetSystemFrameCount() + 7) & 15))
{
for(u32 i=0; i< g_MaxResonatingObjects; i++)
{
//If the sound is no longer playing or the entity has been deleted, clear the entry out
if(!m_ResonatingObjects[i].entity && !m_ResonatingObjects[i].resoSnd)
{
m_ResonatingObjects[i].Clear();
}
}
}
// update city ambiance metrics
if(!(fwTimer::GetSystemFrameCount() & 31) || m_ForceAmbientMetricsUpdate)
{
const Vector3 north = Vector3(0.0f, 1.0f, 0.0f);
const Vector3 east = Vector3(1.0f,0.0f,0.0f);
const Matrix34 listenerMatrix = MAT34V_TO_MATRIX34(g_AudioEngine.GetEnvironment().GetPanningListenerMatrix());
for(u32 i = 0 ; i < 4; i++)
{
m_VehicleSegmentCounts[i] = 0.f;
m_HostilePedSegmentCount[i] = 0.f;
m_ExteriorPedSegmentCounts[i] = 0.f;
m_InteriorPedSegmentCounts[i] = 0.f;
m_InteriorOccludedPedSegmentCounts[i] = 0.0f;
m_ExteriorPedPanicCount[i] = 0.0f;
m_InteriorPedPanicCount[i] = 0.0f;
}
u32 segment = ~0U;
m_VehicleSegmentCounts[4] = 0.f;
CVehicle::Pool* vehiclePool = CVehicle::GetPool();
for(s32 i = 0; i < vehiclePool->GetSize(); i++)
{
CVehicle *veh = vehiclePool->GetSlot(i);
if(veh)
{
f32 vehicleAmount = 1.0f;
const Vector3 pos = VEC3V_TO_VECTOR3(veh->GetTransform().GetPosition());
Vector3 vehToListener = pos - listenerMatrix.d;
vehicleAmount *= m_VehicleDistanceSqToVehicleAmount.CalculateValue(vehToListener.Mag2());
vehicleAmount *= m_VehicleSpeedToVehicleAmount.CalculateValue(veh->GetVehicleAudioEntity()->GetFwdSpeedLastFrame());
if(vehToListener.Mag2() < (100.f*100.f))
{
m_VehicleSegmentCounts[4]+=vehicleAmount;
}
else
{
vehToListener.Normalize();
const f32 dp = DotProduct(vehToListener, north);
const f32 angle = AcosfSafe(dp);
const f32 degrees = RtoD*angle;
f32 actualDegrees;
if(vehToListener.Dot(east) <= 0.0f)
{
actualDegrees = 360.0f - degrees;
}
else
{
actualDegrees = degrees;
}
if(segment != ~0U)
{
m_VehicleSegmentCounts[segment]+=vehicleAmount;
}
// defer using segment to prevent LHS
segment = static_cast<u32>(((actualDegrees+45.f) / 90.0f)) % 4;
}
}
}
if(segment != ~0U)
{
m_VehicleSegmentCounts[segment]+=1.f;
}
fwPool<CPed> *pedPool = CPed::GetPool();
f32 totalPedZPos = 0.0f;
u32 totalNumPeds = 0;
u32 totalMalePeds = 0;
CInteriorProxy* pIntProxy = CPortalVisTracker::GetPrimaryInteriorProxy();
s32 roomIdx = CPortalVisTracker::GetPrimaryRoomIdx();
spdAABB roomBoundingBox;
roomBoundingBox.Invalidate();
if (pIntProxy)
{
CInteriorInst* pIntInst = pIntProxy->GetInteriorInst();
if (pIntInst && roomIdx>0 && roomIdx!=INTLOC_INVALID_INDEX)
{
if(roomIdx < pIntInst->GetNumRooms())
{
pIntInst->GetRoomBoundingBox(roomIdx, roomBoundingBox);
}
}
}
for(s32 i = 0; i < pedPool->GetSize(); i++)
{
CPed *ped = pedPool->GetSlot(i);
// Don't count law enforcement peds, otherwise we'll start getting eg. loads of walla during 5* wanted level shootouts
// Also don't count animals, for obvious reasons
if(ped && !ped->IsLocalPlayer() && ped->GetHealth() > 0.0f && ped->GetPedModelInfo()->GetPersonalitySettings().GetIsHuman())
{
const Vector3 pos = VEC3V_TO_VECTOR3(ped->GetTransform().GetPosition());
Vector3 pedToListener = pos - listenerMatrix.d;
// Bias against from peds at different heights to the listener when in an interior
if(ped->GetIsInInterior())
{
pedToListener.SetZ(pedToListener.GetZ() * 4.0f);
}
if (pedToListener.Mag2() < (60.f*60.f)) // only include peds that are reasonably close
{
Vector3 normalisedPedToListener = pedToListener;
normalisedPedToListener.Normalize();
const f32 dp = DotProduct(normalisedPedToListener, north);
const f32 angle = AcosfSafe(dp);
const f32 degrees = RtoD*angle;
f32 actualDegrees;
if(normalisedPedToListener.Dot(east) <= 0.0f)
{
actualDegrees = 360.0f - degrees;
}
else
{
actualDegrees = degrees;
}
const u32 segment = static_cast<u32>(((actualDegrees+45.f) / 90.0f)) % 4;
if(ped->GetPedIntelligence()->GetQueriableInterface()->GetHostileTarget() != NULL)
{
m_HostilePedSegmentCount[segment]+=1.f;
}
else if(ped->GetIsInInterior())
{
if(ped->GetAudioEntity()->GetEnvironmentGroup())
{
f32 occludedPedFactor = 1.0f - (Clamp(ped->GetAudioEntity()->GetEnvironmentGroup()->GetOcclusionValue()/0.5f, 0.0f, 1.0f));
// Fade off the ped contribution as we approach the edge of the range
occludedPedFactor *= (1.0f - Clamp((pedToListener.Mag() - 30.0f)/30.0f, 0.0f, 1.0f));
m_InteriorOccludedPedSegmentCounts[segment] += occludedPedFactor;
m_InteriorPedSegmentCounts[segment]+=1.f;
}
if(ped->GetIsPanicking())
{
m_InteriorPedPanicCount[segment]+=1.f;
}
}
else if(ped->GetIsInExterior())
{
m_ExteriorPedSegmentCounts[segment]+=1.f;
if(ped->GetIsPanicking())
{
m_ExteriorPedPanicCount[segment]+=1.f;
}
}
if(ped->IsMale())
{
totalMalePeds++;
}
totalNumPeds++;
totalPedZPos += pos.z;
}
}
}
if(totalNumPeds > 0)
{
m_AveragePedZPos = totalPedZPos/totalNumPeds;
m_PedFractionMale = totalMalePeds/(f32)totalNumPeds;
}
m_ForceAmbientMetricsUpdate = false;
}
}
}
void naEnvironment::UpdateLinkedRoomList(CInteriorInst* intInst)
{
ClearLinkedRooms();
if (intInst)
{
CMloModelInfo *modelInfo = intInst->GetMloModelInfo();
s32 roomPortalIdx = intInst->GetNumPortalsInRoom(INTLOC_ROOMINDEX_LIMBO) - 1;
while(roomPortalIdx >= 0)
{
// Need the global portal index in order to get the portal data
s32 globalPortalIdx = intInst->GetPortalIdxInRoom(INTLOC_ROOMINDEX_LIMBO, roomPortalIdx);
CPortalFlags portalFlags = modelInfo->GetPortalFlags(globalPortalIdx);
if(portalFlags.GetIsLinkPortal())
{
// If this is a link portal, update our list of rooms to not occlude from
// this is a valid linked portal to another MLO - update tracker accordingly
// Need to use the room specific portal index in this function
CPortalInst* portalInst = intInst->GetMatchingPortalInst(INTLOC_ROOMINDEX_LIMBO, roomPortalIdx);
if(portalInst)
{
// update using info from the linked portal
CInteriorInst* destIntInst = NULL;
s32 destPortalIdx = -1;
// get data about linked interior through this portal
portalInst->GetDestinationData(intInst, destIntInst, destPortalIdx);
if(destIntInst)
{
// need to get the room index from the new interior and portal
s32 linkedRoomIdx = destIntInst->GetDestThruPortalInRoom(INTLOC_ROOMINDEX_LIMBO, destPortalIdx); //entry/exit portal so we know it's room INTLOC_ROOMINDEX_LIMBO
if(linkedRoomIdx != INTLOC_INVALID_INDEX)
{
AddLinkedRoom(destIntInst, linkedRoomIdx);
}
}
}
}
roomPortalIdx--;
}
}
}
void naEnvironment::AddLinkedRoom(CInteriorInst* intInst, s32 roomIdx)
{
if(intInst)
{
for (u32 i=0; i<AUD_MAX_LINKED_ROOMS; i++)
{
if(CInteriorProxy::GetFromLocation(m_LinkedRoom[i]) == intInst->GetProxy() && m_LinkedRoom[i].GetRoomIndex() == roomIdx)
{
// already stored this frame
return;
}
else if (!m_LinkedRoom[i].IsValid())
{
m_LinkedRoom[i] = CInteriorInst::CreateLocation(intInst, roomIdx);
break;
}
}
}
}
void naEnvironment::ClearLinkedRooms()
{
for (u32 i=0; i<AUD_MAX_LINKED_ROOMS; i++)
{
m_LinkedRoom[i].MakeInvalid();
}
}
bool naEnvironment::IsRoomInLinkedRoomList(const InteriorSettings* linkedInteriorSettings, const InteriorRoom* linkedIntRoom)
{
if(naVerifyf(linkedInteriorSettings && linkedIntRoom, "Querying invalid room in audio check"))
{
for (u32 i=0; i<AUD_MAX_LINKED_ROOMS; i++)
{
if(m_LinkedRoom[i].IsValid())
{
const InteriorSettings* intSettings = NULL;
const InteriorRoom* intRoom = NULL;
GetInteriorSettingsForInteriorLocation(m_LinkedRoom[i], intSettings, intRoom);
if(intSettings == linkedInteriorSettings && intRoom == linkedIntRoom)
{
return true;
}
}
}
}
return false;
}
void naEnvironment::SpikeResonanceObjects(const f32 impulse, const Vector3 &pos, const f32 maxDist,const CEntity* entity)
{
for(u32 i = 0; i < g_MaxResonatingObjects ; i++)
{
if(m_InterestingLocalObjects[i].object && m_InterestingLocalObjects[i].resoSoundInfo.resoSoundRef != g_NullSoundRef)
{
Vec3V objectPos = m_InterestingLocalObjects[i].object->GetTransform().GetPosition();
if(m_InterestingLocalObjects[i].object->GetCurrentPhysicsInst())
{
objectPos = m_InterestingLocalObjects[i].object->GetCurrentPhysicsInst()->GetCenterOfMass();
}
if(entity && entity != m_InterestingLocalObjects[i].object)
{
// if we have an entity is because we only want to spike that entity alone.
continue;
}
const f32 dist = (VEC3V_TO_VECTOR3(objectPos) - pos).Mag();
// Spike the resonanceIntensity based on the distance
f32 intensitySpike = 1.f - Min((dist <= sm_DistancePlateau ? 0.f : dist)/maxDist, 1.f);
intensitySpike *= impulse;
intensitySpike = Min(intensitySpike, 1.f);
const f32 attackTime = m_InterestingLocalObjects[i].resoSoundInfo.attackTime;
const f32 releaseTime = m_InterestingLocalObjects[i].resoSoundInfo.releaseTime;
m_InterestingLocalObjects[i].resoSoundInfo.resonanceIntensity = ProcessResoVolume(intensitySpike,attackTime,releaseTime,m_InterestingLocalObjects[i].resoSoundInfo.resonanceIntensity);
}
}
}
f32 naEnvironment::GetVectorToNorthXYAngle(const Vector3& v)
{
Vector3 XYVecNormalized = v;
XYVecNormalized.z = 0.0f;
XYVecNormalized.NormalizeFast();
f32 cosAngle = XYVecNormalized.Dot(g_Directions[AUD_AMB_DIR_NORTH]);
cosAngle = Max(Min(cosAngle, 1.0f), -1.0f);
f32 angle = AcosfSafe(cosAngle);
angle *= (360.0f*0.5f/PI); // Switch from radians to degrees
// But it might be 360-that if we're in the left side - do a cos with right to see
f32 right = XYVecNormalized.Dot(g_Directions[AUD_AMB_DIR_EAST]);
if (right<0.0f)
{
angle = 359.9f - angle;
}
return angle;
}
f32 naEnvironment::GetVectorToUpAngle(const Vector3& v)
{
const Vector3 up = Vector3(0.0f, 0.0f, 1.0f);
Vector3 vNormalized = v;
vNormalized.NormalizeFast();
f32 cosUp = vNormalized.Dot(up);
cosUp = Max(Min(cosUp, 1.0f), -1.0f);
f32 angleUp = AcosfSafe(cosUp);
angleUp *= (360.0f*0.5f/PI); // convert radians to degrees
return angleUp;
}
void naEnvironment::UpdateEchoPositions()
{
// @OCC TODO - Determine best echo metrics calculation
/*
// Update the suitability of each direction to having echoes from it
// Centered around each direction, look at lower and upper, and each side, with half importance.
Vector3 centrePosition = g_AudioEngine.GetEnvironment().GetPanningListenerPosition();
for (s32 i=0; i<8; i++)
{
f32 echoGoodnessFactor = 0.0f;
f32 bestDistance = 0.0f;
for (s32 j=-1; j<2; j++)
{
u32 index = (i+j+8) % 8;
f32 lowerContribution = m_DistanceToEchoCurve.CalculateValue(m_LevelWallDistances[index*3]);
f32 upperContribution = m_DistanceToEchoCurve.CalculateValue(m_UpperWallDistances[index]);
if (j!=0)
{
lowerContribution *= 0.33f;
upperContribution *= 0.33f;
}
else
{
// Average our normals
Vector3 lowerNormal = m_LevelReflectionNormal[i];
lowerNormal.z = 0.0f;
lowerNormal.NormalizeSafe();
Vector3 upperNormal = m_UpperReflectionNormal[i];
upperNormal.z = 0.0f;
upperNormal.NormalizeSafe();
if ((lowerContribution+upperContribution)>0.01f)
{
upperNormal.Scale(upperContribution/(lowerContribution+upperContribution));
lowerNormal.Scale(lowerContribution/(lowerContribution+upperContribution));
m_EchoDirection[i] = lowerNormal + upperNormal;
}
else
{
m_EchoDirection[i].Set(1.0f);
m_EchoDirection[i].Normalize();
}
}
echoGoodnessFactor += (lowerContribution + upperContribution);
bestDistance += (lowerContribution * m_LevelWallDistances[index]);
bestDistance += (upperContribution * m_UpperWallDistances[index]);
}
// That's up-and-down, 1/3 from each side, so a total of (1+2/3)*2 = 3.33 zero-to-ones
echoGoodnessFactor /= 3.33f;
m_EchoSuitability[i] = echoGoodnessFactor;
bestDistance /= 3.33f;
// But we also need to scale the distance up by the total echo goodness, to normalise it :-/
bestDistance = Selectf(echoGoodnessFactor*-1.0f, 25.0f, bestDistance/echoGoodnessFactor);
if(bestDistance < 0)
{
bestDistance = 25.0;
m_EchoSuitability[i] = 0.0f;
}
m_EchoDistance[i] = bestDistance;
Vector3 offset = m_RelativeScanPositions[i*3];
offset.Scale(bestDistance);
m_EchoPosition[i] = centrePosition + offset;
}
*/
// Update the suitability of each direction to having echoes from it
// Centered around each direction, look at lower and upper, and each side, with half importance.
Vector3 centrePosition = VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetPanningListenerPosition());
for (u8 i=0; i<8; i++)
{
f32 interpolatedDistance = 0.0f;
Vector3 vEchoDirection(0.0f, 0.0f, 0.0f);
// We use 4 probes per N, NE, E, ...direction. 1 on 15 degrees to either side of the main level radial, and the upper probe on the main radial.
// So for N, we use the NNW, N, NNE level probes, and the N upper probe
s32 levelLeftProbeIdx = (i*3 + -1 + 24) % 24;
s32 levelCenterProbeIdx = (i*3 + 24) % 24;
s32 levelRightProbeIdx = (i*3 + 1 + 24) % 24;
s32 upperProbeIdx = i;
Vector3 vProbeNormals[4];
vProbeNormals[0] = m_LevelReflectionNormal[levelLeftProbeIdx];
vProbeNormals[1] = m_LevelReflectionNormal[levelCenterProbeIdx];
vProbeNormals[2] = m_LevelReflectionNormal[levelRightProbeIdx];
vProbeNormals[3] = m_UpperReflectionNormal[upperProbeIdx];
f32 probeDistances[4];
probeDistances[0] = audNorthAudioEngine::GetOcclusionManager()->GetWallDistance(AUD_OCC_PROBE_ELEV_W, levelLeftProbeIdx);
probeDistances[1] = audNorthAudioEngine::GetOcclusionManager()->GetWallDistance(AUD_OCC_PROBE_ELEV_W, levelCenterProbeIdx);
probeDistances[2] = audNorthAudioEngine::GetOcclusionManager()->GetWallDistance(AUD_OCC_PROBE_ELEV_W, levelRightProbeIdx);
probeDistances[3] = audNorthAudioEngine::GetOcclusionManager()->GetWallDistance(AUD_OCC_PROBE_ELEV_WWN, upperProbeIdx);
f32 totalWeight = 0.0f;
f32 probeWeights[4];
for(s32 probe = 0; probe < 4; probe++)
{
probeWeights[probe] = m_DistanceToEchoCurve.CalculateValue(probeDistances[probe]);
totalWeight += probeWeights[probe];
}
// Interpolate based on weights to get the rest of the data (distance and normal)
if(totalWeight > 0.0f)
{
for(s32 probe = 0; probe < 4; probe++)
{
// Should we factor in the z component?
f32 probeWeight = probeWeights[probe] / totalWeight;
interpolatedDistance += probeDistances[probe] * probeWeight;
if(probeWeight > 0.0f)
{
vProbeNormals[probe].Scale(probeWeight);
vEchoDirection += vProbeNormals[probe];
}
}
}
else
{
vEchoDirection = audNorthAudioEngine::GetOcclusionManager()->GetRelativeScanPosition(i*3);
vEchoDirection.Scale(-1.0f);
interpolatedDistance = (probeDistances[0] + probeDistances[1] + probeDistances[2] + probeDistances[3]) * 0.25f;
}
Vector3 offset = audNorthAudioEngine::GetOcclusionManager()->GetRelativeScanPosition(i*3);
offset.Scale(interpolatedDistance);
m_EchoPosition[i] = centrePosition + offset;
// Base the suitability on all the probes, so if only 1 is suitable it's not that suitable a location overall
m_EchoSuitability[i] = totalWeight * 0.25f;
m_EchoDistance[i] = interpolatedDistance;
m_EchoDirection[i] = vEchoDirection;
audWeaponAudioEntity::SetReportGoodness(i, m_EchoSuitability[i]);
}
#if __BANK
DrawWeaponEchoes();
#endif
}
void naEnvironment::GetEchoSuitablilityForSourcePosition(const Vector3& sourcePosition, f32* suitability)
{
if (!suitability)
{
naErrorf("Null ptr suitability passed into GetEchoSuitabilityForSourcePosition");
return;
}
Vector3 listenerPosition = VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition());
// Work out angle of incidence stuff, and multiply those factors by the standard suitability factors.
for (s32 i=0; i<8; i++)
{
Vector3 echoPoint = m_EchoPosition[i];
Vector3 echoPointNormal = m_EchoDirection[i];
Vector3 echoToListener = listenerPosition - echoPoint;
Vector3 echoToSource = sourcePosition - echoPoint;
// Flatten it - we do all this in 2d
echoToListener.z = 0.0f;
echoToListener.NormalizeSafe();
echoToSource.z = 0.0f;
echoToSource.NormalizeSafe();
// Reflect listener direction in echo normal
// Reflect listener direction in echo normal
Quaternion listenerToNormalRotation;
bool success = listenerToNormalRotation.FromVectors(echoToListener, echoPointNormal);
if (success)
{
Vector3 listenerReflection; // I think this is wrong in rage
listenerToNormalRotation.Transform(echoPointNormal, listenerReflection);
// Now see how similar that direction is to the source direction
f32 sourceAndReflectedListenerDotProduct = echoToSource.Dot(listenerReflection);
//rk ported the next 2 lines from RDR2 11/12/12
if(sourceAndReflectedListenerDotProduct < 0)
sourceAndReflectedListenerDotProduct *= -1;
// Map the dot product to a suitability scaling factor
f32 angleSuitability = m_AngleOfIncidenceToSuitabilityCurve.CalculateValue(sourceAndReflectedListenerDotProduct);
suitability[i] = m_EchoSuitability[i] * angleSuitability;
}
else
{
suitability[i] = 0.0f;
}
}
}
s32 naEnvironment::GetBestEchoForSourcePosition(const Vector3& sourcePosition, s32* oppositeEcho)
{
if (!g_EnableLocalEnvironmentMetrics)
{
if (oppositeEcho)
{
*oppositeEcho = -1;
}
return -1;
}
f32 echoSuitablility[8];
GetEchoSuitablilityForSourcePosition(sourcePosition, &echoSuitablility[0]);
// Pick the best one
s32 bestEcho = -1;
f32 bestSuitability = 0.0f;
s32 bestOppositeEcho = -1;
for (s32 i=0; i<8; i++)
{
if (echoSuitablility[i] >= bestSuitability)
{
bestSuitability = echoSuitablility[i];
bestEcho = i;
}
}
if (bestEcho>-1)
{
// Once we've found our best echo, look for the best on the 'other side' and play one from there too.
f32 bestOppositeSuitability = 0.0f;
for (s32 i=3; i<6; i++)
{
u32 echoIndex = (bestEcho + i) % 8;
if (echoSuitablility[echoIndex] >= bestOppositeSuitability)
{
bestOppositeSuitability = echoSuitablility[echoIndex];
bestOppositeEcho = echoIndex;
}
}
}
if (oppositeEcho)
{
*oppositeEcho = bestOppositeEcho;
}
return bestEcho;
}
void naEnvironment::CalculateEchoDelayAndAttenuation(u32 echoIndex, const Vector3 &sourcePosition, u32* returnPredelay, f32* returnExtraAttenuation, Vector3* projectedPosition, u32 depth)
{
f32 LocalEchoSuitability[8];
f32 LocalEchoDistance[8];
Vector3 LocalEchoPositions[8];
const Vector3 vListenerPos(VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition()));
if(depth==0)
{
for(s32 i=0; i<8; ++i)
{
LocalEchoSuitability[i] = m_EchoSuitability[i];
LocalEchoDistance[i] = m_EchoDistance[i];
LocalEchoPositions[i].Set(m_EchoPosition[i]);
}
}
else
{
s32 offset = audEngineUtil::GetRandomInteger()%8;
for(s32 i=0; i<8; ++i)
{
s32 index = (i + offset)%8;
LocalEchoSuitability[i] = m_EchoSuitability[index];
LocalEchoDistance[i] = Min(20.0f, m_EchoDistance[index]);
LocalEchoPositions[i].Set(m_EchoPosition[index]-vListenerPos);
LocalEchoPositions[i].Scale(LocalEchoDistance[i]);
LocalEchoPositions[i].Add(sourcePosition);
}
}
f32 listenerDistanceToEcho = LocalEchoDistance[echoIndex];
f32 sourceDistanceToEcho = sourcePosition.Dist(LocalEchoPositions[echoIndex]);
f32 sourceDistanceToListener = sourcePosition.Dist(vListenerPos);
f32 distanceDifference = listenerDistanceToEcho + sourceDistanceToEcho - sourceDistanceToListener;
// Work out predelay
u32 predelay = (u32) m_DistanceToDelayCurve.CalculateValue(distanceDifference);
// Work out extra attenuation - want to make it silent if the source is co-located with the echo position, otherwise it'll sound odd.
// Plus, we attenuate echoes depending on how suitable they are.
// We project the echo back past the echo point, so that it gets distance attenuated correctly anyway - that way we get reverb and filtering for free.
// f32 extraDistanceAttenuation = m_DeafeningDistanceRolloffCurve.CalculateValue(sourceDistanceToEcho / rolloffScale);
f32 coLocatedAttenuation = m_SilentWhenEchoCoLocatedCurve.CalculateValue(sourceDistanceToEcho);
f32 suitabilityAttenuation = m_EchoGoodnessDbVolumeCurve.CalculateValue(m_EchoSuitability[echoIndex]);
f32 extraAttenuation = coLocatedAttenuation + suitabilityAttenuation;
Vector3 projectedEcho = LocalEchoPositions[echoIndex] - vListenerPos;
projectedEcho.Normalize();
projectedEcho.Scale(sourceDistanceToEcho);
projectedEcho.Add(LocalEchoPositions[echoIndex]);
if (returnPredelay && returnExtraAttenuation)
{
*returnPredelay = predelay;
*returnExtraAttenuation = extraAttenuation;
*projectedPosition = projectedEcho;
// Warningf("dd: %f; eda: %f; cla: %f; sa: %f; ex: %f; delay: %d", distanceDifference, extraDistanceAttenuation, coLocatedAttenuation, suitabilityAttenuation, extraAttenuation, predelay);
}
}
void naEnvironment::GetInteriorGunSoundMetricsForExterior(f32& returnVolume, f32& returnHold)
{
#if __BANK
m_OutdoorInteriorWeaponVolCurve.Init("REV_WET_TO_OUTDOOR_INTERIOR_WEAPON_VOLUME");
m_OutdoorInteriorWeaponHoldCurve.Init("REV_SIZE_TO_OUTDOOR_INTERIOR_WEAPON_HOLD");
#endif
f32 normalizedReverbWet = ((f32)((m_SourceEnvironmentMetrics[0] & 12) >> 2))/3.0f;
f32 normalizedReverbSize = ((f32)((m_SourceEnvironmentMetrics[0] & 3)))/3.0f;
if(m_SourceEnvironmentMetrics[0] == 0)
{
returnVolume = m_OutdoorInteriorWeaponVolCurve.CalculateValue(0.0f);
returnHold = m_OutdoorInteriorWeaponHoldCurve.CalculateValue(0.0f);
}
else if(normalizedReverbWet == 0)
{
returnVolume = m_OutdoorInteriorWeaponVolCurve.CalculateValue(0.15f);
returnHold = m_OutdoorInteriorWeaponHoldCurve.CalculateValue(normalizedReverbSize);
}
else
{
returnVolume = m_OutdoorInteriorWeaponVolCurve.CalculateValue(normalizedReverbWet);
returnHold = m_OutdoorInteriorWeaponHoldCurve.CalculateValue(normalizedReverbSize);
}
}
void naEnvironment::SetLevelReflectionNormal(const Vector3& levelReflectionNormal, s32 radialIndex)
{
if(naVerifyf(radialIndex >= 0 && radialIndex < g_audNumLevelProbeRadials,
"naEnvironment::SetLevelReflectionNormal Trying to set reflectionNormal data out of bounds at index: %d", radialIndex))
{
m_LevelReflectionNormal[radialIndex] = levelReflectionNormal;
}
}
void naEnvironment::SetUpperReflectionNormal(const Vector3& upperReflectionNormal, s32 radialIndex)
{
if(naVerifyf(radialIndex >= 0 && radialIndex < g_audNumMidProbeRadials,
"naEnvironment::SetUpperReflectionNormal Trying to set reflectionNormal data out of bounds at index: %d", radialIndex))
{
m_UpperReflectionNormal[radialIndex] = upperReflectionNormal;
}
}
void naEnvironment::UpdateLocalReverbEnvironment()
{
// Get up-to-date navmesh numbers
UpdateNavmeshSourceEnvironment();
// Work out how reverby each speaker direction is.
// Draw lines out from the camera in the direction of the speaker - then dot that vector with each of the local offsets, to see how much
// they contribute - quite a lot like the panning algorithm
f32 speakerReverbWets[4][3];
for (u32 i=0; i<4; i++)
{
for (u32 j=0; j<g_NumSourceReverbs; j++)
{
speakerReverbWets[i][j] = 0.0f;
}
}
Vector3 speakerPosition[4];
speakerPosition[0] = Vector3(-0.7071f, 0.7071f, 0.0f);
speakerPosition[1] = Vector3(0.7071f, 0.7071f, 0.0f);
speakerPosition[2] = Vector3(-0.7071f, -0.7071f, 0.0f);
speakerPosition[3] = Vector3(0.7071f, -0.7071f, 0.0f);
Matrix34 fullListenerMatrix = MAT34V_TO_MATRIX34(g_AudioEngine.GetEnvironment().GetPanningListenerMatrix());
// listenerMatrix.d.Zero();
// Matrix34 fullListenerMatrix = FindPlayerPed()->GetMatrix();
Matrix34 listenerMatrix = fullListenerMatrix;
listenerMatrix.d.Zero();
f32 elevation[MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES];
Vector3 positionOnUnitCircle[MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES];
f32 contributionScaling[MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES];
for (u32 k=0; k < m_NumValidPolys; k++)
{
// Where is the poly relative to us
Vector3 polyRelativePosition = m_SourceEnvironmentPolyCentroids[k];
fullListenerMatrix.UnTransform(polyRelativePosition);
f32 distanceFromCamera = polyRelativePosition.Mag();
Vector3 positionOnUnitSphere = polyRelativePosition;
positionOnUnitSphere.NormalizeSafe();
positionOnUnitCircle[k] = positionOnUnitSphere;
positionOnUnitCircle[k].z = 0.f;
positionOnUnitCircle[k].NormalizeSafe();
// Get the angle between the pos on unit sphere and unit circle - and try linearly interping between our headline contr.
elevation[k] = positionOnUnitCircle[k].Dot(positionOnUnitSphere);
if (elevation[k] < -0.01f || elevation[k] > 1.01f)
{
elevation[k] = 0.0f; // it's almost certainly directly above or below, which is elevation=0.0f
}
// Scale contribution by the distance away from the camera
f32 distanceScale = m_DistanceToListenerReverbCurve.CalculateValue(distanceFromCamera);
f32 areaScale = m_AreaToListenerReverbCurve.CalculateValue(m_SourceEnvironmentPolyAreas[k]);
contributionScaling[k] = distanceScale * areaScale;
}
const float scriptOverrideScalar = g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::ListenerReverbDisabled) ? 0.f : 1.f;
for (u32 i=0; i<4; i++)
{
// For this speaker, sum all our neighbouring poly's reverb contribution, scaling the contribution up with poly area,
// up with angle alignment, and down with distance - all done with curves.
// Probably also need to add the one you're actually over in a special cased way - currently just include it as normal,
// since it's centre won't be totally aligned with the listener.
f32 contribution[MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES];
f32 totalContribution = 0.0f;
for (u32 k=0; k<m_NumValidPolys; k++)
{
// Work out this poly's contribution for this speaker
f32 dotProd = positionOnUnitCircle[k].Dot(speakerPosition[i]);
// Don't care about ones more than 90degs away
dotProd = Max(0.0f, dotProd);
if (k==0)
{
// it's the poly we're actually in
dotProd = 1.0f;
}
// Linearly interp between 1/num_speakers and primary contribution as elevation goes from 0 (90degs) to 1 (flat)
contribution[k] = ((1.0f - elevation[k]) * (1.0f/4.0f)) + (elevation[k] * dotProd);
// Scale contribution by area and distance scales
contribution[k] *= contributionScaling[k];
totalContribution += contribution[k];
}
for (u32 k=0; k<m_NumValidPolys; k++)
{
for (u32 j=0; j<g_NumSourceReverbs; j++)
{
speakerReverbWets[i][j] += (contribution[k] * m_SourceEnvironmentPolyReverbWet[k][j] / totalContribution);
}
}
}
// Factor in the ambient environment zones.
const EnvironmentRule* avgEnvRule = g_AmbientAudioEntity.GetAmbientEnvironmentRule();
const f32 envRuleReverbScale = g_AmbientAudioEntity.GetAmbientEnvironmentRuleReverbScale();
if(avgEnvRule && envRuleReverbScale != 0.0f)
{
const f32 scaledReverbDamp = avgEnvRule->ReverbDamp * envRuleReverbScale;
for (u32 i=0; i<4; i++)
{
speakerReverbWets[i][0] *= 1.0f - scaledReverbDamp; // Small
speakerReverbWets[i][1] *= 1.0f - scaledReverbDamp; // Medium
speakerReverbWets[i][2] *= 1.0f - scaledReverbDamp; // Large
speakerReverbWets[i][0] = Max(speakerReverbWets[i][0], avgEnvRule->ReverbSmall * envRuleReverbScale);
speakerReverbWets[i][1] = Max(speakerReverbWets[i][1], avgEnvRule->ReverbMedium * envRuleReverbScale);
speakerReverbWets[i][2] = Max(speakerReverbWets[i][2], avgEnvRule->ReverbLarge * envRuleReverbScale);
}
}
// Smooth our final stored ones
for (u32 i=0; i<4; i++)
{
for (u32 j=0; j<g_NumSourceReverbs; j++)
{
m_SpeakerReverbWets[i][j] = g_SpeakerReverbDamping * m_SpeakerReverbWetSmoothers[i][j].CalculateValue(scriptOverrideScalar * speakerReverbWets[i][j], audNorthAudioEngine::GetCurrentTimeInMs());
}
}
#if __BANK
if (g_DebugListenerReverb)
{
for (u32 i=0; i<4; i++)
{
for (u32 j=0; j<3; j++)
{
m_SpeakerReverbWetSmoothers[i][j].SetRate(g_SpeakerReverbSmootherRate);
}
}
static const char *meterNames[] = {"s", "m", "l"};
static f32 x[4] = {300, 700, 300, 700};
static f32 y[4] = {300, 300, 600, 600};
static audMeterList meterList[4];
for (u32 i=0; i<4; i++)
{
meterList[i].left = x[i];
meterList[i].bottom = y[i];
meterList[i].width = 100.f;
meterList[i].height = 200.f;
meterList[i].values = &(m_SpeakerReverbWets[i][0]);
meterList[i].names = meterNames;
meterList[i].numValues = 3;
audNorthAudioEngine::DrawLevelMeters(&(meterList[i]));
}
for(u32 k = 0; k < m_NumValidPolys; k++)
{
grcDebugDraw::Sphere(m_SourceEnvironmentPolyCentroids[k], 0.25f, Color_AliceBlue);
char buf[128];
formatf(buf, "%d: %f, %f", k, m_SourceEnvironmentPolyReverbWet[k], m_SourceEnvironmentPolyAreas[k]);
grcDebugDraw::Text(m_SourceEnvironmentPolyCentroids[k], Color_AliceBlue, buf);
}
}
if(sm_DebugCoveredObjects)
{
for (u32 i = 0; i< m_InterestingLocalObjects.GetMaxCount(); i++)
{
if(m_InterestingLocalObjects[i].object && m_InterestingLocalObjects[i].envGroup)// && m_InterestingLocalObjects[i].underCover)
{
Vec3V objectPos = m_InterestingLocalObjects[i].object->GetTransform().GetPosition();
if(m_InterestingLocalObjects[i].object->GetCurrentPhysicsInst())
{
objectPos = m_InterestingLocalObjects[i].object->GetCurrentPhysicsInst()->GetCenterOfMass();
}
char buf[128];
formatf(buf, "%s COVER", (m_InterestingLocalObjects[i].envGroup->IsUnderCover()) ? "UNDER " : "NOT UNDER ");
//formatf(buf, "%s COVER", (m_InterestingLocalObjects[i].underCover) ? "UNDER " : "NOT UNDER ");
grcDebugDraw::Text(objectPos, Color_AliceBlue, buf);
//grcDebugDraw::Sphere(m_InterestingLocalObjects[i].object->GetTransform().GetPosition(),1.f,Color_green);
}
}
for(u32 k = 0; k < m_NumValidPolys; k++)
{
if(m_CoveredPolys.IsSet(k))
{
grcDebugDraw::Sphere(m_SourceEnvironmentPolyCentroids[k],sqrt(m_SourceEnvironmentPolyAreas[k]/PI)*sm_RadiusScale, Color32(0,255,0,100));
}
}
}
#endif
g_AudioEngine.GetEnvironment().SetSpeakerReverbWets(m_SpeakerReverbWets);
}
void naEnvironment::UpdateNavmeshSourceEnvironment()
{
u32 roomToneHash = g_NullSoundHash;
u32 rainType = RAIN_TYPE_NONE;
// update our default one
if (!g_DebugRoomSettings)
{
return;
}
InteriorRoom* room = audNorthAudioEngine::GetObject<InteriorRoom>(g_DebugRoomSettings->Room[0].Ref);
if(room)
{
room->ReverbSmall = g_DebugRoomSettingsReverbSmall;
room->ReverbMedium = g_DebugRoomSettingsReverbMedium;
room->ReverbLarge = g_DebugRoomSettingsReverbLarge;
}
// This is almost identical to the occlusion group source environment calculations, but we need to store off
// a bunch more info, and it's wasteful to keep that in pooled OcGroups, just for this one thing.
u32 timeInMs = audNorthAudioEngine::GetCurrentTimeInMs();
// Matrix34 listenerMatrix = g_AudioEngine.GetEnvironment().GetPanningListenerMatrix();
bool playerIsInAnInterior = false;
bool playerIsInASubwayStationOrSubwayTunnel = false;
bool playerIsInASubwayOrTunnel = false;
Vector3 listenerPosition = audNorthAudioEngine::GetMicrophones().GetLocalEnvironmentScanPosition();
CInteriorInst* pIntInst = NULL;
s32 roomIdx = -1;
#if __DEV
bool usingPlayer = false;
#endif
if ((m_IsListenerInHead || g_UsePlayer) && !audNorthAudioEngine::GetOcclusionManager()->GetIsPortalOcclusionEnabled())
{
#if __DEV
usingPlayer = true;
#endif
CPed* player = CGameWorld::FindLocalPlayer();
if(player && player->GetIsInInterior())
{
pIntInst = CInteriorInst::GetInteriorForLocation(player->GetInteriorLocation());
roomIdx = player->GetInteriorLocation().GetRoomIndex();
}
}
else
{
pIntInst = CPortalVisTracker::GetPrimaryInteriorInst();
roomIdx = CPortalVisTracker::GetPrimaryRoomIdx();
}
if (pIntInst && roomIdx>0 && roomIdx!=INTLOC_INVALID_INDEX)
{
playerIsInAnInterior = true;
if (pIntInst->IsSubwayMLO())
{
playerIsInASubwayOrTunnel = true;
}
}
/*
int32 interiorId = -1;
if (player)
{
interiorId = player->GetInteriorInstId();
}
CInteriorInst* pIntInst = NULL;
if (player && player->m_nFlags.bInMloRoom)
{
int32 roomId = player->GetInteriorRoomId();
if (roomId>=0 && roomId!=INTLOC_INVALID_INDEX)
{
playerIsInAnInterior = true;
pIntInst = CInteriorInst::GetPool()->GetAt(interiorId);
if (pIntInst && pIntInst->IsSubwayMLO())
{
playerIsInASubway = true;
}
}
}
*/
if (pIntInst && pIntInst->IsPopulated() && roomIdx>0 && roomIdx!=INTLOC_INVALID_INDEX)// && !pIntInst->IsSubwayMLO())
{
g_AudioEngine.GetEnvironment().SetIsListenerInInterior(true, 0);
if (m_SourceEnvironmentPathHandle != PATH_HANDLE_NULL)
{
CPathServer::CancelRequest(m_SourceEnvironmentPathHandle);
m_SourceEnvironmentPathHandle = PATH_HANDLE_NULL;
}
m_NumValidPolys = 1;
m_SourceEnvironmentPolyCentroids[0] = VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetPanningListenerPosition(0));
m_SourceEnvironmentPolyAreas[0] = 1.0f;
// Get the room we're in
const InteriorSettings* interiorSettings = NULL;
const InteriorRoom* room = NULL;
GetInteriorSettingsForInterior(pIntInst, roomIdx, interiorSettings, room);
naCErrorf(interiorSettings, "Couldn't get InteriorSettings for the room we're in");
if (interiorSettings && room)
{
m_SourceEnvironmentMetrics[0] = 0;
f32 reverbWets[g_NumSourceReverbs];
reverbWets[0] = room->ReverbSmall;
reverbWets[1] = room->ReverbMedium;
reverbWets[2] = room->ReverbLarge;
for (u32 j=0; j<g_NumSourceReverbs; j++)
{
m_SourceEnvironmentPolyReverbWet[0][j] = reverbWets[j];
}
roomToneHash = room->RoomToneSound;
rainType = room->RainType;
if (room->InteriorType==INTERIOR_TYPE_ROAD_TUNNEL ||
room->InteriorType==INTERIOR_TYPE_SUBWAY_TUNNEL ||
room->InteriorType==INTERIOR_TYPE_SUBWAY_STATION)
{
// override the sourceEnvironment, to save setting it, and still get speech echos
m_SourceEnvironmentMetrics[0] = 15;
}
else if (room->InteriorType==INTERIOR_TYPE_SUBWAY_ENTRANCE)
{
// override the sourceEnvironment, to save setting it, and still get speech echos, but a bit less than in the subway proper
m_SourceEnvironmentMetrics[0] = 10;
}
if (room->InteriorType==INTERIOR_TYPE_SUBWAY_TUNNEL ||
room->InteriorType==INTERIOR_TYPE_SUBWAY_STATION)
{
playerIsInASubwayStationOrSubwayTunnel = true;
}
}
// @OCC TODO - Should we switch the CInteriorInst* and InteriorRoomIdx to instead use the fwInteriorLocation?
// Do not update the listener interior settings if the microphone is frozen.
if(!audNorthAudioEngine::GetMicrophones().IsMicFrozen())
{
m_ListenerInteriorInstance = pIntInst;
m_InteriorRoomId = roomIdx;
m_ListenerInteriorSettings = interiorSettings;
m_ListenerInteriorRoom = room;
}
// Update Interior info
UpdateLinkedRoomList(pIntInst);
// UpdateLinkedRoomList(NULL);
}
else
{
// Update Interior info
UpdateLinkedRoomList(NULL);
m_ListenerInteriorInstance = NULL;
m_InteriorRoomId = 0;
m_ListenerInteriorSettings = NULL;
m_ListenerInteriorRoom = NULL;
if (m_SourceEnvironmentPathHandle != PATH_HANDLE_NULL)
{
// we're waiting on a request, so let's see if it's done yet.
EPathServerRequestResult ret = CPathServer::IsRequestResultReady(m_SourceEnvironmentPathHandle);
if (ret==ERequest_Ready)
{
u32 numValidPolys;
u32 sourceEnvironmentMetrics[MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES];
f32 sourceEnvironmentPolyAreas[MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES];
Vector3 sourceEnvironmentPolyCentroids[MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES];
BANK_ONLY(m_CoveredPolys.Reset());
u8 sourceEnvironmentFlags[MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES];
EPathServerErrorCode errorCode = CPathServer::GetAudioPropertiesResult(m_SourceEnvironmentPathHandle, numValidPolys,
sourceEnvironmentMetrics, sourceEnvironmentPolyAreas, sourceEnvironmentPolyCentroids, sourceEnvironmentFlags);
if (errorCode == PATH_NO_ERROR && numValidPolys>0)
{
m_NumValidPolys = numValidPolys;
for (u32 i= 0; i<numValidPolys; i++)
{
m_SourceEnvironmentMetrics[i] = sourceEnvironmentMetrics[i];
const f32 reverbSize = GetSourceEnvironmentReverbSizeFromSEMetric(m_SourceEnvironmentMetrics[i]);
const f32 reverbWet = GetSourceEnvironmentReverbWetFromSEMetric(m_SourceEnvironmentMetrics[i]);
if (sourceEnvironmentFlags[i] & CAudioRequest::FLAG_POLYGON_IS_SHELTERED)
{
m_CoveredPolys.Set(i);
}
for (u32 j=0; j<g_NumSourceReverbs; j++)
{
m_SourceEnvironmentPolyReverbWet[i][j] = reverbWet * m_SourceReverbCrossfadeCurves[j].CalculateValue(reverbSize);
}
m_SourceEnvironmentPolyAreas[i] = sourceEnvironmentPolyAreas[i];
m_SourceEnvironmentPolyCentroids[i] = sourceEnvironmentPolyCentroids[i];
}
}
m_SourceEnvironmentPathHandle = PATH_HANDLE_NULL;
// And now update our time and position, so we don't fire in another request too soon.
// (You could argue these should be set when the request is made, but better to update too slowly than thrash the pathserver.)
// (Actually, I'll ALSO set them when the request goes in, to be super friendly.)
m_SourceEnvironmentTimeLastUpdated = timeInMs;
m_ListenerPositionLastSourceEnvironmentUpdate = listenerPosition;
}
else if(ret == ERequest_NotFound)
{
m_SourceEnvironmentPathHandle = PATH_HANDLE_NULL;
}
else
{
// If we've been waiting more than 2 seconds, something's broken - we've probably lost track of our path handle somehow
// Or you've paused the game... or things have got really busy and there's nothing we can do... or it's a full moon.
// I'll enable this locally and keep an eye on it.
// naAssertf(timeInMs < (m_SourceEnvironmentTimeLastUpdated + 2000) || g_TempFakedPaused || fwTimer::IsGamePaused(), "We've had to wait more than two seconds in UpdateNavmeshSourceEnvironment...could be something broken....or just a full moon");
}
}
else
{
// We don't have a pending request, so see if it's about time we did.
u32 minimumTimeToUpdate = 200;
f32 distanceMovedSqrd = (listenerPosition - m_ListenerPositionLastSourceEnvironmentUpdate).Mag2();
if ((timeInMs > (m_SourceEnvironmentTimeLastUpdated + minimumTimeToUpdate)) &&
(distanceMovedSqrd > 0.25f))
{
// It's been a while, and we've moved, so fire in an update request
m_SourceEnvironmentPathHandle = CPathServer::RequestAudioProperties(listenerPosition, NULL, g_SourceEnvironmentRadius, true);
if (m_SourceEnvironmentPathHandle!=PATH_HANDLE_NULL)
{
// And make sure we don't update again for a while - we also update these number when we get the results back,
// doing it here is mainly to ensure that a failed request doesn't start thrashing.
m_SourceEnvironmentTimeLastUpdated = timeInMs;
m_ListenerPositionLastSourceEnvironmentUpdate = listenerPosition;
}
else
{
// We got a KB.
}
}
}
}
// See if we've just left a subway/tunnel
if (m_PlayerIsInASubwayOrTunnel && !playerIsInASubwayOrTunnel)
{
g_ScriptAudioEntity.PlayMobileGetSignal(0.6f);
}
m_PlayerIsInASubwayStationOrSubwayTunnel = playerIsInASubwayStationOrSubwayTunnel;
m_PlayerIsInASubwayOrTunnel = playerIsInASubwayOrTunnel;
m_PlayerIsInAnInterior = playerIsInAnInterior;
BANK_ONLY(m_PlayerInteriorRatioSmoother.SetRate(g_InteriorRatioSmoothRate/1000.0f));
if (m_PlayerIsInAnInterior)
{
m_PlayerInteriorRatio = m_PlayerInteriorRatioSmoother.CalculateValue(1.0f, timeInMs);
}
else
{
m_PlayerInteriorRatio = m_PlayerInteriorRatioSmoother.CalculateValue(0.0f, timeInMs);
}
// Update our room tone hash
m_RoomToneHash = roomToneHash;
m_RainType = rainType;
if(m_PlayerIsInASubwayOrTunnel && !m_PlayerWasInASubwayOrTunnelLastFrame)
g_AudioScannerManager.TriggerCopDispatchInteraction(SCANNER_CDIT_REPORT_SUSPECT_ENTERED_METRO);
m_PlayerWasInASubwayOrTunnelLastFrame = m_PlayerIsInASubwayOrTunnel;
CalculateUnderCoverFactor();
m_NavmeshInformationUpdatedThisFrame = true;
/*
#if __DEV
if (1)
{
char occlusionDebug[256] = "";
sprintf(occlusionDebug, "t:%d", timeInMs - m_SourceEnvironmentTimeLastUpdated);
grcDebugDraw::PrintToScreenCoors(occlusionDebug, 35,40);
}
#endif
*/
}
void naEnvironment::GetReverbFromSEMetric(const f32 sourceEnvironmentWet, const f32 sourceEnvironmentSize, f32* reverbSmall, f32* reverbMedium, f32* reverbLarge)
{
static const audThreePointPiecewiseLinearCurve revCurve0(0.0f, 1.0f, 0.25f, 0.71f, 0.5f, 0.0f);
(*reverbSmall) = sourceEnvironmentWet * revCurve0.CalculateValue(sourceEnvironmentSize);
if(sourceEnvironmentSize <= 0.5f)
{
static const audThreePointPiecewiseLinearCurve revCurve1sm(0.0f, 0.0f, 0.25f, 0.71f, 0.5f, 1.0f);
(*reverbMedium) = sourceEnvironmentWet * revCurve1sm.CalculateValue(sourceEnvironmentSize);
}
else
{
static const audThreePointPiecewiseLinearCurve revCurve1big(0.5f, 1.0f, 0.75f, 0.71f, 1.0f, 0.0f);
(*reverbMedium) = sourceEnvironmentWet * revCurve1big.CalculateValue(sourceEnvironmentSize);
}
static const audThreePointPiecewiseLinearCurve revCurve2(0.5f, 0.0f, 0.75f, 0.71f, 1.0f, 1.0f);
(*reverbLarge) = sourceEnvironmentWet * revCurve2.CalculateValue(sourceEnvironmentSize);
}
f32 naEnvironment::GetSourceEnvironmentReverbWetFromSEMetric(const u32 sourceEnvironment)
{
if (sourceEnvironment==0)
{
return 0.0f;
}
u32 quantisedReverbWet = (sourceEnvironment & 12) >> 2;
u32 quantisedReverbSize = (sourceEnvironment & 3);
f32 wet = m_SourceEnvironmentToReverbWetCurve.CalculateValue((f32)quantisedReverbWet);
// Now scale the wet value by size - we want to make small spaces 'wetter' - we could do 16 individual pairings, if this isn't good enough
wet *= m_SourceEnvironmentSizeToWetScaling.CalculateValue((f32)quantisedReverbSize);
return wet;
}
f32 naEnvironment::GetSourceEnvironmentReverbSizeFromSEMetric(const u32 sourceEnvironment)
{
if (sourceEnvironment==0)
{
return 0.0f;
}
u32 quantisedReverbSize = (sourceEnvironment & 3);
return m_SourceEnvironmentToReverbSizeCurve.CalculateValue((f32)quantisedReverbSize);
}
fwInteriorLocation naEnvironment::GetRoomIntLocFromPortalIntLoc(const fwInteriorLocation portalIntLoc) const
{
// Initialize to invalid
fwInteriorLocation roomIntLoc;
const CInteriorInst* intInst = NULL;
s32 roomIdx = INTLOC_INVALID_INDEX;
if(naVerifyf(portalIntLoc.IsValid(), "Passed Invalid fwInteriorLocation")
&& naVerifyf(portalIntLoc.IsAttachedToPortal(), "Passed non portal attached interior location when trying to get a room attached interior location"))
{
const CInteriorInst* portalIntInst = CInteriorInst::GetInteriorForLocation(portalIntLoc);
if(portalIntInst)
{
const s32 portalIdx = portalIntLoc.GetPortalIndex();
CPortalFlags portalFlags;
u32 roomToUnsignedIdx, roomFromUnsignedIdx;
portalIntInst->GetMloModelInfo()->GetPortalData(portalIdx, roomToUnsignedIdx, roomFromUnsignedIdx, portalFlags);
// naAssertf(!portalFlags.GetIsMirrorPortal(), "We have an entity attached to a mirror portal, not sure why, but need to handle that case");
const s32 roomToIdx = static_cast<s32>(roomToUnsignedIdx);
const s32 roomFromIdx = static_cast<s32>(roomFromUnsignedIdx);
const CInteriorInst* playerIntInst = audNorthAudioEngine::GetGtaEnvironment()->GetListenerInteriorInstance();
const s32 playerRoomIdx = audNorthAudioEngine::GetGtaEnvironment()->GetInteriorRoomId();
if(playerIntInst && playerRoomIdx != INTLOC_ROOMINDEX_LIMBO)
{
if(portalIntInst == playerIntInst)
{
intInst = playerIntInst;
// Set the roomIdx to be the same room as the player, so doors don't occlude themselves and get the correct rooms reverb
if(playerRoomIdx == roomToIdx)
roomIdx = roomToIdx;
else if(playerRoomIdx == roomFromIdx)
roomIdx = roomFromIdx;
// We're in the same interior but neither of the rooms the door belongs to, so just prefer to use an interior room over outside
else
roomIdx = roomToIdx != INTLOC_ROOMINDEX_LIMBO ? roomToIdx : roomFromIdx;
}
else
{
// Check for a linked portal in which case we need to go through and get the destination to see if it matches the player
if(portalFlags.GetIsLinkPortal())
{
// If the the portal's interior isn't loaded, then we can't see what interiors are linked, so just return what we can.
// Worst case scenario is a door is occluded when it shouldn't be because we're not using the same room index as the player
// but this really shouldn't be an issue in normal gameplay since doors that you see will have their interiors loaded.
// This is to fix a crash probably due to the interior's being reset/reloaded/swapped after a checkpoint restart and
// we end up accessing the GetRoomZeroPortalInsts() when that array is empty because it's not populated.
if(!portalIntInst->IsPopulated())
{
intInst = portalIntInst;
roomIdx = roomToIdx != INTLOC_ROOMINDEX_LIMBO ? roomToIdx : roomFromIdx;
}
else
{
CPortalInst* portalInst = portalIdx < portalIntInst->GetRoomZeroPortalInsts().GetCount() ? portalIntInst->GetRoomZeroPortalInsts()[portalIdx] : NULL;
if(naVerifyf(portalInst, "Couldn't get the CPortalInst* when getting the linked interior destination") && portalInst->IsLinkPortal())
{
CInteriorInst* linkedIntInst = NULL;
s32 linkedIntRoomLimboPortalIdx = INTLOC_INVALID_INDEX;
portalInst->GetDestinationData(portalIntInst, linkedIntInst, linkedIntRoomLimboPortalIdx);
// Is it linked to the interior that the player is in?
if(linkedIntInst && linkedIntInst == playerIntInst && linkedIntRoomLimboPortalIdx != INTLOC_INVALID_INDEX)
{
intInst = playerIntInst;
roomIdx = linkedIntInst->GetDestThruPortalInRoom(INTLOC_ROOMINDEX_LIMBO, linkedIntRoomLimboPortalIdx); //entry/exit portal so we know it's room 0
}
// it's linked but not to us, so grab the non-limbo roomIdx, otherwise we'll be treating it as though it's outside
else
{
intInst = portalIntInst;
roomIdx = roomToIdx != INTLOC_ROOMINDEX_LIMBO ? roomToIdx : roomFromIdx;
}
}
}
}
// That means it's in a different interior that's not connected, so prefer to use outside or the limbo room, as that's the closest "room" to the player
else
{
intInst = portalIntInst;
roomIdx = roomToIdx == INTLOC_ROOMINDEX_LIMBO ? roomToIdx : roomFromIdx;
}
}
}
// The player is outside
else
{
// If it's a link portal, then don't use outside as roomIdx of 0 is actually the interior it's leading to
// Otherwise we'll be playing doors to linked interiors as though they're outside
if(portalFlags.GetIsLinkPortal())
{
intInst = portalIntInst;
roomIdx = roomToIdx != INTLOC_ROOMINDEX_LIMBO ? roomToIdx : roomFromIdx;
}
// Prefer to use outside, so doors don't occlude themselves because they're inside and the player is outside
else
{
intInst = portalIntInst;
roomIdx = roomToIdx == INTLOC_ROOMINDEX_LIMBO ? roomToIdx : roomFromIdx;
}
}
}
}
if(roomIdx != INTLOC_INVALID_INDEX)
{
roomIntLoc = CInteriorInst::CreateLocation(intInst, roomIdx);
}
return roomIntLoc;
}
void naEnvironment::FindInteriorSettingsForRoom(const u32 interiorHash, const u32 roomHash, const InteriorSettings*& interior, const InteriorRoom*& intRoom) const
{
interior = NULL;
intRoom = NULL;
// Get the gameobject with the interior name, then look through for the room.
// If the game object doesn't exist, store NULL, and we'll use defaults from higher-level code
interior = audNorthAudioEngine::GetObject<InteriorSettings>(interiorHash);
if(interior && interior->numRooms > 0)
{
naAssertf(interior->ClassId == InteriorSettings::TYPE_ID, "Attempting to use an interior settings object but it has type %u when it should be %u", interior->ClassId, InteriorSettings::TYPE_ID);
// Look for the right room - use the first one if we don't find a match
for (u8 i=0; i<interior->numRooms; i++)
{
const InteriorRoom* loopRoom = audNorthAudioEngine::GetObject<InteriorRoom>(interior->Room[i].Ref);
if(loopRoom && loopRoom->RoomName == roomHash)
{
intRoom = loopRoom;
break;
}
}
}
}
void naEnvironment::GetInteriorSettingsForEntity(const CEntity* entity, const InteriorSettings*& intSettings, const InteriorRoom*& intRoom)
{
intSettings = NULL;
intRoom = NULL;
// If we're debugging, override with our default global room settings
if (g_UseDebugRoomSettings)
{
GetDebugInteriorSettings(intSettings, intRoom);
return;
}
if(naVerifyf(entity, "Null entity pointer in GetInteriorSettingsForEntity"))
{
const fwInteriorLocation intLoc = entity->GetAudioInteriorLocation();
GetInteriorSettingsForInteriorLocation(intLoc, intSettings, intRoom);
}
}
void naEnvironment::GetInteriorSettingsForInteriorLocation(const fwInteriorLocation intLoc, const InteriorSettings*& intSettings, const InteriorRoom*& intRoom)
{
intSettings = NULL;
intRoom = NULL;
// If we're debugging, override with our default global room settings
if (g_UseDebugRoomSettings)
{
GetDebugInteriorSettings(intSettings, intRoom);
return;
}
// If we're attached to a portal, then we need to figure out the best roomIdx to use based on the player location
fwInteriorLocation roomAttachedIntLoc = intLoc;
if(intLoc.IsAttachedToPortal())
{
roomAttachedIntLoc = GetRoomIntLocFromPortalIntLoc(intLoc);
}
const CInteriorInst* intInst = CInteriorInst::GetInteriorForLocation(roomAttachedIntLoc);
if(intInst)
{
const s32 roomIdx = roomAttachedIntLoc.GetRoomIndex();
if(roomIdx != INTLOC_INVALID_INDEX && roomIdx < (s32)intInst->GetNumRooms())
{
GetInteriorSettingsForInterior(intInst, roomIdx, intSettings, intRoom);
}
}
}
void naEnvironment::GetInteriorSettingsForInterior(const CInteriorInst* intInst, const s32 roomIdx, const InteriorSettings*& intSettings, const InteriorRoom*& intRoom)
{
intSettings = NULL;
intRoom = NULL;
// If we're debugging, override with our default global room settings
if (g_UseDebugRoomSettings)
{
GetDebugInteriorSettings(intSettings, intRoom);
return;
}
if (intInst && roomIdx!=INTLOC_INVALID_INDEX
&& naVerifyf(roomIdx < intInst->GetNumRooms(), "Requesting InteriorSettings with an invalid roomIdx. Int: %s roomIdx: %d", intInst->GetModelName(), roomIdx))
{
// Get the room we're in
CMloModelInfo* pModelInfo = intInst->GetMloModelInfo();
if(naVerifyf(pModelInfo, "Failed to get model info from interior instance model index %d", intInst->GetModelId().GetModelIndex()))
{
pModelInfo->GetAudioSettings(roomIdx, intSettings, intRoom);
// If we don't find one, return a sensible default
if (!intSettings)
{
// perhaps try again - in debug mode, we might have created the object after the room was init'd.
if (g_TryRoomsEveryTime)
{
audNorthAudioEngine::GetGtaEnvironment()->FindInteriorSettingsForRoom(pModelInfo->GetHashKey(), intInst->GetRoomHashcode(roomIdx), intSettings, intRoom);
// And store it, so next time we find it straight away
pModelInfo->SetAudioSettings(roomIdx, intSettings, intRoom);
}
// If the default doesn't exist, use the debug one
if (!intSettings)
{
GetDebugInteriorSettings(intSettings, intRoom);
}
}
}
}
}
void naEnvironment::GetDebugInteriorSettings(const InteriorSettings*& intSettings, const InteriorRoom*& intRoom) const
{
intSettings = g_DebugRoomSettings;
intRoom = audNorthAudioEngine::GetObject<InteriorRoom>(g_DebugRoomSettings->Room[0].Ref);
}
#if __BANK
void naEnvironment::AddBuildingToWorld(const Vector3 &pos, CEntity* entity)
{
if(entity)
{
const Vector3 boundingBoxMax = entity->GetBoundingBoxMax();
const Vector3 boundingBoxMin = entity->GetBoundingBoxMin();
f32 width = abs(boundingBoxMax.x - boundingBoxMin.x);
f32 depth = abs(boundingBoxMax.y - boundingBoxMin.y);
f32 baseArea = (depth * width);
// Discount buildings with a massive or tiny footprint- they're probably actually huge chunks of terrain or
// little electricity pylons respectively
if(baseArea > g_MinBuildingFootprintArea &&
baseArea < g_MaxBuildingFootprintArea)
{
f32 height = abs(boundingBoxMax.z - boundingBoxMin.z);
if(height >= g_MinBuildingHeight)
{
if(m_NumBuildingAddsThisFrame >= g_audMaxCachedSectorOps)
{
ProcessCachedWorldSectorOps();
}
m_WSBuildingAddHeights[m_NumBuildingAddsThisFrame] = static_cast<u32>(height);
m_WSBuildingAddIndices[m_NumBuildingAddsThisFrame++] = static_cast<u32>(ComputeWorldSectorIndex(pos));
}
}
}
}
void naEnvironment::AddTreeToWorld(const Vector3 &pos, CEntity* entity)
{
if(entity)
{
f32 height = entity->GetBoundingBoxMax().z - entity->GetBoundingBoxMin().z;
// Small trees don't count. Its a shrubbery! etc.
if(height > g_MinHeightForTree)
{
// if theres space in the cached list then add it there to save a LHS
if(m_NumTreeAddsThisFrame >= g_audMaxCachedSectorOps)
{
ProcessCachedWorldSectorOps();
}
m_WSTreeAddIndices[m_NumTreeAddsThisFrame++] = static_cast<u32>(ComputeWorldSectorIndex(pos));
}
}
}
void naEnvironment::RemoveTreeFromWorld(const Vector3 &pos)
{
// if theres space in the cached list then add it there to save a LHS
if(m_NumTreeRemovesThisFrame >= g_audMaxCachedSectorOps)
{
ProcessCachedWorldSectorOps();
}
m_WSTreeRemoveIndices[m_NumTreeRemovesThisFrame++] = static_cast<u32>(ComputeWorldSectorIndex(pos));
}
void naEnvironment::RemoveBuildingFromWorld(const Vector3 &pos)
{
// if theres space in the cached list then add it there to save a LHS
if(m_NumBuildingRemovesThisFrame >= g_audMaxCachedSectorOps)
{
ProcessCachedWorldSectorOps();
}
m_WSBuildingRemoveIndices[m_NumBuildingRemovesThisFrame++] = static_cast<u32>(ComputeWorldSectorIndex(pos));
}
void naEnvironment::AddWaterQuad(const Vector3 &v1,const Vector3 &v2,const Vector3 &v3,const Vector3 &v4)
{
const u32 index1 = static_cast<u32>(ComputeWorldSectorIndex(v1));
const u32 index2 = static_cast<u32>(ComputeWorldSectorIndex(v2));
const u32 index3 = static_cast<u32>(ComputeWorldSectorIndex(v3));
const u32 index4 = static_cast<u32>(ComputeWorldSectorIndex(v4));
const u32 secX1 = index1 % g_audNumWorldSectorsX;
const u32 secY1 = (index1-secX1)/g_audNumWorldSectorsX;
const u32 secX2 = index2 % g_audNumWorldSectorsX;
const u32 secY2 = (index2-secX2)/g_audNumWorldSectorsX;
const u32 secX3 = index3 % g_audNumWorldSectorsX;
const u32 secY3 = (index3-secX3)/g_audNumWorldSectorsX;
const u32 secX4 = index4 % g_audNumWorldSectorsX;
const u32 secY4 = (index4-secX4)/g_audNumWorldSectorsX;
// find the min and max sector index
const u32 minIndex = Min(index1, index2, index3, index4);
const u32 maxIndex = Max(index1, index2, index3, index4);
// and min/max X/Y
const u32 minSecX = Max(Min(secX1, secX2, secX3, secX4), (u32)0);
const u32 maxSecX = Min(Max(secX1, secX2, secX3, secX4), (u32)(g_audNumWorldSectorsX-1));
const u32 minSecY = Max(Min(secY1, secY2, secY3, secY4), (u32)0);
const u32 maxSecY = Min(Max(secY1, secY2, secY3, secY4), (u32)(g_audNumWorldSectorsY-1));
for(u32 i = minIndex; i <= maxIndex; i++)
{
const u32 curX = i % g_audNumWorldSectorsX;
const u32 curY = (i-curX)/g_audNumWorldSectorsX;
// does this sector intersect
if(curX >= minSecX && curX <= maxSecX && curY >= minSecY && curY <= maxSecY)
{
//g_AudioWorldSectors[i].isWaterSector = false;
}
}
}
void naEnvironment::ProcessCachedWorldSectorOps()
{
for(u32 i =0; i < m_NumBuildingAddsThisFrame; i++)
{
f32 fBuildingHeight = (f32)(m_WSBuildingAddHeights[i]/284.f);
s32 buildingHeight = (s32)(fBuildingHeight * 254.f);
buildingHeight = (u8)Clamp(buildingHeight,0,254);
if(g_AudioWorldSectors[m_WSBuildingAddIndices[i]].tallestBuilding < buildingHeight)
{
Assign(g_AudioWorldSectors[m_WSBuildingAddIndices[i]].tallestBuilding, buildingHeight);
}
if(g_AudioWorldSectors[m_WSBuildingAddIndices[i]].numBuildings < 255)
{
g_AudioWorldSectors[m_WSBuildingAddIndices[i]].numBuildings++;
}
}
for(u32 i =0; i < m_NumBuildingRemovesThisFrame; i++)
{
if(g_AudioWorldSectors[m_WSBuildingRemoveIndices[i]].numBuildings > 0)
{
g_AudioWorldSectors[m_WSBuildingRemoveIndices[i]].numBuildings--;
}
}
for(u32 i =0; i < m_NumTreeAddsThisFrame; i++)
{
if(g_AudioWorldSectors[m_WSTreeAddIndices[i]].numTrees < 127)
{
g_AudioWorldSectors[m_WSTreeAddIndices[i]].numTrees++;
}
}
for(u32 i =0; i < m_NumTreeRemovesThisFrame; i++)
{
if(g_AudioWorldSectors[m_WSTreeRemoveIndices[i]].numTrees > 0)
{
g_AudioWorldSectors[m_WSTreeRemoveIndices[i]].numTrees--;
}
}
m_NumBuildingAddsThisFrame = m_NumBuildingRemovesThisFrame = m_NumTreeAddsThisFrame = m_NumTreeRemovesThisFrame = 0;
}
#endif
f32 naEnvironment::ComputeWorldSectorIndex(const Vector3 &pos) const
{
// NOTE: this function returns an f32 rather than u32 index, so that a LHS can be avoided in higher level code in some cases
// NOTE: assumes world is centered at origin
f32 x = floorf((pos.x / g_audWidthOfSector) + (g_fAudNumWorldSectorsX * 0.5f));
f32 y = floorf((pos.y / g_audDepthOfSector) + (g_fAudNumWorldSectorsY * 0.5f));
x = Clamp(x, 0.0f, g_fAudNumWorldSectorsX-1);
y = Clamp(y, 0.0f, g_fAudNumWorldSectorsY-1);
return x + (y * g_fAudNumWorldSectorsX);
}
void naEnvironment::ComputeRainMixForSector(const u32 sectorIndex, const f32 rainVol, audRainXfade *directionMixes)
{
audRainXfade sectorRain[9];
const u32 sectorX = sectorIndex % g_audNumWorldSectorsX, sectorY = (sectorIndex-sectorX)/g_audNumWorldSectorsY;
u32 i = 0;
ComputeRainMixForSector(sectorX-1,sectorY+1,sectorRain[i++]);
ComputeRainMixForSector(sectorX,sectorY+1,sectorRain[i++]);
ComputeRainMixForSector(sectorX+1,sectorY+1,sectorRain[i++]);
ComputeRainMixForSector(sectorX-1,sectorY,sectorRain[i++]);
ComputeRainMixForSector(sectorX,sectorY,sectorRain[i++]);
ComputeRainMixForSector(sectorX+1,sectorY,sectorRain[i++]);
ComputeRainMixForSector(sectorX-1,sectorY-1,sectorRain[i++]);
ComputeRainMixForSector(sectorX,sectorY-1,sectorRain[i++]);
ComputeRainMixForSector(sectorX+1,sectorY-1,sectorRain[i++]);
const Matrix34 listenerMatrix = audNorthAudioEngine::GetMicrophones().GetAmbienceMic();//g_AudioEngine.GetEnvironment().GetPanningListenerMatrix();
const Vector3 north = Vector3(0.0f, 1.0f, 0.0f);
const Vector3 east = Vector3(1.0f,0.0f,0.0f);
Vector3 right = listenerMatrix.a;
right.NormalizeSafe();
const f32 cosangle = right.Dot(north);
const f32 angle = AcosfSafe(cosangle);
const f32 degrees = RtoD*angle;
f32 actualDegrees;
if(right.Dot(east) <= 0.0f)
{
actualDegrees = 360.0f - degrees;
}
else
{
actualDegrees = degrees;
}
s32 basePan = 360 - ((s32)actualDegrees - 90);
const Vector2 listenerPos(listenerMatrix.d.x, listenerMatrix.d.y);
const Vector2 emitterPositions[] = {Vector2(sectorRain[1].x,sectorRain[1].y), // NORTH
Vector2(sectorRain[3].x,sectorRain[3].y), // EAST
Vector2(sectorRain[5].x,sectorRain[5].y), // SOUTH
Vector2(sectorRain[7].x,sectorRain[7].y)}; // WEST
for(u32 emitterIdx = 0; emitterIdx < 4; emitterIdx++)
{
Vector2 list2emitter = (listenerPos - emitterPositions[emitterIdx]);
list2emitter.NormalizeSafe();
f32 factor[9];
for(u32 i = 0; i < 9; i++)
{
const Vector2 sectorPos = Vector2(sectorRain[i].x, sectorRain[i].y);
Vector2 list2sector = (listenerPos - sectorPos);
list2sector.NormalizeSafe();
// ignore angle for sector that the listener is in
const f32 angleContrib = (i==4?1.0f:Clamp(list2emitter.Dot(list2sector), 0.0f, 1.0f));
static f32 distScaler = 0.05f;
const f32 distFactor = Clamp(1.0f / ((listenerPos - sectorPos) * distScaler).Mag2(), 0.0f, 1.0f);
factor[i] = angleContrib * distFactor;
}
f32 sqSum = 0.0f;
for(u32 i = 0; i < 9; i++)
{
sqSum += factor[i] * factor[i];
}
f32 sqFactor = sqrt(sqSum);
directionMixes[emitterIdx].buildingRain = directionMixes[emitterIdx].waterRain = directionMixes[emitterIdx].treeRain = 0.0f;
for(u32 i = 0; i < 9; i++)
{
const f32 sectorFactor = (factor[i]/sqFactor);
directionMixes[emitterIdx].buildingRain += (sectorFactor * sectorRain[i].buildingRain);
directionMixes[emitterIdx].treeRain += (sectorFactor * sectorRain[i].treeRain);
directionMixes[emitterIdx].waterRain += (sectorFactor * sectorRain[i].waterRain);
}
directionMixes[emitterIdx].buildingRain *= rainVol;
directionMixes[emitterIdx].treeRain *= rainVol;
directionMixes[emitterIdx].waterRain *= rainVol;
directionMixes[emitterIdx].pan = (basePan + emitterIdx*90) % 360;
}
}
void naEnvironment::ComputeRainMixForSector(const u32 sectorX, const u32 sectorY, audRainXfade &rain) const
{
if(sectorX < g_audNumWorldSectorsX && sectorY < g_audNumWorldSectorsY)
{
const u32 sector = sectorX + (sectorY*g_audNumWorldSectorsX);
const bool isWaterSector = g_AudioWorldSectors[sector].isWaterSector;
rain.x = g_audWidthOfSector * (sectorX - (g_fAudNumWorldSectorsX/2.f));
rain.y = g_audDepthOfSector * (sectorY - (g_fAudNumWorldSectorsY/2.f));
if(g_AmbientAudioEntity.IsPlayerInTheCity())
{
rain.treeRain = (Min( g_NumTreesForRain, static_cast<f32>(g_AudioWorldSectors[sector].numTrees)) / g_NumTreesForRainBuiltUp);
}
else
{
rain.treeRain = (Min( g_NumTreesForRain, static_cast<f32>(g_AudioWorldSectors[sector].numTrees)) / g_NumTreesForRain);
}
rain.buildingRain = 1.0f - rain.treeRain;
const Vector3 v1(rain.x,rain.y,100.0f), v2(rain.x+g_audWidthOfSector,rain.y+g_audDepthOfSector,100.f);
// const Vector3 centre = (v1+v2) * 0.5f;
if(isWaterSector)
{
rain.waterRain = 1.0f;
rain.buildingRain = 0.0f;
rain.treeRain = 0.0f;
}
else
{
rain.waterRain = 0.0f;
}
}
else
{
// invalid sector - default to water (since the map is surrounded by water)
rain.waterRain = 1.0f;
rain.buildingRain = 0.0f;
rain.treeRain = 0.0f;
rain.x = 0.0f;
rain.y = 0.0f;
}
f32 sqFactor = sqrt(rain.waterRain*rain.waterRain + rain.buildingRain*rain.buildingRain + rain.treeRain*rain.treeRain);
rain.waterRain /= sqFactor;
rain.buildingRain /= sqFactor;
rain.treeRain /= sqFactor;
}
f32 naEnvironment::GetBuiltUpFactorOfSector(u32 sector){
return m_BuiltUpFactorPerZone[sector];
}
void naEnvironment::UpdateMetrics()
{
const Matrix34 listenerMatrix = MAT34V_TO_MATRIX34(g_AudioEngine.GetEnvironment().GetPanningListenerMatrix());
const u32 sectorIndex = static_cast<u32>(ComputeWorldSectorIndex(listenerMatrix.d));
const u32 sectorX = sectorIndex % g_audNumWorldSectorsX;
const u32 sectorY = (sectorIndex-sectorX)/g_audNumWorldSectorsY;
if(!m_RefreshHighwayFactor)
{
if(m_LastHighwaySectorX != sectorX ||
m_LastHighwaySectorY != sectorY)
{
m_RefreshHighwayFactor = true;
m_HighwayZoneToRefresh = 0;
m_LastHighwaySectorX = sectorX;
m_LastHighwaySectorY = sectorY;
}
}
for (u32 y=0; y<AUD_NUM_SECTORS_ACROSS; y++)
{
for (u32 x=0; x<AUD_NUM_SECTORS_ACROSS; x++)
{
u32 xSector = sectorX+x-AUD_HALF_NUM_SECTORS_ACROSS;
u32 ySector = sectorY-y+AUD_HALF_NUM_SECTORS_ACROSS;
if(xSector < g_audNumWorldSectorsX && ySector < g_audNumWorldSectorsY)
{
const u32 sector = xSector + (ySector*g_audNumWorldSectorsX);
m_BuildingHeights[x+AUD_NUM_SECTORS_ACROSS*y] = GetBuildingHeightForSector(sector);
m_BuildingCount[x+AUD_NUM_SECTORS_ACROSS*y] = GetBuildingCountForSector(sector);
m_TreeCount[x+AUD_NUM_SECTORS_ACROSS*y] = GetTreeCountForSector(sector);
m_WaterAmount[x+AUD_NUM_SECTORS_ACROSS*y] = GetWaterAmountForSector(sector);
m_HighwayAmount[x+AUD_NUM_SECTORS_ACROSS*y] = GetHighwayAmountForSector(sector);
#if __BANK
// Just update one zone at a time, no need for uber-accuracy
if(sm_ProcessSectors && m_RefreshHighwayFactor &&
(y + (x * AUD_NUM_SECTORS_ACROSS)) == m_HighwayZoneToRefresh)
{
Vector3 zoneMin,zoneMax;
zoneMin.x = g_audWidthOfSector * (xSector - (g_fAudNumWorldSectorsX/2.f));
zoneMin.y = g_audDepthOfSector * (ySector - (g_fAudNumWorldSectorsY/2.f));
zoneMax.x = zoneMin.x + g_audWidthOfSector;
zoneMax.y = zoneMin.y + g_audDepthOfSector;
Vector3 centre = (zoneMin + zoneMax) * 0.5f;
centre.z = CGameWorldHeightMap::GetMinHeightFromWorldHeightMap(centre.x, centre.y);
zoneMin.z = centre.z - 50.0f;
zoneMax.z = centre.z + 50.0f;
audParseNodeCallbackData callbackData;
callbackData.numHighwayNodes = 0;
callbackData.numNodes = 0;
callbackData.minPos = zoneMin;
callbackData.maxPos = zoneMax;
ThePaths.ForAllNodesInArea(zoneMin, zoneMax, ParseNodeCallback, &callbackData);
if(callbackData.numNodes > 0)
{
Assign(g_AudioWorldSectors[sector].numHighwayNodes, Clamp(callbackData.numHighwayNodes, 0, 256));
}
}
#endif
}
if(g_UpdateBuiltUpFactor)
{
f32 builtUpFactor = m_HeightToBuiltUpFactor.CalculateValue(m_BuildingHeights[x+AUD_NUM_SECTORS_ACROSS*y]);
m_BuiltUpFactorPerZone[x+AUD_NUM_SECTORS_ACROSS*y] = m_BuiltUpSmoother.CalculateValue(builtUpFactor, audNorthAudioEngine::GetCurrentTimeInMs());
}
}
}
// Work out where the centre of the main sector is, so we know how much of the others to factor in
f32 mainSectorX = g_audWidthOfSector * (sectorX - (g_fAudNumWorldSectorsX/2.f) + 0.5f);
f32 mainSectorY = g_audDepthOfSector * (sectorY - (g_fAudNumWorldSectorsY/2.f) + 0.5f);
f32 ourX = listenerMatrix.d.x;
f32 ourY = listenerMatrix.d.y;
f32 ratioCtoR = (ourX - mainSectorX)/(g_audWidthOfSector); // this goes from -0.5 to 0.5 - we never want to be fully in the next sector
f32 ratioCtoU = (ourY - mainSectorY)/(g_audDepthOfSector); // this goes from -0.5 to 0.5 - we never want to be fully in the next sector
u32 diagonalCrossfadeIndex = AUD_CENTER_SECTOR;
u32 yCrossfadeIndex = AUD_CENTER_SECTOR + AUD_NUM_SECTORS_ACROSS;
if (ratioCtoU>0.0f)
{
yCrossfadeIndex = AUD_CENTER_SECTOR - AUD_NUM_SECTORS_ACROSS;
}
u32 xCrossfadeIndex = AUD_CENTER_SECTOR - 1;
if (ratioCtoR>0.0f)
{
xCrossfadeIndex = AUD_CENTER_SECTOR + 1;
diagonalCrossfadeIndex = yCrossfadeIndex+1;
}
else
{
diagonalCrossfadeIndex = yCrossfadeIndex-1;
}
UpdateBuiltUpMetrics(ratioCtoR, ratioCtoU, xCrossfadeIndex, yCrossfadeIndex, diagonalCrossfadeIndex);
UpdateBuildingDensityMetrics(ratioCtoR, ratioCtoU, xCrossfadeIndex, yCrossfadeIndex, diagonalCrossfadeIndex);
UpdateTreeDensityMetrics(ratioCtoR, ratioCtoU, xCrossfadeIndex, yCrossfadeIndex, diagonalCrossfadeIndex);
UpdateWaterMetrics(ratioCtoR, ratioCtoU, xCrossfadeIndex, yCrossfadeIndex, diagonalCrossfadeIndex);
UpdateHighwayMetrics(ratioCtoR, ratioCtoU, xCrossfadeIndex, yCrossfadeIndex, diagonalCrossfadeIndex);
if(m_RefreshHighwayFactor)
{
m_HighwayZoneToRefresh++;
if(m_HighwayZoneToRefresh >= AUD_NUM_SECTORS)
{
m_RefreshHighwayFactor = false;
}
}
}
void naEnvironment::UpdateBuiltUpMetrics(f32 ratioCtoR, f32 ratioCtoU, u32 xCrossfadeIndex, u32 yCrossfadeIndex, u32 diagonalCrossfadeIndex)
{
m_BuiltUpSmoother.SetRate(g_BuiltUpSmootherRate);
m_BuiltUpDampingFactorSmoother.SetRates(g_BuiltUpDampingFactorSmoothUpRate/1000.0f, g_BuiltUpDampingFactorSmoothDownRate/1000.0f);
f32 xRatio = Abs(ratioCtoR);
f32 yRatio = Abs(ratioCtoU);
f32 centreHeight = GetAveragedBuildingHeightForIndex(AUD_CENTER_SECTOR);
f32 xOffsetHeight = GetAveragedBuildingHeightForIndex(xCrossfadeIndex);
f32 yOffsetHeight = GetAveragedBuildingHeightForIndex(yCrossfadeIndex);
f32 diagOffsetHeight = GetAveragedBuildingHeightForIndex(diagonalCrossfadeIndex);
f32 baseXOffsetHeight = audCurveRepository::GetLinearInterpolatedValue(centreHeight, xOffsetHeight, 0.0f, 1.0f, xRatio);
f32 yOffsetXOffsetHeight = audCurveRepository::GetLinearInterpolatedValue(yOffsetHeight, diagOffsetHeight, 0.0f, 1.0f, xRatio);
f32 finalHeight = audCurveRepository::GetLinearInterpolatedValue(baseXOffsetHeight, yOffsetXOffsetHeight, 0.0f, 1.0f, yRatio);
f32 builtUpFactor = m_HeightToBuiltUpFactor.CalculateValue(finalHeight);
f32 timeSmoothedBuiltUpFactor = m_BuiltUpSmoother.CalculateValue(builtUpFactor, audNorthAudioEngine::GetCurrentTimeInMs());
m_BuiltUpFactor = timeSmoothedBuiltUpFactor;
f32 directionalBuildingHeight[4];
directionalBuildingHeight[0] = GetAveragedBuildingHeightFromXYOffset(0.0f+ratioCtoR, g_NSEW_Offset+ratioCtoU);
directionalBuildingHeight[1] = GetAveragedBuildingHeightFromXYOffset(g_NSEW_Offset+ratioCtoR, 0.0f+ratioCtoU);
directionalBuildingHeight[2] = GetAveragedBuildingHeightFromXYOffset(0.0f+ratioCtoR, -g_NSEW_Offset+ratioCtoU);
directionalBuildingHeight[3] = GetAveragedBuildingHeightFromXYOffset(-g_NSEW_Offset+ratioCtoR, 0.0f+ratioCtoU);
for (u32 i=0; i<4; i++)
{
m_DirectionalBuiltUpFactor[i] = m_BuiltUpDirectionalSmoother[i].CalculateValue(m_HeightToBuiltUpFactor.CalculateValue(directionalBuildingHeight[i]), audNorthAudioEngine::GetCurrentTimeInMs());
}
#if __BANK
if (g_DebugBuiltUpInfo)
{
static f32 x[AUD_NUM_SECTORS];
static f32 y[AUD_NUM_SECTORS];
for (u32 i=0; i<AUD_NUM_SECTORS_ACROSS; i++)
{
for (u32 j=0; j<AUD_NUM_SECTORS_ACROSS; j++)
{
x[i*AUD_NUM_SECTORS_ACROSS+j] = 100.0f + (800.0f/AUD_NUM_SECTORS_ACROSS)*j;
y[i*AUD_NUM_SECTORS_ACROSS+j] = 100.0f + (700.0f/AUD_NUM_SECTORS_ACROSS)*i;
}
}
static f32 heights[AUD_NUM_SECTORS][2];
static audMeterList meterList[AUD_NUM_SECTORS];
static const char* noName[2] = {"",""};
for (u32 i=0; i<AUD_NUM_SECTORS; i++)
{
heights[i][0] = m_BuildingHeights[i] / 256.0f;
heights[i][1] = m_HeightToBuiltUpFactor.CalculateValue(m_BuildingHeights[i]);
meterList[i].left = x[i];
meterList[i].bottom = y[i];
meterList[i].width = 10.f;
meterList[i].height = 40.f;
meterList[i].values = &(heights[i][0]);
meterList[i].names = &noName[0];
meterList[i].numValues = 2;
audNorthAudioEngine::DrawLevelMeters(&(meterList[i]));
}
static f32 localBuiltUpValue;
static audMeterList localBuiltUpMeter;
static const char* localBuiltUpMeterName = "C";
localBuiltUpValue = m_BuiltUpFactor;
localBuiltUpMeter.left = x[AUD_NUM_SECTORS-1] + 150;
localBuiltUpMeter.bottom = y[AUD_NUM_SECTORS/2];
localBuiltUpMeter.width = 10.f;
localBuiltUpMeter.height = 40.f;
localBuiltUpMeter.values = &localBuiltUpValue;
localBuiltUpMeter.numValues = 1;
localBuiltUpMeter.names = &localBuiltUpMeterName;
audNorthAudioEngine::DrawLevelMeters(&localBuiltUpMeter);
const Matrix34 listenerMatrix = MAT34V_TO_MATRIX34(g_AudioEngine.GetEnvironment().GetPanningListenerMatrix());
Vector3 right = listenerMatrix.a;
right.NormalizeFast();
const f32 cosangle = right.Dot(g_Directions[AUD_AMB_DIR_NORTH]);
const f32 angle = AcosfSafe(cosangle);
const f32 degrees = RtoD*angle;
f32 actualDegrees;
if(right.Dot(g_Directions[AUD_AMB_DIR_EAST]) <= 0.0f)
{
actualDegrees = 360.0f - degrees;
}
else
{
actualDegrees = degrees;
}
//Adjust the degrees so North is 0 degrees and get it between 0-360
actualDegrees += 270.f;
while(actualDegrees > 360.0f)
{
actualDegrees -= 360.0f;
}
static audMeterList dirMeterLists[4];
static const char *dirNamesList[4] = {"N", "E", "S", "W"};
static const f32 meterBasePan[4] = {0, 90, 180, 270};
static f32 dirBuiltUpValues[4];
for(u32 i = 0; i < 4; i++)
{
dirBuiltUpValues[i] = m_DirectionalBuiltUpFactor[i];
f32 cirleDegrees = meterBasePan[i] + (360.0f - actualDegrees) + 270.0f;
while(cirleDegrees > 360.0f)
{
cirleDegrees -= 360.0f;
}
const f32 angle = cirleDegrees * (PI/180);
dirMeterLists[i].left = x[AUD_NUM_SECTORS-1] + 150 + (75.f * rage::Cosf(angle));
dirMeterLists[i].width = 10.f;
dirMeterLists[i].bottom = y[AUD_NUM_SECTORS/2] + (75.f * rage::Sinf(angle));
dirMeterLists[i].height = 40.f;
dirMeterLists[i].names = &dirNamesList[i];
dirMeterLists[i].values = &dirBuiltUpValues[i];
dirMeterLists[i].numValues = 1;
audNorthAudioEngine::DrawLevelMeters(&dirMeterLists[i]);
}
}
if (g_DisplayBuiltUpFactors)
{
char builtUpFactor[256] = "";
// sprintf(builtUpFactor, "bUF: %f; ht: %f; ySec: %d, xSec: %d; diagSec: %d; yR: %f; xR: %f", m_BuiltUpFactor, finalHeight,
// yCrossfadeIndex, xCrossfadeIndex, diagonalCrossfadeIndex, yRatio, xRatio);
sprintf(builtUpFactor, "Centre: %f; North: %f; East: %f; South: %f; West: %f", m_BuiltUpFactor, m_DirectionalBuiltUpFactor[0], m_DirectionalBuiltUpFactor[1], m_DirectionalBuiltUpFactor[2], m_DirectionalBuiltUpFactor[3]);
grcDebugDraw::PrintToScreenCoors(builtUpFactor, 25,30);
}
#endif
// Until I can do an audio sync, override this good stuff with something shit
if (!g_UseProperBuiltUpFactor)
{
if (g_ShouldOverrideBuiltUpFactor)
{
m_BuiltUpFactor = g_OverrideBuiltUpFactor;
for (u32 i=0; i<4; i++)
{
m_DirectionalBuiltUpFactor[i] = g_OverrideBuiltUpFactor;
}
}
// else if (CMapAreas::IsPositionInNamedArea(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition()), atStringhash("Manhattan")))
// {
// m_BuiltUpFactor = 1.0f;
// }
else
{
m_BuiltUpFactor = 0.5f;
}
}
// If we're in an interior, damp down all our built-up factors.
f32 builtUpDampingFactor = 1.0f;
bool isInteriorASubway = false;
if (AreWeInAnInterior(&isInteriorASubway))
{
if (!isInteriorASubway)
{
builtUpDampingFactor = 0.0f;
}
}
f32 builtUpDampingFactorSmoothed = m_BuiltUpDampingFactorSmoother.CalculateValue(builtUpDampingFactor, audNorthAudioEngine::GetCurrentTimeInMs());
// Do useful stuff with the metric
g_AudioEngine.GetEnvironment().SetBuiltUpFactor(m_BuiltUpFactor);
g_AudioEngine.GetEnvironment().SetBuiltUpDirectionFactors(m_DirectionalBuiltUpFactor);
const f32 builtUpRolloff = m_BuiltUpToRolloff.CalculateValue(m_BuiltUpFactor);
f32 plateauScale = 1.0f;
if(naVerifyf(builtUpRolloff != 0.0f, "'BUILT_UP_TO_ROLLOFF' curve returned 0.0f, needs to be non-zero"))
{
plateauScale = 1.0f / builtUpRolloff;
}
g_AudioEngine.GetEnvironment().SetGlobalRolloffPlateauScale(plateauScale);
g_AudioEngine.GetEnvironment().SetRolloffDampingFactor(builtUpDampingFactorSmoothed*g_BuiltUpDampingFactorOverride);
}
void naEnvironment::UpdateBuildingDensityMetrics(f32 ratioCtoR, f32 ratioCtoU, u32 xCrossfadeIndex, u32 yCrossfadeIndex, u32 diagonalCrossfadeIndex)
{
m_BuildingDensitySmoother.SetRate(g_BuiltUpSmootherRate);
f32 xRatio = Abs(ratioCtoR);
f32 yRatio = Abs(ratioCtoU);
f32 centreCount = GetAveragedBuildingCountForIndex(AUD_CENTER_SECTOR);
f32 xOffsetCount = GetAveragedBuildingCountForIndex(xCrossfadeIndex);
f32 yOffsetCount = GetAveragedBuildingCountForIndex(yCrossfadeIndex);
f32 diagOffsetCount = GetAveragedBuildingCountForIndex(diagonalCrossfadeIndex);
f32 baseXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(centreCount, xOffsetCount, 0.0f, 1.0f, xRatio);
f32 yOffsetXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(yOffsetCount, diagOffsetCount, 0.0f, 1.0f, xRatio);
f32 finalCount = audCurveRepository::GetLinearInterpolatedValue(baseXOffsetCount, yOffsetXOffsetCount, 0.0f, 1.0f, yRatio);
f32 buldingDensity = m_BuildingNumToDensityFactor.CalculateValue(finalCount);
f32 timeSmoothedDensity = m_BuildingDensitySmoother.CalculateValue(buldingDensity, audNorthAudioEngine::GetCurrentTimeInMs());
m_BuildingDensityFactor = timeSmoothedDensity;
f32 directionalBuildingDensity[4];
directionalBuildingDensity[0] = GetAveragedBuildingCountFromXYOffset(0.0f+ratioCtoR, g_NSEW_Offset+ratioCtoU);
directionalBuildingDensity[1] = GetAveragedBuildingCountFromXYOffset(g_NSEW_Offset+ratioCtoR, 0.0f+ratioCtoU);
directionalBuildingDensity[2] = GetAveragedBuildingCountFromXYOffset(0.0f+ratioCtoR, -g_NSEW_Offset+ratioCtoU);
directionalBuildingDensity[3] = GetAveragedBuildingCountFromXYOffset(-g_NSEW_Offset+ratioCtoR, 0.0f+ratioCtoU);
for (u32 i=0; i<4; i++)
{
m_DirectionalBuildingDensity[i] = m_BuildingDensityDirectionalSmoother[i].CalculateValue(m_BuildingNumToDensityFactor.CalculateValue(directionalBuildingDensity[i]), audNorthAudioEngine::GetCurrentTimeInMs());
}
}
void naEnvironment::UpdateTreeDensityMetrics(f32 ratioCtoR, f32 ratioCtoU, u32 xCrossfadeIndex, u32 yCrossfadeIndex, u32 diagonalCrossfadeIndex)
{
m_TreeDensitySmoother.SetRate(g_BuiltUpSmootherRate);
f32 xRatio = Abs(ratioCtoR);
f32 yRatio = Abs(ratioCtoU);
f32 centreCount = GetAveragedTreeCountForIndex(AUD_CENTER_SECTOR);
f32 xOffsetCount = GetAveragedTreeCountForIndex(xCrossfadeIndex);
f32 yOffsetCount = GetAveragedTreeCountForIndex(yCrossfadeIndex);
f32 diagOffsetCount = GetAveragedTreeCountForIndex(diagonalCrossfadeIndex);
f32 baseXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(centreCount, xOffsetCount, 0.0f, 1.0f, xRatio);
f32 yOffsetXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(yOffsetCount, diagOffsetCount, 0.0f, 1.0f, xRatio);
f32 finalCount = audCurveRepository::GetLinearInterpolatedValue(baseXOffsetCount, yOffsetXOffsetCount, 0.0f, 1.0f, yRatio);
f32 treeDensity = m_TreeNumToDensityFactor.CalculateValue(finalCount);
f32 timeSmoothedDensity = m_TreeDensitySmoother.CalculateValue(treeDensity, audNorthAudioEngine::GetCurrentTimeInMs());
m_TreeDensityFactor = timeSmoothedDensity;
f32 directionalTreeDensity[4];
directionalTreeDensity[0] = GetAveragedTreeCountFromXYOffset(0.0f+ratioCtoR, g_NSEW_Offset+ratioCtoU);
directionalTreeDensity[1] = GetAveragedTreeCountFromXYOffset(g_NSEW_Offset+ratioCtoR, 0.0f+ratioCtoU);
directionalTreeDensity[2] = GetAveragedTreeCountFromXYOffset(0.0f+ratioCtoR, -g_NSEW_Offset+ratioCtoU);
directionalTreeDensity[3] = GetAveragedTreeCountFromXYOffset(-g_NSEW_Offset+ratioCtoR, 0.0f+ratioCtoU);
for (u32 i=0; i<4; i++)
{
m_DirectionalTreeDensity[i] = m_TreeDensityDirectionalSmoother[i].CalculateValue(m_TreeNumToDensityFactor.CalculateValue(directionalTreeDensity[i]), audNorthAudioEngine::GetCurrentTimeInMs());
}
}
void naEnvironment::UpdateWaterMetrics(f32 ratioCtoR, f32 ratioCtoU, u32 xCrossfadeIndex, u32 yCrossfadeIndex, u32 diagonalCrossfadeIndex)
{
m_WaterSmoother.SetRate(g_BuiltUpSmootherRate);
f32 xRatio = Abs(ratioCtoR);
f32 yRatio = Abs(ratioCtoU);
f32 centreCount = GetAveragedWaterAmountForIndex(AUD_CENTER_SECTOR);
f32 xOffsetCount = GetAveragedWaterAmountForIndex(xCrossfadeIndex);
f32 yOffsetCount = GetAveragedWaterAmountForIndex(yCrossfadeIndex);
f32 diagOffsetCount = GetAveragedWaterAmountForIndex(diagonalCrossfadeIndex);
f32 baseXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(centreCount, xOffsetCount, 0.0f, 1.0f, xRatio);
f32 yOffsetXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(yOffsetCount, diagOffsetCount, 0.0f, 1.0f, xRatio);
f32 finalCount = audCurveRepository::GetLinearInterpolatedValue(baseXOffsetCount, yOffsetXOffsetCount, 0.0f, 1.0f, yRatio);
f32 waterFactor = m_WaterAmountToWaterFactor.CalculateValue(finalCount);
f32 timeSmoothedFactor = m_WaterSmoother.CalculateValue(waterFactor, audNorthAudioEngine::GetCurrentTimeInMs());
m_WaterFactor = timeSmoothedFactor;
f32 directionalWaterAmount[4];
directionalWaterAmount[0] = GetAveragedWaterAmountFromXYOffset(0.0f+ratioCtoR, g_NSEW_Offset+ratioCtoU);
directionalWaterAmount[1] = GetAveragedWaterAmountFromXYOffset(g_NSEW_Offset+ratioCtoR, 0.0f+ratioCtoU);
directionalWaterAmount[2] = GetAveragedWaterAmountFromXYOffset(0.0f+ratioCtoR, -g_NSEW_Offset+ratioCtoU);
directionalWaterAmount[3] = GetAveragedWaterAmountFromXYOffset(-g_NSEW_Offset+ratioCtoR, 0.0f+ratioCtoU);
for (u32 i=0; i<4; i++)
{
m_DirectionalWaterFactor[i] = m_WaterDirectionalSmoother[i].CalculateValue(m_WaterAmountToWaterFactor.CalculateValue(directionalWaterAmount[i]), audNorthAudioEngine::GetCurrentTimeInMs());
}
}
void naEnvironment::UpdateHighwayMetrics(f32 ratioCtoR, f32 ratioCtoU, u32 xCrossfadeIndex, u32 yCrossfadeIndex, u32 diagonalCrossfadeIndex)
{
m_HighwaySmoother.SetRate(g_BuiltUpSmootherRate);
f32 xRatio = Abs(ratioCtoR);
f32 yRatio = Abs(ratioCtoU);
f32 centreCount = GetAveragedHighwayAmountForIndex(AUD_CENTER_SECTOR);
f32 xOffsetCount = GetAveragedHighwayAmountForIndex(xCrossfadeIndex);
f32 yOffsetCount = GetAveragedHighwayAmountForIndex(yCrossfadeIndex);
f32 diagOffsetCount = GetAveragedHighwayAmountForIndex(diagonalCrossfadeIndex);
f32 baseXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(centreCount, xOffsetCount, 0.0f, 1.0f, xRatio);
f32 yOffsetXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(yOffsetCount, diagOffsetCount, 0.0f, 1.0f, xRatio);
f32 finalCount = audCurveRepository::GetLinearInterpolatedValue(baseXOffsetCount, yOffsetXOffsetCount, 0.0f, 1.0f, yRatio);
f32 highwayFactor = m_HighwayAmountToHighwayFactor.CalculateValue(finalCount);
f32 timeSmoothedFactor = m_HighwaySmoother.CalculateValue(highwayFactor, audNorthAudioEngine::GetCurrentTimeInMs());
m_HighwayFactor = timeSmoothedFactor;
f32 directionalHighwayAmount[4];
directionalHighwayAmount[0] = GetAveragedHighwayAmountFromXYOffset(0.0f+ratioCtoR, g_NSEW_Offset+ratioCtoU);
directionalHighwayAmount[1] = GetAveragedHighwayAmountFromXYOffset(g_NSEW_Offset+ratioCtoR, 0.0f+ratioCtoU);
directionalHighwayAmount[2] = GetAveragedHighwayAmountFromXYOffset(0.0f+ratioCtoR, -g_NSEW_Offset+ratioCtoU);
directionalHighwayAmount[3] = GetAveragedHighwayAmountFromXYOffset(-g_NSEW_Offset+ratioCtoR, 0.0f+ratioCtoU);
for (u32 i=0; i<4; i++)
{
m_DirectionalHighwayFactor[i] = m_HighwayDirectionalSmoother[i].CalculateValue(m_HighwayAmountToHighwayFactor.CalculateValue(directionalHighwayAmount[i]), audNorthAudioEngine::GetCurrentTimeInMs());
}
}
f32 naEnvironment::GetBuildingHeightForSector(u32 sectorX, u32 sectorY)
{
if(sectorX < g_audNumWorldSectorsX && sectorY < g_audNumWorldSectorsY)
{
const u32 sector = sectorX + (sectorY*g_audNumWorldSectorsX);
return GetBuildingHeightForSector(sector);
// rain.x = g_audWidthOfSector * (sectorX - (g_fAudNumWorldSectorsX/2.f));
// rain.y = g_audDepthOfSector * (sectorY - (g_fAudNumWorldSectorsY/2.f));
}
else
{
// invalid sector - default to water (since the map is surrounded by water)
return 0.0f;
}
}
f32 naEnvironment::GetBuildingCountForSector(u32 sectorX, u32 sectorY)
{
if(sectorX < g_audNumWorldSectorsX && sectorY < g_audNumWorldSectorsY)
{
const u32 sector = sectorX + (sectorY*g_audNumWorldSectorsX);
return GetBuildingCountForSector(sector);
}
else
{
// invalid sector - default to water (since the map is surrounded by water)
return 0.0f;
}
}
f32 naEnvironment::GetTreeCountForSector(u32 sectorX, u32 sectorY)
{
if(sectorX < g_audNumWorldSectorsX && sectorY < g_audNumWorldSectorsY)
{
const u32 sector = sectorX + (sectorY*g_audNumWorldSectorsX);
return GetTreeCountForSector(sector);
}
else
{
// invalid sector - default to water (since the map is surrounded by water)
return 0.0f;
}
}
f32 naEnvironment::GetWaterAmountForSector(u32 sectorX, u32 sectorY)
{
if(sectorX < g_audNumWorldSectorsX && sectorY < g_audNumWorldSectorsY)
{
const u32 sector = sectorX + (sectorY*g_audNumWorldSectorsX);
return GetWaterAmountForSector(sector);
}
else
{
// invalid sector - default to water (since the map is surrounded by water)
return 1.0f;
}
}
f32 naEnvironment::GetHighwayAmountForSector(u32 sectorX, u32 sectorY)
{
if(sectorX < g_audNumWorldSectorsX && sectorY < g_audNumWorldSectorsY)
{
const u32 sector = sectorX + (sectorY*g_audNumWorldSectorsX);
return GetHighwayAmountForSector(sector);
}
else
{
// invalid sector
return 0.0f;
}
}
f32 naEnvironment::GetAveragedBuildingHeightForIndex(u32 index)
{
// Straight average the nine heights around the requested one - we can maybe do better than this, and bias to the centre
f32 totalHeight = 0.0f;
for (s32 i=-AUD_NUM_SECTORS_ACROSS; i<=AUD_NUM_SECTORS_ACROSS; i+=AUD_NUM_SECTORS_ACROSS)
{
for (s32 j=-1; j<2; j++)
{
s32 finalIndex = index+j+i;
if (finalIndex >= 0 && finalIndex < AUD_NUM_SECTORS)
{
totalHeight += m_BuildingHeights[index+j+i];
}
}
}
f32 averageHeight = totalHeight/9.0f;
return averageHeight;
}
f32 naEnvironment::GetAveragedBuildingCountForIndex(u32 index)
{
// Straight average the nine counts around the requested one - we can maybe do better than this, and bias to the centre
f32 totalCount = 0.0f;
for (s32 i=-AUD_NUM_SECTORS_ACROSS; i<=AUD_NUM_SECTORS_ACROSS; i+=AUD_NUM_SECTORS_ACROSS)
{
for (s32 j=-1; j<2; j++)
{
s32 finalIndex = index+j+i;
if (finalIndex >= 0 && finalIndex < AUD_NUM_SECTORS)
{
totalCount += m_BuildingCount[index+j+i];
}
}
}
f32 averageCount = totalCount/9.0f;
return averageCount;
}
f32 naEnvironment::GetAveragedTreeCountForIndex(u32 index)
{
// Straight average the nine counts around the requested one - we can maybe do better than this, and bias to the centre
f32 totalCount = 0.0f;
for (s32 i=-AUD_NUM_SECTORS_ACROSS; i<=AUD_NUM_SECTORS_ACROSS; i+=AUD_NUM_SECTORS_ACROSS)
{
for (s32 j=-1; j<2; j++)
{
s32 finalIndex = index+j+i;
if (finalIndex >= 0 && finalIndex < AUD_NUM_SECTORS)
{
totalCount += m_TreeCount[index+j+i];
}
}
}
f32 averageCount = totalCount/9.0f;
return averageCount;
}
f32 naEnvironment::GetAveragedWaterAmountForIndex(u32 index)
{
// Straight average the nine counts around the requested one - we can maybe do better than this, and bias to the centre
f32 totalCount = 0.0f;
for (s32 i=-AUD_NUM_SECTORS_ACROSS; i<=AUD_NUM_SECTORS_ACROSS; i+=AUD_NUM_SECTORS_ACROSS)
{
for (s32 j=-1; j<2; j++)
{
s32 finalIndex = index+j+i;
if (finalIndex >= 0 && finalIndex < AUD_NUM_SECTORS)
{
totalCount += m_WaterAmount[index+j+i];
}
}
}
f32 averageCount = totalCount/9.0f;
return averageCount;
}
f32 naEnvironment::GetAveragedHighwayAmountForIndex(u32 index)
{
// Straight average the nine counts around the requested one - we can maybe do better than this, and bias to the centre
f32 totalCount = 0.0f;
for (s32 i=-AUD_NUM_SECTORS_ACROSS; i<=AUD_NUM_SECTORS_ACROSS; i+=AUD_NUM_SECTORS_ACROSS)
{
for (s32 j=-1; j<2; j++)
{
s32 finalIndex = index+j+i;
if (finalIndex >= 0 && finalIndex < AUD_NUM_SECTORS)
{
totalCount += m_HighwayAmount[index+j+i];
}
}
}
f32 averageCount = totalCount/9.0f;
return averageCount;
}
f32 naEnvironment::GetAveragedBuildingHeightFromXYOffset(f32 xOffset, f32 yOffset)
{
// Check we're not straying out of bounds
// valid situation now that the player can drive outside the world
// it returns 0.0 (the height of the water) so everything should be fine
// Assert(Abs(xOffset)<(f32)(AUD_HALF_NUM_SECTORS_ACROSS));
// Assert(Abs(yOffset)<(f32)(AUD_HALF_NUM_SECTORS_ACROSS));
if (Abs(xOffset)>=(f32)(AUD_HALF_NUM_SECTORS_ACROSS) || Abs(yOffset)>=(f32)(AUD_HALF_NUM_SECTORS_ACROSS))
{
return 0.0f;
}
// Work out which main square we're in, then which ones to combine, and by how much
u32 primaryXIndexOffset = (u32)Abs(xOffset);
f32 primaryToSecondaryXRatio = 1.0f - (Abs(xOffset) - (f32)primaryXIndexOffset);
u32 primaryYIndexOffset = (u32)Abs(yOffset);
f32 primaryToSecondaryYRatio = 1.0f - (Abs(yOffset) - (f32)primaryYIndexOffset);
u32 primarySquare = AUD_CENTER_SECTOR + primaryXIndexOffset*(xOffset>0.0f?1:-1) +
(primaryYIndexOffset*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 secondaryXSquare = AUD_CENTER_SECTOR + (primaryXIndexOffset+1)*(xOffset>0.0f?1:-1) +
(primaryYIndexOffset*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 secondaryYSquare = AUD_CENTER_SECTOR + primaryXIndexOffset*(xOffset>0.0f?1:-1) +
((primaryYIndexOffset+1)*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 diagSquare = AUD_CENTER_SECTOR + (primaryXIndexOffset+1)*(xOffset>0.0f?1:-1) +
((primaryYIndexOffset+1)*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
f32 primaryHeight = GetAveragedBuildingHeightForIndex(primarySquare);
f32 xOffsetHeight = GetAveragedBuildingHeightForIndex(secondaryXSquare);
f32 yOffsetHeight = GetAveragedBuildingHeightForIndex(secondaryYSquare);
f32 diagOffsetHeight = GetAveragedBuildingHeightForIndex(diagSquare);
f32 baseXOffsetHeight = audCurveRepository::GetLinearInterpolatedValue(xOffsetHeight, primaryHeight, 0.0f, 1.0f, primaryToSecondaryXRatio);
f32 yOffsetXOffsetHeight = audCurveRepository::GetLinearInterpolatedValue(diagOffsetHeight, yOffsetHeight, 0.0f, 1.0f, primaryToSecondaryXRatio);
f32 finalHeight = audCurveRepository::GetLinearInterpolatedValue(yOffsetXOffsetHeight, baseXOffsetHeight, 0.0f, 1.0f, primaryToSecondaryYRatio);
return finalHeight;
}
f32 naEnvironment::GetAveragedBuildingCountFromXYOffset(f32 xOffset, f32 yOffset)
{
// Check we're not straying out of bounds
// valid situation now that the player can drive outside the world
// it returns 0.0 (the height of the water) so everything should be fine
// Assert(Abs(xOffset)<(f32)(AUD_HALF_NUM_SECTORS_ACROSS));
// Assert(Abs(yOffset)<(f32)(AUD_HALF_NUM_SECTORS_ACROSS));
if (Abs(xOffset)>=(f32)(AUD_HALF_NUM_SECTORS_ACROSS) || Abs(yOffset)>=(f32)(AUD_HALF_NUM_SECTORS_ACROSS))
{
return 0.0f;
}
// Work out which main square we're in, then which ones to combine, and by how much
u32 primaryXIndexOffset = (u32)Abs(xOffset);
f32 primaryToSecondaryXRatio = 1.0f - (Abs(xOffset) - (f32)primaryXIndexOffset);
u32 primaryYIndexOffset = (u32)Abs(yOffset);
f32 primaryToSecondaryYRatio = 1.0f - (Abs(yOffset) - (f32)primaryYIndexOffset);
u32 primarySquare = AUD_CENTER_SECTOR + primaryXIndexOffset*(xOffset>0.0f?1:-1) +
(primaryYIndexOffset*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 secondaryXSquare = AUD_CENTER_SECTOR + (primaryXIndexOffset+1)*(xOffset>0.0f?1:-1) +
(primaryYIndexOffset*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 secondaryYSquare = AUD_CENTER_SECTOR + primaryXIndexOffset*(xOffset>0.0f?1:-1) +
((primaryYIndexOffset+1)*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 diagSquare = AUD_CENTER_SECTOR + (primaryXIndexOffset+1)*(xOffset>0.0f?1:-1) +
((primaryYIndexOffset+1)*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
f32 primaryCount = GetAveragedBuildingCountForIndex(primarySquare);
f32 xOffsetCount = GetAveragedBuildingCountForIndex(secondaryXSquare);
f32 yOffsetCount = GetAveragedBuildingCountForIndex(secondaryYSquare);
f32 diagOffsetCount = GetAveragedBuildingCountForIndex(diagSquare);
f32 baseXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(xOffsetCount, primaryCount, 0.0f, 1.0f, primaryToSecondaryXRatio);
f32 yOffsetXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(diagOffsetCount, yOffsetCount, 0.0f, 1.0f, primaryToSecondaryXRatio);
f32 finalCount = audCurveRepository::GetLinearInterpolatedValue(yOffsetXOffsetCount, baseXOffsetCount, 0.0f, 1.0f, primaryToSecondaryYRatio);
return finalCount;
}
f32 naEnvironment::GetAveragedTreeCountFromXYOffset(f32 xOffset, f32 yOffset)
{
// Check we're not straying out of bounds
// valid situation now that the player can drive outside the world
// it returns 0.0 (the height of the water) so everything should be fine
// Assert(Abs(xOffset)<(f32)(AUD_HALF_NUM_SECTORS_ACROSS));
// Assert(Abs(yOffset)<(f32)(AUD_HALF_NUM_SECTORS_ACROSS));
if (Abs(xOffset)>=(f32)(AUD_HALF_NUM_SECTORS_ACROSS) || Abs(yOffset)>=(f32)(AUD_HALF_NUM_SECTORS_ACROSS))
{
return 0.0f;
}
// Work out which main square we're in, then which ones to combine, and by how much
u32 primaryXIndexOffset = (u32)Abs(xOffset);
f32 primaryToSecondaryXRatio = 1.0f - (Abs(xOffset) - (f32)primaryXIndexOffset);
u32 primaryYIndexOffset = (u32)Abs(yOffset);
f32 primaryToSecondaryYRatio = 1.0f - (Abs(yOffset) - (f32)primaryYIndexOffset);
u32 primarySquare = AUD_CENTER_SECTOR + primaryXIndexOffset*(xOffset>0.0f?1:-1) +
(primaryYIndexOffset*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 secondaryXSquare = AUD_CENTER_SECTOR + (primaryXIndexOffset+1)*(xOffset>0.0f?1:-1) +
(primaryYIndexOffset*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 secondaryYSquare = AUD_CENTER_SECTOR + primaryXIndexOffset*(xOffset>0.0f?1:-1) +
((primaryYIndexOffset+1)*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 diagSquare = AUD_CENTER_SECTOR + (primaryXIndexOffset+1)*(xOffset>0.0f?1:-1) +
((primaryYIndexOffset+1)*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
f32 primaryCount = GetAveragedTreeCountForIndex(primarySquare);
f32 xOffsetCount = GetAveragedTreeCountForIndex(secondaryXSquare);
f32 yOffsetCount = GetAveragedTreeCountForIndex(secondaryYSquare);
f32 diagOffsetCount = GetAveragedTreeCountForIndex(diagSquare);
f32 baseXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(xOffsetCount, primaryCount, 0.0f, 1.0f, primaryToSecondaryXRatio);
f32 yOffsetXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(diagOffsetCount, yOffsetCount, 0.0f, 1.0f, primaryToSecondaryXRatio);
f32 finalCount = audCurveRepository::GetLinearInterpolatedValue(yOffsetXOffsetCount, baseXOffsetCount, 0.0f, 1.0f, primaryToSecondaryYRatio);
return finalCount;
}
f32 naEnvironment::GetAveragedWaterAmountFromXYOffset(f32 xOffset, f32 yOffset)
{
// Check we're not straying out of bounds
// valid situation now that the player can drive outside the world
// it returns 0.0 (the height of the water) so everything should be fine
// Assert(Abs(xOffset)<(f32)(AUD_HALF_NUM_SECTORS_ACROSS));
// Assert(Abs(yOffset)<(f32)(AUD_HALF_NUM_SECTORS_ACROSS));
if (Abs(xOffset)>=(f32)(AUD_HALF_NUM_SECTORS_ACROSS) || Abs(yOffset)>=(f32)(AUD_HALF_NUM_SECTORS_ACROSS))
{
return 0.0f;
}
// Work out which main square we're in, then which ones to combine, and by how much
u32 primaryXIndexOffset = (u32)Abs(xOffset);
f32 primaryToSecondaryXRatio = 1.0f - (Abs(xOffset) - (f32)primaryXIndexOffset);
u32 primaryYIndexOffset = (u32)Abs(yOffset);
f32 primaryToSecondaryYRatio = 1.0f - (Abs(yOffset) - (f32)primaryYIndexOffset);
u32 primarySquare = AUD_CENTER_SECTOR + primaryXIndexOffset*(xOffset>0.0f?1:-1) +
(primaryYIndexOffset*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 secondaryXSquare = AUD_CENTER_SECTOR + (primaryXIndexOffset+1)*(xOffset>0.0f?1:-1) +
(primaryYIndexOffset*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 secondaryYSquare = AUD_CENTER_SECTOR + primaryXIndexOffset*(xOffset>0.0f?1:-1) +
((primaryYIndexOffset+1)*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 diagSquare = AUD_CENTER_SECTOR + (primaryXIndexOffset+1)*(xOffset>0.0f?1:-1) +
((primaryYIndexOffset+1)*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
f32 primaryCount = GetAveragedWaterAmountForIndex(primarySquare);
f32 xOffsetCount = GetAveragedWaterAmountForIndex(secondaryXSquare);
f32 yOffsetCount = GetAveragedWaterAmountForIndex(secondaryYSquare);
f32 diagOffsetCount = GetAveragedWaterAmountForIndex(diagSquare);
f32 baseXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(xOffsetCount, primaryCount, 0.0f, 1.0f, primaryToSecondaryXRatio);
f32 yOffsetXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(diagOffsetCount, yOffsetCount, 0.0f, 1.0f, primaryToSecondaryXRatio);
f32 finalCount = audCurveRepository::GetLinearInterpolatedValue(yOffsetXOffsetCount, baseXOffsetCount, 0.0f, 1.0f, primaryToSecondaryYRatio);
return finalCount;
}
f32 naEnvironment::GetAveragedHighwayAmountFromXYOffset(f32 xOffset, f32 yOffset)
{
// Check we're not straying out of bounds
// valid situation now that the player can drive outside the world
// it returns 0.0 (the height of the water) so everything should be fine
// Assert(Abs(xOffset)<(f32)(AUD_HALF_NUM_SECTORS_ACROSS));
// Assert(Abs(yOffset)<(f32)(AUD_HALF_NUM_SECTORS_ACROSS));
if (Abs(xOffset)>=(f32)(AUD_HALF_NUM_SECTORS_ACROSS) || Abs(yOffset)>=(f32)(AUD_HALF_NUM_SECTORS_ACROSS))
{
return 0.0f;
}
// Work out which main square we're in, then which ones to combine, and by how much
u32 primaryXIndexOffset = (u32)Abs(xOffset);
f32 primaryToSecondaryXRatio = 1.0f - (Abs(xOffset) - (f32)primaryXIndexOffset);
u32 primaryYIndexOffset = (u32)Abs(yOffset);
f32 primaryToSecondaryYRatio = 1.0f - (Abs(yOffset) - (f32)primaryYIndexOffset);
u32 primarySquare = AUD_CENTER_SECTOR + primaryXIndexOffset*(xOffset>0.0f?1:-1) +
(primaryYIndexOffset*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 secondaryXSquare = AUD_CENTER_SECTOR + (primaryXIndexOffset+1)*(xOffset>0.0f?1:-1) +
(primaryYIndexOffset*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 secondaryYSquare = AUD_CENTER_SECTOR + primaryXIndexOffset*(xOffset>0.0f?1:-1) +
((primaryYIndexOffset+1)*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
u32 diagSquare = AUD_CENTER_SECTOR + (primaryXIndexOffset+1)*(xOffset>0.0f?1:-1) +
((primaryYIndexOffset+1)*(yOffset<0.0f?1:-1)*AUD_NUM_SECTORS_ACROSS);
f32 primaryCount = GetAveragedHighwayAmountForIndex(primarySquare);
f32 xOffsetCount = GetAveragedHighwayAmountForIndex(secondaryXSquare);
f32 yOffsetCount = GetAveragedHighwayAmountForIndex(secondaryYSquare);
f32 diagOffsetCount = GetAveragedHighwayAmountForIndex(diagSquare);
f32 baseXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(xOffsetCount, primaryCount, 0.0f, 1.0f, primaryToSecondaryXRatio);
f32 yOffsetXOffsetCount = audCurveRepository::GetLinearInterpolatedValue(diagOffsetCount, yOffsetCount, 0.0f, 1.0f, primaryToSecondaryXRatio);
f32 finalCount = audCurveRepository::GetLinearInterpolatedValue(yOffsetXOffsetCount, baseXOffsetCount, 0.0f, 1.0f, primaryToSecondaryYRatio);
return finalCount;
}
void naEnvironment::MapDirectionalMetricsToSpeakers()
{
Matrix34 listenerMatrix = MAT34V_TO_MATRIX34(g_AudioEngine.GetEnvironment().GetPanningListenerMatrix());
static Vector3 directions[4];
directions[0] = Vector3(1.0f, 0.0f, 0.0f); //N
directions[1] = Vector3(0.0f, -1.0f, 0.0f); //E
directions[2] = Vector3(-1.0f, 0.0f, 0.0f); //S
directions[3] = Vector3(0.0f, 1.0f, 0.0f); //W
Vector3 speakerPosition[4]; // these are in some different space, where 1,0,0 is forwards, and 0,-1,0 is right.
speakerPosition[0] = Vector3(0.7071f, 0.7071f, 0.0f); // FL
speakerPosition[1] = Vector3(0.7071f, -0.7071f, 0.0f); // FR
speakerPosition[2] = Vector3(-0.7071f, 0.7071f, 0.0f); // RL
speakerPosition[3] = Vector3(-0.7071f, -0.7071f, 0.0f); // RR
// we're always NESW from that, so ignore any positional aspect
listenerMatrix.d.Zero();
// This does the same clipped dot-product followed by elevation leakage that the panning algorithm and listener reverb do.
// quick elevation check - will be the same elevation for all directions
f32 elevation = Abs(listenerMatrix.c.z); // 1 for not at all elevated, 0 for fully
f32 speakerBuiltUpFactor[4];
f32 speakerBlockedFactor[4];
f32 speakerBlockedLinearVolume[4];
f32 speakerOWOFactor[4];
for (u32 i=0; i<4; i++) // 4 speakers
{
speakerBuiltUpFactor[i] = 0.0f;
speakerBlockedFactor[i] = 0.0f;
speakerBlockedLinearVolume[i] = 0.0f;
speakerOWOFactor[i] = 0.0f;
for (u32 j=0; j<4; j++) // 4 directional metrics
{
// Where is the poly relative to us
Vector3 directionRelative = directions[j];
listenerMatrix.UnTransform(directionRelative);
Vector3 positionOnUnitCircle = directionRelative;
positionOnUnitCircle.z = 0.0f;
positionOnUnitCircle.NormalizeSafe();
f32 dotProd = positionOnUnitCircle.Dot(speakerPosition[i]);
// Don't care about ones more than 90degs away
dotProd = Max(0.0f, dotProd);
// Scale our contribution by the elevation
f32 contribution = ((1.0f - elevation) * (1.0f/4.0f)) + (elevation * dotProd * dotProd);
speakerBuiltUpFactor[i] += (contribution * m_DirectionalBuiltUpFactor[j]);
f32 exteriorOcclusionInDirection = audNorthAudioEngine::GetOcclusionManager()->GetExteriorOcclusionForDirection((audAmbienceDirection)j);
speakerBlockedFactor[i] += (contribution * audNorthAudioEngine::GetOcclusionManager()->GetBlockedFactorForDirection((audAmbienceDirection)j));
speakerBlockedLinearVolume[i] += (contribution * audNorthAudioEngine::GetOcclusionManager()->GetBlockedLinearVolumeForDirection((audAmbienceDirection)j));
speakerOWOFactor[i] += (contribution * exteriorOcclusionInDirection);
}
m_SpeakerBuiltUpFactors[i] = m_SpeakerBuiltUpSmoother[i].CalculateValue(speakerBuiltUpFactor[i], audNorthAudioEngine::GetCurrentTimeInMs());
m_SpeakerBlockedFactor[i] = speakerBlockedFactor[i];
m_SpeakerBlockedLinearVolume[i] = speakerBlockedLinearVolume[i];
m_SpeakerOWOFactor[i] = speakerOWOFactor[i];
}
#if __BANK
if (g_DebugBuiltUpInfo)
{
char builtUpFactor[256] = "";
sprintf(builtUpFactor, "el: %f; FL: %f; FR: %f; RL: %f; RR: %f", elevation, speakerBuiltUpFactor[0], speakerBuiltUpFactor[1], speakerBuiltUpFactor[2], speakerBuiltUpFactor[3]);
grcDebugDraw::PrintToScreenCoors(builtUpFactor, 32,37);
}
#endif
g_AudioEngine.GetEnvironment().SetBuiltUpSpeakerFactors(m_SpeakerBuiltUpFactors);
g_AudioEngine.GetEnvironment().SetBlockedSpeakerFactors(m_SpeakerBlockedFactor);
g_AudioEngine.GetEnvironment().SetBlockedSpeakerLinearVolumes(m_SpeakerBlockedLinearVolume);
}
f32 naEnvironment::GetBuildingHeightForSector(const u32 sector) const
{
return (f32)(g_AudioWorldSectors[sector].tallestBuilding);
}
f32 naEnvironment::GetBuildingCountForSector(const u32 sector) const
{
return (f32)(g_AudioWorldSectors[sector].numBuildings);
}
f32 naEnvironment::GetTreeCountForSector(const u32 sector) const
{
return (f32)(g_AudioWorldSectors[sector].numTrees);
}
f32 naEnvironment::GetWaterAmountForSector(const u32 sector) const
{
if(g_AudioWorldSectors[sector].isWaterSector &&
g_AudioWorldSectors[sector].numTrees == 0 &&
g_AudioWorldSectors[sector].numBuildings == 0)
{
return 1.0f;
}
return 0.0f;
}
f32 naEnvironment::GetHighwayAmountForSector(const u32 sector) const
{
return (f32)g_AudioWorldSectors[sector].numHighwayNodes;
}
f32 naEnvironment::GetRollOffFactor()
{
return m_BuiltUpToRolloff.CalculateValue(g_AudioEngine.GetEnvironment().GetBuiltUpFactor());
}
bool naEnvironment::IsCameraShelteredFromRain() const
{
// only return true when all probes are colliding
for(u32 i = 0 ; i < VFXWEATHER_NUM_WEATHER_PROBES; i++)
{
if(!g_vfxWeather.GetWeatherProbeResult(i))
{
return false;
}
}
return true;
}
f32 naEnvironment::GetSpeakerReverbWet(u32 speaker, u32 reverb)
{
if(naVerifyf(speaker < 4, "Invalid speaker passed into GetSpeakerReverbWet; out of bounds")
&& naVerifyf(reverb < 3, "Invalid reverb passed into GetSpeakerReverbWet; out of bounds"))
{
return m_SpeakerReverbWets[speaker][reverb];
}
return 0.f;
}
CEntity* naEnvironment::GetInterestingLocalObject(u32 index)
{
if(naVerifyf(index < g_MaxInterestingLocalObjects, "Invalid index passed into GetInterestingLocalObject; out of bounds"))
{
return m_InterestingLocalObjects[index].object;
}
return NULL;
}
const CEntity* naEnvironment::GetInterestingLocalObject(u32 index) const
{
if(naVerifyf(index < g_MaxInterestingLocalObjects, "Invalid index passed into GetInterestingLocalObject; out of bounds"))
{
return m_InterestingLocalObjects[index].object;
}
return NULL;
}
void naEnvironment::GetInterestingLocalObjectInfo(u32 index,audInterestingObjectInfo &objectInfo)
{
if(naVerifyf(index < g_MaxInterestingLocalObjects, "Invalid index passed into GetInterestingLocalObject; out of bounds"))
{
objectInfo = m_InterestingLocalObjects[index];
}
}
const audInterestingObjectInfo *naEnvironment::GetInterestingLocalObjectInfo(u32 index) const
{
if(naVerifyf(index < g_MaxInterestingLocalObjects, "Invalid index passed into GetInterestingLocalObject; out of bounds"))
{
return &m_InterestingLocalObjects[index];
}
return NULL;
}
const ModelAudioCollisionSettings * naEnvironment::GetInterestingLocalObjectMaterial(u32 index) const
{
const audInterestingObjectInfo *objectInfo = GetInterestingLocalObjectInfo(index);
if(objectInfo && objectInfo->object)
{
if(objectInfo->macsComponent == -1)
{
return (objectInfo->object->GetBaseModelInfo()->GetAudioCollisionSettings());
}
else
{
return audCollisionAudioEntity::GetFragComponentMaterialSettings(objectInfo->object,(u32)objectInfo->macsComponent);
}
}
return NULL;
}
CEntity *naEnvironment::GetInterestingLocalObjectCached(u32 index)
{
if(naVerifyf(index < m_NumInterestingLocalObjectsCached, "Invalid index passed into GetInterestingLocalObjectCached; out of bounds"))
{
return m_InterestingLocalObjectsCached[index].object;
}
return NULL;
}
naEnvironmentGroup *naEnvironment::GetInterestingLocalEnvGroup(u32 index)
{
if(naVerifyf(index < g_MaxInterestingLocalObjects, "Invalid index passed into GetInterestingLocalEnvGroupCached; out of bounds"))
{
return m_InterestingLocalObjects[index].envGroup;
}
return NULL;
}
naEnvironmentGroup *naEnvironment::GetInterestingLocalEnvGroupCached(u32 index)
{
if(naVerifyf(index < m_NumInterestingLocalObjectsCached, "Invalid index passed into GetInterestingLocalEnvGroupCached; out of bounds"))
{
return m_InterestingLocalObjectsCached[index].envGroup;
}
return NULL;
}
u32 naEnvironment::GetNumInterestingLocalObjectsCached() const
{
return m_NumInterestingLocalObjectsCached;
}
bool naEnvironment::IsObjectUnderCover(u32 index)
{
naAssertf(index < g_MaxInterestingLocalObjects, "Invalid index passed into GetInterestingLocalObject; out of bounds");
#if __BANK
if(sm_UseCoverAproximation)
{
return m_InterestingLocalObjects[index].underCover;
}
#endif
if(m_InterestingLocalObjects[index].envGroup)
{
return m_InterestingLocalObjects[index].envGroup->IsUnderCover();
}
return true;
}
void naEnvironment::AddInterestingObject(CEntity *obj,const u32 shockwaveSoundHash, const u32 resoSoundHash,s32 component ,bool /*checkCovered*/)
{
if(naVerifyf(obj, "Null CEntity ptr passed into AddInter estingObject"))
{
naAssertf(obj->GetOwnedBy()!=ENTITY_OWNEDBY_PROCEDURAL, "Found a procedural entity tagged with audio, please bug the audio team.");
if(m_NumInterestingLocalObjectsCached < g_MaxInterestingLocalObjects)
{
naAssertf(!m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached].object, "Trying to add an interesting object but slot is already occupied");
// Set the entity.
m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached].object = obj;
// Before setting anything up, check if we already had the info in the cached list.
s32 objectIndex = -1;
for(s32 i = g_MaxInterestingLocalObjects - 1; i >= 0; i--)
{
if(audNorthAudioEngine::GetGtaEnvironment()->GetInterestingLocalObject(i) == obj)
{
objectIndex = i;
break;
}
}
if( objectIndex != -1 )
{
audNorthAudioEngine::GetGtaEnvironment()->GetInterestingLocalObjectInfo(objectIndex,m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached]);
}
else
{
m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached].macsComponent = component;
m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached].shockwaveSoundInfo.prevVol = 0.0f;
if (shockwaveSoundHash != g_NullSoundHash)
{
bool hasCustomAttackReleaseTime = false;
f32 customAttackTime = 0.f;
f32 customReleaseTime = 0.f;
Sound uncompressedMetadata;
const VariableBlockSound *vbSound = SOUNDFACTORY.DecompressMetadata<VariableBlockSound>(shockwaveSoundHash, uncompressedMetadata);
if (vbSound)
{
for (u32 i = 0; i < vbSound->numVariables; i++)
{
if (vbSound->Variable[i].VariableRef == ATSTRINGHASH("AttackTime", 0xF0104A91))
{
customAttackTime = vbSound->Variable[i].VariableValue;
hasCustomAttackReleaseTime = true;
}
if (vbSound->Variable[i].VariableRef == ATSTRINGHASH("ReleaseTime", 0x19482A71))
{
customReleaseTime = vbSound->Variable[i].VariableValue;
hasCustomAttackReleaseTime = true;
}
}
}
m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached].shockwaveSoundInfo.attackTime = hasCustomAttackReleaseTime ? Max(0.00001f, customAttackTime) : -1.f;
m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached].shockwaveSoundInfo.releaseTime = hasCustomAttackReleaseTime ? Max(0.00001f, customReleaseTime) : -1.f;
}
if(resoSoundHash != g_NullSoundHash)
{
Sound uncompressedMetadata;
const EnvelopeSound *envSound = SOUNDFACTORY.DecompressMetadata<EnvelopeSound>(resoSoundHash, uncompressedMetadata);
if(envSound)
{
m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached].resoSoundInfo.attackTime = (f32)audSound::ApplyVariance(envSound->Envelope.Attack, envSound->Envelope.AttackVariance, 1<<16U);
m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached].resoSoundInfo.attackTime *= 0.001f;
m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached].resoSoundInfo.releaseTime = (f32)audSound::ApplyVariance(envSound->Envelope.Release, envSound->Envelope.ReleaseVariance, 1<<16U);
m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached].resoSoundInfo.releaseTime *= 0.001f;
m_InterestingLocalObjectsCached[m_NumInterestingLocalObjectsCached].resoSoundInfo.resoSoundRef = envSound->SoundRef;
}
}
}
m_NumInterestingLocalObjectsCached ++;
}
}
}
void naEnvironment::AddLocalTree(CEntity *tree)
{
if (tree->GetTransform().GetPosition().GetZf() > m_WaterHeightAtListener && (tree->GetBoundRadius() > sm_BushesMinRadius) && (m_NumLocalTrees < (g_MaxInterestingLocalTrees - 1)))
{
// Add it to the list
m_LocalTrees[m_NumLocalTrees].tree = tree;
// update our backGround tree info.
// First get the sector: FL,FR,RL,RR
Vec3V treePos2D = tree->GetTransform().GetPosition();
treePos2D.SetZ(0);
Vec3V listenerPos2D = g_AudioEngine.GetEnvironment().GetVolumeListenerMatrix().GetCol3();
listenerPos2D.SetZ(0);
Vec3V dirToTree = Subtract(treePos2D, listenerPos2D);
//f32 distance = fabs(((ScalarV)Mag(dirToTree)).Getf());
dirToTree = Normalize(dirToTree);
float frontDot = Dot( Vec3V(0.0f,1.f,0.0f), dirToTree).Getf();
float rightDot = Dot( Vec3V(1.f,0.0f,0.0f), dirToTree).Getf();
audFBSectors sector = AUD_SECTOR_FL;
if((frontDot >= 0.f))
{
if(rightDot >= 0.f)
sector = AUD_SECTOR_FR;
else
sector = AUD_SECTOR_FL;
}
else
{
if(rightDot >= 0.f)
sector = AUD_SECTOR_RR;
else
sector = AUD_SECTOR_RL;
}
m_LocalTrees[m_NumLocalTrees].sector = sector;
if(tree->GetBoundRadius() >= sm_BigTreesMinRadius)
{
m_BackGroundTrees[sector].numBigTrees ++ ;
m_LocalTrees[m_NumLocalTrees ++].type = AUD_BIG_TREES;
}
else if(tree->GetBoundRadius() >= sm_SmallTreesMinRadius)
{
m_BackGroundTrees[sector].numTrees ++ ;
m_LocalTrees[m_NumLocalTrees ++].type = AUD_SMALL_TREES;
}
else
{
m_BackGroundTrees[sector].numBushes ++ ;
m_LocalTrees[m_NumLocalTrees ++].type = AUD_BUSHES;
}
}
}
#if __DEV
void naEnvironment::DebugDraw()
{
}
#endif
#if __BANK
void naEnvironment::CreateRAVEInteriorRoomObject(const char* instanceName, const char* roomName)
{
if(roomName)
{
char xmlMsg[4096];
sprintf(xmlMsg, "<RAVEMessage>\n");
naDisplayf("%s", xmlMsg);
char tmpBuf[256] = {0};
sprintf(tmpBuf, " <EditObjects metadataType=\"GAMEOBJECTS\" chunkNameHash=\"%u\">\n", (u32)audStringHash("BASE"));
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, " <InteriorRoom name=\"%s_%s\">\n", instanceName, roomName);
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, " <RoomName>%s</RoomName>\n", roomName);
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, " <ReverbSmall>%f</ReverbSmall>\n", g_DebugRoomSettingsReverbSmall);
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, " <ReverbMedium>%f</ReverbMedium>\n", g_DebugRoomSettingsReverbMedium);
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, " <ReverbLarge>%f</ReverbLarge>\n", g_DebugRoomSettingsReverbLarge);
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, " </InteriorRoom>\n");
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, " </EditObjects>\n");
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, "</RAVEMessage>\n");
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
naDisplayf("XML message is %" SIZETFMT "d characters long \n", strlen(xmlMsg));
audRemoteControl &rc = g_AudioEngine.GetRemoteControl();
bool success = rc.SendXmlMessage(xmlMsg, istrlen(xmlMsg));
naAssertf(success, "Failed to send xml message to rave");
naDisplayf("%s", (success)? "Success":"Failed");
}
}
void naEnvironment::CreateRAVEObjectsForCurrentInteriorAndRooms()
{
CInteriorInst* pIntInst = CPortalVisTracker::GetPrimaryInteriorInst();
if(pIntInst)
{
for(u32 instanceRoomLoop = 0; instanceRoomLoop < pIntInst->GetNumRooms(); instanceRoomLoop++)
{
const char* instanceName = pIntInst->GetModelName();
const char* roomName = pIntInst->GetRoomName(instanceRoomLoop);
CreateRAVEInteriorRoomObject(instanceName, roomName);
}
CreateRAVEObjectsForCurrentInterior();
}
}
void naEnvironment::CreateRAVEObjectsForCurrentInterior()
{
CInteriorInst* pIntInst = CPortalVisTracker::GetPrimaryInteriorInst();
if(pIntInst)
{
char xmlMsg[4096];
const char* instanceName = pIntInst->GetModelName();
sprintf(xmlMsg, "<RAVEMessage>\n");
naDisplayf("%s", xmlMsg);
char tmpBuf[256] = {0};
sprintf(tmpBuf, " <EditObjects metadataType=\"GAMEOBJECTS\" chunkNameHash=\"%u\">\n", (u32)audStringHash("BASE"));
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, " <InteriorSettings name=\"%s\">\n", instanceName);
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
for(u32 instanceRoomLoop = 0; instanceRoomLoop < pIntInst->GetNumRooms(); instanceRoomLoop++)
{
const char* roomName = pIntInst->GetRoomName(instanceRoomLoop);
sprintf(tmpBuf, " <Room>\n");
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, " <Ref>%s_%s</Ref>\n", instanceName, roomName);
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, " </Room>\n");
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
}
sprintf(tmpBuf, " </InteriorSettings>\n");
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, " </EditObjects>\n");
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
sprintf(tmpBuf, "</RAVEMessage>\n");
naDisplayf("%s", tmpBuf);
strcat(xmlMsg, tmpBuf);
naDisplayf("XML message is %" SIZETFMT "d characters long \n", strlen(xmlMsg));
audRemoteControl &rc = g_AudioEngine.GetRemoteControl();
bool success = rc.SendXmlMessage(xmlMsg, istrlen(xmlMsg));
naAssertf(success, "Failed to send xml message to rave");
naDisplayf("%s", (success)? "Success":"Failed");
}
}
void naEnvironment::CreateRAVEObjectsForCurrentRoom()
{
CInteriorInst* pIntInst = CPortalVisTracker::GetPrimaryInteriorInst();
if(pIntInst)
{
s32 roomIndex = CPortalVisTracker::GetPrimaryRoomIdx();
if(roomIndex < pIntInst->GetNumRooms())
{
const char* instanceName = pIntInst->GetModelName();
const char* roomName = pIntInst->GetRoomName(roomIndex);
CreateRAVEInteriorRoomObject(instanceName, roomName);
}
}
}
void DebugDrawSector(const u32 sectorX, const u32 sectorY)
{
if(sectorX < g_audNumWorldSectorsX && sectorY < g_audNumWorldSectorsY)
{
const u32 sectorIndex = sectorX + (sectorY * g_audNumWorldSectorsX);
Color32 colour(255,0,0);
const f32 x = g_audWidthOfSector * (sectorX - (g_fAudNumWorldSectorsX/2.f));
const f32 y = g_audDepthOfSector * (sectorY - (g_fAudNumWorldSectorsY/2.f));
Vector3 v0,v1,v2,v3;
v0.z = v1.z = v2.z = v3.z = 810.0f;
v0.x = x;
v0.y = y;
v1.x = x + g_audWidthOfSector;
v1.y = y;
v2.x = x;
v2.y = y + g_audDepthOfSector;
v3.x = x + g_audWidthOfSector;
v3.y = y + g_audDepthOfSector;
Vector3 centre = (v0 + v3) * 0.5f;
centre.z = v0.z = v1.z = v2.z = v3.z = 70.f;
if(g_AudioWorldSectors[sectorIndex].isWaterSector)
{
colour = Color32(0,0,255);
}
grcDebugDraw::Line(v0,v1,colour);
grcDebugDraw::Line(v1,v3,colour);
grcDebugDraw::Line(v3,v2,colour);
grcDebugDraw::Line(v2,v0,colour);
char rainDebug[256] = "";
formatf(rainDebug, 255, "%d: numHighwayNodes %d tallestBuilding: %d numBuildings: %d numTrees: %u", sectorIndex, g_AudioWorldSectors[sectorIndex].numHighwayNodes, g_AudioWorldSectors[sectorIndex].tallestBuilding, g_AudioWorldSectors[sectorIndex].numBuildings, g_AudioWorldSectors[sectorIndex].numTrees);
grcDebugDraw::Text(centre, colour, rainDebug);
}
}
void DebugDrawRoadInfo(const u32 sectorX, const u32 sectorY)
{
if(sectorX < g_audNumWorldSectorsX && sectorY < g_audNumWorldSectorsY)
{
const u32 sectorIndex = sectorX + (sectorY * g_audNumWorldSectorsX);
Color32 colour(255,0,0);
const f32 x = g_audWidthOfSector * (sectorX - (g_fAudNumWorldSectorsX/2.f));
const f32 y = g_audDepthOfSector * (sectorY - (g_fAudNumWorldSectorsY/2.f));
Vector3 v0,v1;
v0.x = x;
v0.y = y;
v1.x = x + g_audWidthOfSector;
v1.y = y + g_audDepthOfSector;
f32 height = CGameWorldHeightMap::GetMinHeightFromWorldHeightMap(v0.x, v0.y);
v0.z = height - 20.0f;
v1.z = height + 20.0f;
if(g_AudioWorldSectors[sectorIndex].numHighwayNodes > 0)
{
colour.SetAlpha((u32)Clamp((s32)(255.0f * (g_AudioWorldSectors[sectorIndex].numHighwayNodes/50.0f)), 0, 255));
grcDebugDraw::BoxAxisAligned(VECTOR3_TO_VEC3V(v0), VECTOR3_TO_VEC3V(v1), colour, true);
}
}
}
void naEnvironment::DrawDebug() const
{
if(g_audDrawWorldSectorDebug)
{
const Vector3 camPos = VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition());
const u32 sector = static_cast<u32>(ComputeWorldSectorIndex(camPos));
const u32 sectorX = sector % g_audNumWorldSectorsX;
const u32 sectorY = (sector - sectorX)/g_audNumWorldSectorsX;
DebugDrawSector(sectorX-1, sectorY-1);
DebugDrawSector(sectorX-1, sectorY);
DebugDrawSector(sectorX-1, sectorY+1);
DebugDrawSector(sectorX, sectorY-1);
DebugDrawSector(sectorX, sectorY);
DebugDrawSector(sectorX, sectorY+1);
DebugDrawSector(sectorX+1, sectorY);
DebugDrawSector(sectorX+1, sectorY+1);
DebugDrawSector(sectorX+1, sectorY-1);
}
if(g_audResetWorldSectorRoadInfo)
{
for(u32 x = 0; x < g_audNumWorldSectorsX; x++)
{
for(u32 y = 0; y < g_audNumWorldSectorsY; y++)
{
g_AudioWorldSectors[x + (y * g_audNumWorldSectorsX)].numHighwayNodes = 0;
}
}
g_audResetWorldSectorRoadInfo = false;
}
if(g_audDrawLocalObjects)
{
for(u32 i = 0; i < g_MaxInterestingLocalObjects; i++)
{
if(m_InterestingLocalObjects[i].object && GetInterestingLocalObjectMaterial(i))
{
const char *matName = audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(GetInterestingLocalObjectMaterial(i)->NameTableOffset);
Color32 color = Color32(255,0,0);
Vector3 pos = VEC3V_TO_VECTOR3(m_InterestingLocalObjects[i].object->GetTransform().GetPosition());
if(m_InterestingLocalObjects[i].object->GetCurrentPhysicsInst())
{
color = Color32(0,255,0);
pos = VEC3V_TO_VECTOR3(m_InterestingLocalObjects[i].object->GetCurrentPhysicsInst()->GetCenterOfMass());
}
CDebugScene::DrawEntityBoundingBox(m_InterestingLocalObjects[i].object,color);
char buf[128];
formatf(buf, sizeof(buf), "%s %s %s %s %s", matName, (AUD_GET_TRISTATE_VALUE(GetInterestingLocalObjectMaterial(i)->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_ISRESONANT)==AUD_TRISTATE_TRUE?"reso":""), (GetInterestingLocalObjectMaterial(i)->RandomAmbient != g_NullSoundHash?"rand ambient":""), (GetInterestingLocalObjectMaterial(i)->ShockwaveSound != g_NullSoundHash?"shockwave":""),(GetInterestingLocalObjectMaterial(i)->RainLoop != g_NullSoundHash?"rain":""));
grcDebugDraw::Text(pos + Vector3(0.f,0.f,0.5f), color, buf);
}
}
}
if(g_audDrawShockwaves)
{
for(u32 i = 0 ; i < kMaxRegisteredShockwaves; i++)
{
if(m_RegisteredShockwavesAllocation.IsSet(i))
{
grcDebugDraw::Sphere(RCC_VEC3V(m_RegisteredShockwaves[i].centre), 1.f, Color32(0,0,255));
char buf[128];
formatf(buf, sizeof(buf), "intensity: %f updated %0.2fs ago", m_RegisteredShockwaves[i].intensity, (fwTimer::GetTimeInMilliseconds() - m_RegisteredShockwaves[i].lastUpdateTime) * 0.001f);
grcDebugDraw::Text(RCC_VEC3V(m_RegisteredShockwaves[i].centre), Color32(0,0,255), buf);
if(m_RegisteredShockwaves[i].radius > 0.f)
{
grcDebugDraw::Sphere(RCC_VEC3V(m_RegisteredShockwaves[i].centre), m_RegisteredShockwaves[i].radius, Color32(255,0,0), false);
}
}
}
}
}
void naEnvironment::DrawWeaponEchoes()
{
if(g_DrawWeaponEchoes)
{
Vector3 vListenerPos = VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetPanningListenerPosition());
Vector3 vBestEchoPlayPos, vOppositeEchoPlayPos;
u32 bestEchoPredelay = 0;
u32 oppositeEchoPredelay = 0;
f32 bestEchoAtten = 0.0f;
f32 oppositeEchoAtten = 0.0f;
s32 oppositeEchoIdx;
s32 bestEchoIdx = GetBestEchoForSourcePosition(vListenerPos, &oppositeEchoIdx);
if(bestEchoIdx > -1)
{
CalculateEchoDelayAndAttenuation(bestEchoIdx, vListenerPos, &bestEchoPredelay, &bestEchoAtten, &vBestEchoPlayPos);
}
if(oppositeEchoIdx > -1)
{
CalculateEchoDelayAndAttenuation(oppositeEchoIdx, vListenerPos, &oppositeEchoPredelay, &oppositeEchoAtten, &vOppositeEchoPlayPos);
}
char buf[255];
f32 echoSuitablility[8];
GetEchoSuitablilityForSourcePosition(vListenerPos, &echoSuitablility[0]);
for(s32 i = 0; i < 8; i++)
{
if(echoSuitablility[i] == 0)
continue;
f32 distance = (vListenerPos - m_EchoPosition[i]).Mag();
if(i == bestEchoIdx)
{
grcDebugDraw::Sphere(m_EchoPosition[i], 1.0f, Color_green);
formatf(buf, sizeof(buf), "Best Echo - Suitability: %f distance: %f predelay: %u volume: %f ",
echoSuitablility[i], distance, bestEchoPredelay, bestEchoAtten);
grcDebugDraw::Text(m_EchoPosition[i] + Vector3(0.0f, 0.0f, 1.0f), Color_green, buf);
grcDebugDraw::Sphere(vBestEchoPlayPos, 1.0f, Color_green);
formatf(buf, sizeof(buf), "Best Echo Play Position");
grcDebugDraw::Text(vBestEchoPlayPos + Vector3(0.0f, 0.0f, 1.0f), Color_green, buf);
Vector3 vNormalArrow = m_EchoDirection[i];
vNormalArrow.Scale(2.0f);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(m_EchoPosition[i]), VECTOR3_TO_VEC3V(m_EchoPosition[i] + vNormalArrow), 1.0f, Color_OrangeRed);
}
else if(i == oppositeEchoIdx)
{
grcDebugDraw::Sphere(m_EchoPosition[i], 1.0f, Color_OliveDrab);
char buf[255];
formatf(buf, sizeof(buf), "Opposite Echo - Suitability: %f distance: %f predelay: %u volume: %f",
echoSuitablility[i], distance, oppositeEchoPredelay, oppositeEchoAtten);
grcDebugDraw::Text(m_EchoPosition[i] + Vector3(0.0f, 0.0f, 1.0f), Color_OliveDrab, buf);
grcDebugDraw::Sphere(vOppositeEchoPlayPos, 1.0f, Color_OliveDrab);
formatf(buf, sizeof(buf), "Opposite Echo Play Position");
grcDebugDraw::Text(vOppositeEchoPlayPos + Vector3(0.0f, 0.0f, 1.0f), Color_OliveDrab, buf);
Vector3 vNormalArrow = m_EchoDirection[i];
vNormalArrow.Scale(2.0f);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(m_EchoPosition[i]), VECTOR3_TO_VEC3V(m_EchoPosition[i] + vNormalArrow), 1.0f, Color_OrangeRed);
}
else
{
grcDebugDraw::Sphere(m_EchoPosition[i], 0.5f, Color_grey);
char buf[255];
formatf(buf, sizeof(buf), "Suitability: %f distance: %f", echoSuitablility[i], distance);
grcDebugDraw::Text(m_EchoPosition[i] + Vector3(0.0f, 0.0f, 0.5f), Color_grey, buf);
Vector3 vNormalArrow = m_EchoDirection[i];
vNormalArrow.Scale(2.0f);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(m_EchoPosition[i]), VECTOR3_TO_VEC3V(m_EchoPosition[i] + vNormalArrow), 1.0f, Color_OrangeRed);
}
}
f32 normalizedReverbWet = ((f32)((m_SourceEnvironmentMetrics[0] & 12) >> 2))/3.0f;
f32 normalizedReverbSize = ((f32)((m_SourceEnvironmentMetrics[0] & 3)))/3.0f;
f32 volume;
f32 hold;
if(m_SourceEnvironmentMetrics[0] == 0)
{
volume = m_OutdoorInteriorWeaponVolCurve.CalculateValue(0.0f);
hold = m_OutdoorInteriorWeaponHoldCurve.CalculateValue(0.0f);
}
else if(normalizedReverbWet == 0)
{
volume = m_OutdoorInteriorWeaponVolCurve.CalculateValue(0.15f);
hold = m_OutdoorInteriorWeaponHoldCurve.CalculateValue(normalizedReverbSize);
}
else
{
volume = m_OutdoorInteriorWeaponVolCurve.CalculateValue(normalizedReverbWet);
hold = m_OutdoorInteriorWeaponHoldCurve.CalculateValue(normalizedReverbSize);
}
//Draw tight area stats
f32 yPos = 0.05f;
f32 yInc = 0.025f;
f32 xPos = 0.05f;
formatf(buf, sizeof(buf), "Volume: %f", volume);
grcDebugDraw::Text(Vector2(xPos, yPos), Color_blue1, buf);
yPos += yInc;
formatf(buf, sizeof(buf), "Hold: %f", hold);
grcDebugDraw::Text(Vector2(xPos, yPos), Color_blue1, buf);
}
}
void naEnvironment::DrawAudioWorldSectors()
{
Vector3 origin,gridControl;
origin.SetX((f32)g_audWorldSectorsMinX);
origin.SetY((f32)g_audWorldSectorsMinY);
origin.SetZ(70.f);
gridControl = origin;
for(u32 i = 0 ; i <= g_audNumWorldSectorsX ; ++i)
{
grcDebugDraw::Line(gridControl,gridControl+ Vector3(g_fAudNumWorldSectorsX * g_audWidthOfSector,0.f,0.f), Color_red,1);
gridControl.SetY(gridControl.GetY() + g_audWidthOfSector);
}
gridControl = origin;
for(u32 i = 0 ; i <= g_audNumWorldSectorsY ; ++i)
{
grcDebugDraw::Line(gridControl,gridControl+ Vector3(0.f,g_fAudNumWorldSectorsY * g_audDepthOfSector,0.f), Color_red,1);
gridControl.SetX(gridControl.GetX() + g_audDepthOfSector);
}
}
void GenerateAudioSectorsCB()
{
naEnvironment::DrawAudioWorldSectors(true);
naEnvironment::ProcessSectors(true);
}
void StopCB()
{
naEnvironment::ProcessSectors(false);
naEnvironment::DrawAudioWorldSectors(false);
}
extern bool g_WarpToInterior;
void naEnvironment::AddWidgets(bkBank &bank)
{
bank.PushGroup("GTA Environment",false);
const char *specialEffectModes[kNumSpecialEffectModes] = { "Normal", "Underwater", "Stoned", "PauseMenu", "SlowMotion"};
bank.AddCombo("SpecialEffectModeOverride", (s32*)&audNorthAudioEngine::GetGtaEnvironment()->m_SpecialEffectModeOverride, kNumSpecialEffectModes, (const char**)specialEffectModes);
bank.AddToggle("override", &audNorthAudioEngine::GetGtaEnvironment()->m_OverrideSpecialEffectMode);
bank.AddToggle("Update built up factor for each zone.", &g_UpdateBuiltUpFactor);
// GTA-specific, game-thread only things
bank.PushGroup("Audio world sectors info generator",false);
bank.AddToggle("Draw audio world sectors ", &sm_DrawAudioWorldSectors);
bank.AddToggle("Draw AudioWorldSectors info", &g_audDrawWorldSectorDebug);
bank.AddSlider("sm_NumWaterProbeSets", &sm_NumWaterProbeSets, 0.0f, 997.0f, 1.f);
bank.AddSlider("sm_CurrentSectorX", &sm_CurrentSectorX, 0, 100, 1);
bank.AddSlider("sm_CurrentSectorY", &sm_CurrentSectorY, 0, 100, 1);
bank.AddSlider("g_NumTreesForRain", &g_NumTreesForRain, 0.0f, 997.0f, 1.f);
bank.AddSlider("g_NumTreesForRainBuiltUp", &g_NumTreesForRainBuiltUp, 0.0f, 997.0f, 1.f);
bank.AddButton("Generate audio sectors info",datCallback(GenerateAudioSectorsCB));
bank.AddButton("Stop",datCallback(StopCB));
bank.PopGroup();
bank.AddToggle("EnableLocalEnvironmentMetrics", &g_EnableLocalEnvironmentMetrics);
bank.PushGroup("Interiors");
bank.AddToggle("WarpToInterior", &g_WarpToInterior);
bank.AddToggle("g_UseDebugRoomSettings", &g_UseDebugRoomSettings);
bank.AddToggle("g_TryRoomsEveryTime", &g_TryRoomsEveryTime);
bank.AddSlider("ReverbSmall", &g_DebugRoomSettingsReverbMedium, 0.0f, 1.0f, 0.01f);
bank.AddSlider("ReverbMedium", &g_DebugRoomSettingsReverbSmall, 0.0f, 1.0f, 0.01f);
bank.AddSlider("ReverbLarge", &g_DebugRoomSettingsReverbLarge, 0.0f, 1.0f, 0.01f);
bank.AddToggle("g_UsePlayer", &g_UsePlayer);
bank.AddText("Interior", &g_InteriorInfoDebug[0], sizeof(g_InteriorInfoDebug));
bank.AddText("Room", &g_RoomInfoDebug[0], sizeof(g_RoomInfoDebug));
bank.AddButton("Create RAVE objects for current Interior and Rooms", CFA(CreateRAVEObjectsForCurrentInteriorAndRooms));
bank.AddButton("Create RAVE object for current Interior Only", CFA(CreateRAVEObjectsForCurrentInterior));
bank.AddButton("Create RAVE object for current Room Only", CFA(CreateRAVEObjectsForCurrentRoom));
bank.PopGroup();
bank.AddToggle("g_DrawWeaponEchos", &g_DrawWeaponEchoes);
bank.AddSlider("OpenSpaceReverbSize", &g_OpenSpaceReverbSize, 0.0f, 1.0f, 0.01f);
bank.AddSlider("OpenSpaceReverbWet", &g_OpenSpaceReverbWet, 0.0f, 1.0f, 0.01f);
bank.AddSlider("SpeakerReverbSmootherRate", &g_SpeakerReverbSmootherRate, 0.0f, 1.0f, 0.0001f);
bank.AddSlider("SpeakerReverdDamping", &g_SpeakerReverbDamping, 0.0f, 1.0f, 0.01f);
bank.AddSlider("SourceEnvironmentRadius", &g_SourceEnvironmentRadius, 0.0f, 20.0f, 0.1f);
bank.AddSlider("TheManhattanFactor", &g_OverrideBuiltUpFactor, 0.0f, 1.0f, 0.01f);
bank.AddToggle("OverrideManhattanFactor", &g_ShouldOverrideBuiltUpFactor);
bank.AddSlider("Special Ability Slow-mo timeout", &g_SpecialAbilitySlowMoTimeout, 0, 10000, 10);
bank.AddSlider("Deafening Up Time", &g_DeafeningRampUpTime, 0, 10000, 1);
bank.AddSlider("Deafening Hold Time", &g_DeafeningHoldTime, 0, 10000, 1);
bank.AddSlider("Deafening Down Time", &g_DeafeningRampDownTime, 0, 10000, 1);
bank.AddSlider("Deafening Ear Leak", &g_DeafeningEarLeak, 0.0f, 1.0f, 0.1f);
bank.AddSlider("g_DeafeningRolloff", &g_DeafeningRolloff, 0.0f, 10.0f, 0.1f);
bank.PushGroup("Interesting local objects");
bank.PushGroup("ShockWaves");
bank.AddToggle("g_audDrawShockwaves", &g_audDrawShockwaves);
bank.AddSlider("Heli shockwave radius", &sm_HeliShockwaveRadius, 0.0f, 1000.0f, 0.01f);
bank.AddSlider("Train distance threshold", &sm_TrainDistanceThreshold, 0.0f, 1000.0f, 0.01f);
bank.AddSlider("Train shockwave radius", &sm_TrainShockwaveRadius, 0.0f, 1000.0f, 0.01f);
bank.PopGroup();
bank.PushGroup("Resonance");
bank.AddToggle("Force Resonance Tracks Listener", &g_ForceResonanceTracksListener);
bank.AddToggle("Display ResoIntensity", &sm_DisplayResoIntensity);
bank.AddSlider("Melee resonance impulse", &sm_MeleeResoImpulse, 0.0f, 1.f, 0.001f);
bank.AddSlider("Pistol resonance impulse", &sm_PistolResoImpulse, 0.0f, 1.f, 0.001f);
bank.AddSlider("Rifle resonance impulse", &sm_RifleResoImpulse, 0.0f, 1.f, 0.001f);
bank.AddSlider("Weapons resonance distance", &sm_WeaponsResonanceDistance, 0.0f, 1000.0f, 0.01f);
bank.AddSlider("Explosions resonance impulse", &sm_ExplosionsResonanceImpulse, 0.0f, 1.f, 0.001f);
bank.AddSlider("Explosions resonance distance", &sm_ExplosionsResonanceDistance, 0.0f, 1000.f, 0.001f);
bank.AddSlider("Vehicle collision resonance impulse", &sm_VehicleCollisionResonanceImpulse, 0.0f, 1.f, 0.001f);
bank.AddSlider("Vehicle collision distance", &sm_VehicleCollisionResonanceDistance, 0.0f, 1000.f, 0.001f);
bank.AddSlider("Plateau effect", &sm_DistancePlateau, 0.0f, 1000.0f, 0.01f);
bank.PushGroup("Slow motion", false);
bank.AddSlider("Frequency", &sm_SlowMoLFOFrequencyMin, 0.0f, 1000.0f, 0.01f);
bank.AddSlider("Frequency", &sm_SlowMoLFOFrequencyMax, 0.0f, 1000.0f, 0.01f);
bank.PopGroup();
bank.PopGroup();
bank.PushGroup("Rain");
bank.AddToggle("Force Rain Tracks Listener", &g_ForceRainLoopTracksListener);
bank.AddToggle("Force Rain Plays From Top Of Bounds", &g_ForceRainLoopPlayFromTopOfBounds);
bank.AddSlider("sm_SqdDistanceToPlayRainSounds", &sm_SqdDistanceToPlayRainSounds, 0.0f, 10000.0f, 1.f);
bank.AddSlider("sm_RainOnPropsVolumeOffset", &sm_RainOnPropsVolumeOffset, -100.0f, 24.f, 1.f);
bank.AddSlider("sm_RainOnPopsPreDealy", &sm_RainOnPopsPreDealy, 0, 10000, 100);
bank.AddSlider("sm_MaxNumRainProps", &sm_MaxNumRainProps, 0, 64, 1);
bank.PopGroup();
bank.AddToggle("g_audDrawLocalObjects",&g_audDrawLocalObjects);
bank.AddToggle("Use aproximation to compute which objects are covered", &sm_UseCoverAproximation);
bank.AddToggle("Debug covered objects", &sm_DebugCoveredObjects);
bank.AddSlider("sm_RadiusScale", &sm_RadiusScale, 0.0f, 10.0f, 0.0001f);
bank.AddSlider("sm_ObjOcclusionUpdateTime", &sm_ObjOcclusionUpdateTime, 0, 10000, 100);
bank.PopGroup();
bank.PushGroup("Wind");
bank.PushGroup("Through trees");
bank.PushGroup("Background",false);
bank.AddToggle("sm_DrawBgTreesInfo", &sm_DrawBgTreesInfo);
bank.AddSlider("sm_NumTreesForAvgDist", &sm_NumTreesForAvgDist, 0, 50, 1);
bank.AddSlider("g_FitnessReduced", &g_FitnessReduced, 0.0f, 1.0f, 0.01f);
bank.AddSlider("g_VolSmootherRate", &g_VolSmootherRate, 0.0f, 2.0f, 0.01f);
bank.AddSlider("Bushes min radius", &sm_BushesMinRadius, 0.0f, 1000.0f, 0.01f);
bank.AddSlider("Trees min radius", &sm_SmallTreesMinRadius, 0.0f, 1000.0f, 0.01f);
bank.AddSlider("Big trees min radius", &sm_BigTreesMinRadius, 0.0f, 1000.0f, 0.01f);
bank.AddSlider("sm_BigTreesFitness", &sm_BigTreesFitness, 0.0f, 1.0f, 0.01f);
bank.AddSlider("sm_TreesFitness", &sm_TreesFitness, 0.0f, 1.0f, 0.01f);
bank.AddSlider("sm_BushesFitness", &sm_BushesFitness, 0.0f, 1.0f, 0.01f);
bank.AddSlider("Sounds distance from the listener", &sm_TreesSoundDist, 0.0f, 1000.0f, 0.01f);
//bank.AddSlider("sm_GustVolSpikeForBgTrees", &sm_GustVolSpikeForBgTrees, 0.0f, 24.0f, 0.5f);
bank.AddToggle("sm_MuteBgFL", &sm_MuteBgFL);
bank.AddToggle("sm_MuteBgFR", &sm_MuteBgFR);
bank.AddToggle("sm_MuteBgRL", &sm_MuteBgRL);
bank.AddToggle("sm_MuteBgRR", &sm_MuteBgRR);
bank.PushGroup("GustEnd sound",false);
bank.AddSlider("sm_MinNumberOfTreesToTriggerGustEnd", &sm_MinNumberOfTreesToTriggerGustEnd, 0, 1000, 1);
bank.PopGroup();
bank.PopGroup();
bank.PopGroup();
bank.PushGroup("On props");
bank.AddToggle("Show Info", &sm_DebugGustWhistle);
bank.AddSlider("sm_MinTimeToRetrigger", &sm_MinTimeToRetrigger, 0,50000,100);
bank.PopGroup();
bank.PopGroup();
bank.AddToggle("Reset AudioWorldSector Road Info", &g_audResetWorldSectorRoadInfo);
bank.AddToggle("Debug Listener Reverb", &g_DebugListenerReverb);
bank.AddSlider("g_BuiltUpSmootherRate", &g_BuiltUpSmootherRate, 0.0f, 1.0f, 0.0001f);
bank.AddToggle("Debug BuiltUp Info", &g_DebugBuiltUpInfo);
bank.AddToggle("g_DisplayBuiltUpFactors", &g_DisplayBuiltUpFactors);
bank.AddToggle("g_UseProperBuiltUpFactor", &g_UseProperBuiltUpFactor);
bank.AddSlider("g_NSEW_Offset", &g_NSEW_Offset, 0.0f, 2.49f, 0.01f);
bank.AddSlider("g_BuiltUpDampingFactorSmoothUpRate", &g_BuiltUpDampingFactorSmoothUpRate, 0.0f, 10000.0f, 1.0f);
bank.AddSlider("g_BuiltUpDampingFactorSmoothDownRate", &g_BuiltUpDampingFactorSmoothDownRate, 0.0f, 10000.0f, 1.0f);
bank.AddSlider("g_BuiltUpDampingFactorOverride", &g_BuiltUpDampingFactorOverride, 0.0f, 1.0f, 0.1f);
bank.AddSlider("g_InCarRatioSmoothRate", &g_InCarRatioSmoothRate, 0.0f, 100.0f, 0.1f);
bank.AddSlider("g_InteriorRatioSmoothRate", &g_InteriorRatioSmoothRate, 0.0f, 100.0f, 0.1f);
bank.PopGroup();
}
#endif