6821 lines
240 KiB
C++
6821 lines
240 KiB
C++
//
|
|
// audio/collisionaudioentity.cpp
|
|
//
|
|
// Copyright (C) 1999-2012 Rockstar Games. All Rights Reserved.
|
|
//
|
|
|
|
#include "collisionaudioentity.h"
|
|
#include "frontendaudioentity.h"
|
|
#include "northaudioengine.h"
|
|
#include "weaponaudioentity.h"
|
|
|
|
#include "glassaudioentity.h"
|
|
#include "scriptaudioentity.h"
|
|
#include "vehiclecollisionaudio.h"
|
|
#include "audioengine/engine.h"
|
|
#include "audioengine/engineutil.h"
|
|
#include "audiohardware/driverdefs.h"
|
|
#include "audiohardware/driverutil.h"
|
|
#include "audiosoundtypes/multitracksound.h"
|
|
#include "audiosoundtypes/randomizedsound.h"
|
|
#include "audiosoundtypes/soundcontrol.h"
|
|
#include "animation/animbones.h"
|
|
#include "boataudioentity.h"
|
|
#include "camera/CamInterface.h"
|
|
#include "camera/Viewports/ViewportManager.h"
|
|
#include "camera/debug/DebugDirector.h"
|
|
#include "camera/helpers/frame.h"
|
|
#include "control/replay/Replay.h"
|
|
#include "control/replay/audio/weaponaudiopacket.h"
|
|
#include "control/replay/audio/CollisionAudioPacket.h"
|
|
#include "debug/debugscene.h"
|
|
#include "dynamicmixer.h"
|
|
#include "fragment/type.h"
|
|
#include "fragment/typegroup.h"
|
|
#include "fragment/typechild.h"
|
|
#include "grcore/debugdraw.h"
|
|
#include "input/mouse.h"
|
|
#include "ModelInfo/PedModelInfo.h"
|
|
#include "modelinfo/vehiclemodelinfo.h"
|
|
#include "objects/door.h"
|
|
#include "pedfootstepaudio.h"
|
|
#include "peds/ped.h"
|
|
#include "peds/pedintelligence.h"
|
|
#include "physics/gtaMaterialManager.h"
|
|
#include "physics/gtaInst.h"
|
|
#include "physics/physics.h"
|
|
#include "phbound/support.h"
|
|
#include "renderer/Water.h"
|
|
#include "scene/entities/compEntity.h"
|
|
#include "scene/AnimatedBuilding.h"
|
|
#include "scene/WarpManager.h"
|
|
#include "streaming/streaming.h" // For CStreaming::HasObjectLoaded(), maybe other things.
|
|
#include "vehicles/boat.h"
|
|
#include "audio/heliaudioentity.h"
|
|
#include "game/weather.h"
|
|
|
|
#include "debugaudio.h"
|
|
#include "phbound/boundcomposite.h"
|
|
#include "atl/string.h"
|
|
#include "physics/inst.h"
|
|
#include "physics/gtaInst.h"
|
|
|
|
|
|
AUDIO_OPTIMISATIONS()
|
|
|
|
audCollisionAudioEntity g_CollisionAudioEntity;
|
|
|
|
extern RegdEnt g_AudioDebugEntity;
|
|
|
|
const u32 g_ScrapingCollisionHoldTimeMs = 100;
|
|
static const u32 g_RollingCollisionHoldTimeMs = 100;
|
|
f32 g_MinSpeedForCollision = 0.05f;
|
|
static f32 g_MinRollSpeed = 0.0f;
|
|
BANK_ONLY(static f32 g_CollisionTimeFilterSpeed = 3.f);
|
|
u32 g_MinTimeBetweenCollisions = 200;
|
|
BANK_ONLY(static s32 g_CollisionFilterLifetime = 80);
|
|
BANK_ONLY(static u32 g_FragCollisionStartTime = 200);
|
|
u32 g_CollisionMaterialCountThreshold = 3;
|
|
static u32 g_CollisionCountDecreaseRate = 10;
|
|
bool g_PedsMakeSolidCollisions = false;
|
|
bool g_DoPedScrapeStartImpacts = false;
|
|
bool g_PedsOverrideImpactMag = true;
|
|
f32 g_ScrapeImpactVel = 0.01f;
|
|
f32 g_VehicleScrapeImpactVel = 1.5f;
|
|
BANK_ONLY(static f32 g_RollImpactVel = 0.01f);
|
|
static f32 g_RollScalar = 0.1f; //10.f;
|
|
static f32 g_WreckedVehicleImpactScale = 1.f;
|
|
static f32 g_WreckedVehicleRolloffScale = 1.f;
|
|
static f32 g_AnimalImpactScalar = 0.5f;
|
|
static f32 g_BodyFallBoost = 6.f;
|
|
static f32 g_MaterialScrapeAgainstPedVolumeScaling = 1.f;
|
|
static f32 s_ImpactMagScalar = 0.01f;
|
|
BANK_ONLY(bool g_DebugSwingingProps = false;)
|
|
f32 g_SwingVolSmoothRate = 0.001f;
|
|
u32 g_WeaponImpactFilterTime = 1000;
|
|
u32 g_PedScrapeStartImpactTime = 1000;
|
|
float g_PedWetSurfaceScrapeAtten = 0.5f;
|
|
bank_u32 g_LFPCuttoffInWater = 1000;
|
|
bank_float g_VolumeReductionInWater = 6.0f;
|
|
float g_PlayerMeleeCollisionVolumeBoost = 0.f;
|
|
float g_PlayerVehicleMeleeCollisionVolumeScale = -9.f;
|
|
|
|
f32 g_VelocityThesholdForPropSwing = 0.001f;
|
|
|
|
u32 audCollisionAudioEntity::sm_TimeSinceLastBulletHit = 0;
|
|
u32 audCollisionAudioEntity::sm_TimeSinceLastAutomaticBulletHit = 0;
|
|
u32 audCollisionAudioEntity::sm_TimeSinceLastShotgunBulletHit = 0;
|
|
|
|
bool g_UseCollisionIntensityCulling = true; //Limit the number of collision sounds when lots are playing
|
|
|
|
static const f32 g_MaxMeleeDamageDoneForCollision = 25.0f;
|
|
static const f32 g_MaxImpulseMagnitudeForMeleeDamageDoneVehicle = 7.0f;
|
|
static f32 g_MaxImpulseMagnitudeForMeleeDamageDoneGeneral = 6.0f;
|
|
static const f32 g_BulletRicochetPanDistance = 5.f;
|
|
//static f32 s_MeleeImpactScaleFactor = 25.f;
|
|
f32 audCollisionAudioEntity::sm_PlayerRicochetsRollOffScaleFactor = 1.5f;
|
|
f32 audCollisionAudioEntity::sm_PlayerBIRollOffScaleFactor = 3.f;
|
|
f32 audCollisionAudioEntity::sm_PlayerHeadShotRollOffScaleFactor = 3.f;
|
|
u32 audCollisionAudioEntity::sm_PlayerCollisionEventTimeout = 2000;
|
|
f32 audCollisionAudioEntity::sm_PlayerCollisionEventSpeedThreshold = 1.f;
|
|
f32 audCollisionAudioEntity::sm_PlayerContextCollisionRolloff = 1.f;
|
|
f32 audCollisionAudioEntity::sm_PlayerContextVolumeForBlip = -20.f;
|
|
|
|
f32 audCollisionAudioEntity::sm_DistanceToTriggerRaygunBI = 50.f;
|
|
f32 audCollisionAudioEntity::sm_DistanceToTriggerAutomaticBI = 50.f;
|
|
f32 audCollisionAudioEntity::sm_DistanceToTriggerShotgunBI = 50.f;
|
|
u32 audCollisionAudioEntity::sm_DtToTriggerAutomaticBI = 500;
|
|
u32 audCollisionAudioEntity::sm_DtToTriggerShotgunBI = 500;
|
|
|
|
f32 g_SolidRollAttenuation = -8.f;
|
|
f32 g_SoftRollAttenuation = -12.f;
|
|
|
|
bool g_MapUsesPropRollSpeedSettings = true;
|
|
bool audCollisionAudioEntity::sm_DialogueSuppressBI = false;
|
|
|
|
#if __BANK
|
|
bank_float g_VehicleOnVehicleImpulseScalingFactor = 1.5f;
|
|
bank_float g_BoatImpulseScalingFactor = 3.0f;
|
|
|
|
bool g_PreserveMACSSettingsOnExport = true;
|
|
bool g_DebugFocusEntity = false;
|
|
audScene* audCollisionAudioEntity::sm_IncreaseFootstepVolumeScene = NULL;
|
|
int audCollisionAudioEntity::sm_CurrentSectorX = 0;
|
|
int audCollisionAudioEntity::sm_CurrentSectorY = 0;
|
|
bool audCollisionAudioEntity::sm_IsWaitingToInit = true;
|
|
bool audCollisionAudioEntity::sm_ProcessSectors = false;
|
|
fiStream * audCollisionAudioEntity::sm_NewMoveableFile = NULL;
|
|
fiStream * audCollisionAudioEntity::sm_NewStaticFile = NULL;
|
|
fiStream * audCollisionAudioEntity::sm_ChangedMacsFile = NULL;
|
|
fiStream * audCollisionAudioEntity::sm_DefaultMatFile = NULL;
|
|
bool audCollisionAudioEntity::sm_AutomaticImpactOnlyOnVeh = false;
|
|
bool audCollisionAudioEntity::sm_ShotgunDisabledForPlayer = false;
|
|
bool audCollisionAudioEntity::sm_ShotgunImpactOnlyOnVeh = false;
|
|
|
|
bool g_DisplayPedScrapeMaterial = false;
|
|
#endif // __BANK
|
|
|
|
bank_float g_PedBuildingImpulseScalingFactor = 4.f;
|
|
f32 g_UpsideDownCarImpulseScalingFactor = 3.0f;
|
|
//static u32 g_AudioWeights[NUM_AUDIOWEIGHT] = {1.f, 1.f, 1.f, 1.f, 1.f};
|
|
static f32 g_AudWeightDifferenceScaling[5] = {1.f, 0.7f, 0.5f, 0.2f, 0.f};
|
|
|
|
|
|
|
|
bank_float g_MinBulletImpactDampingDistance = 50.0f;
|
|
bank_float g_MaxBulletImpactDampingDistance = 100.0f;
|
|
bank_float g_MaxBulletImpactCloserAttenuation = 0.0f;
|
|
bank_float g_MaxBulletImpactCloserImpulseDamping = 1.0f;
|
|
|
|
bool g_HardImpactsExcludeSolid = false;
|
|
|
|
//bank_float g_GrenadeImpulseMagnitudeScaling = 5000.f;
|
|
|
|
bank_u32 g_ImpactRetriggerTime = 61;
|
|
|
|
phMaterialMgr::Id g_PedMaterialId = ~0U;
|
|
|
|
const audCategory *g_UppedCollisionCategory = NULL;
|
|
const audCategory *g_PedCollisionCategory = NULL;
|
|
const audCategory *g_DoorsCategory = NULL;
|
|
|
|
const char *g_MaterialNameCarBody = "CAR_METAL";
|
|
const char *g_MaterialNameCarTyre = "RUBBER";
|
|
|
|
const char *g_CustomMaterialNameFootLeft = "FOOT_LEFT";
|
|
const char *g_CustomMaterialNameFootRight = "FOOT_RIGHT";
|
|
|
|
CollisionMaterialSettings *g_VehiclePedMeleeMaterial = NULL;
|
|
|
|
const u32 g_NullMaterialHash = ATSTRINGHASH("AM_BASE_DEFAULT", 0xF2C345CD);
|
|
audMetadataRef g_NullMaterialRef;
|
|
|
|
atMap<u32,u32> audCollisionAudioEntity::sm_MacOverrideMap;
|
|
|
|
audCurve audCollisionAudioEntity::sm_TimeBetweenShotsToRicoProb;
|
|
audCurve audCollisionAudioEntity::sm_DistanceToBIPredelay;
|
|
audCurve audCollisionAudioEntity::sm_DistanceToHeadShotPredelay;
|
|
audCurve audCollisionAudioEntity::sm_DistanceToRicochetsPredelay;
|
|
EXT_PF_GROUP(TriggerSoundsGroup);
|
|
PF_COUNTER(audCollisionAudioEntity_Impacts,TriggerSoundsGroup);
|
|
PF_COUNTER(audCollisionAudioEntity_Scrapes,TriggerSoundsGroup);
|
|
PF_COUNTER(audCollisionAudioEntity_Rolls,TriggerSoundsGroup);
|
|
PF_COUNTER(audCollisionAudioEntity_Breaks,TriggerSoundsGroup);
|
|
PF_COUNTER(audCollisionAudioEntity_Bullets,TriggerSoundsGroup);
|
|
|
|
PF_PAGE(CollisionDebugPage,"audCollisionAudioEntity");
|
|
PF_GROUP(CollisionAudioEntity);
|
|
PF_LINK(CollisionDebugPage, CollisionAudioEntity);
|
|
|
|
PF_VALUE_FLOAT(DebugImpulseMag0, CollisionAudioEntity);
|
|
PF_VALUE_FLOAT(DebugImpulseMag1, CollisionAudioEntity);
|
|
PF_VALUE_FLOAT(DebugNumCollisions, CollisionAudioEntity);
|
|
PF_VALUE_INT(CollisionHistory, CollisionAudioEntity);
|
|
|
|
#if __BANK
|
|
XPARAM(audiotag);
|
|
|
|
namespace rage
|
|
{
|
|
XPARAM(audiowidgets);
|
|
|
|
PF_PAGE(AudioCollisionPage, "Audio Collisions");
|
|
PF_GROUP(AudioCollisions);
|
|
PF_LINK(AudioCollisionPage, AudioCollisions);
|
|
|
|
|
|
|
|
PF_COUNTER(CollisonsProcessed, AudioCollisions);
|
|
}
|
|
|
|
bool g_PrintBulletImpactMaterial = false;
|
|
bool g_PrintMeleeImpactMaterial = false;
|
|
bool g_IncreaseFootstepsVolume = false;
|
|
bool g_DontPlayRicochets = false;
|
|
static char g_CollisionMaterialOverrideName[64] = "\0";
|
|
bool g_DisableCollisionAudio = false;
|
|
|
|
bool g_DebugAudioImpacts = false;
|
|
bool g_DebugAudioRolls = false;
|
|
bool g_DebugAudioScrapes = false;
|
|
bool g_DebugPlayerVehImpacts= false;
|
|
f32 g_DebugImpactThresh = 0.01f;
|
|
bool g_MeleeOnlyBulletImpacts = false;
|
|
|
|
static const ModelAudioCollisionSettings * s_RaveSelectedMaterialSettings = NULL;
|
|
static char s_RaveSelectedMaterialSettingsName[256] = {"No object selected in Rave"};
|
|
static char s_PropSpawnName[256] = {"No prop to spawn"};
|
|
static bool s_AutoSpawnRaveObjects = false;
|
|
static bool s_UpdateCreatedSettings = false;
|
|
|
|
|
|
//f32 g_RicochetProb90 = 0.0f;
|
|
//f32 g_RicochetProb0 = 0.9f;
|
|
|
|
u32 g_CollisionDebugIndex = 0;
|
|
const u32 g_NumDebugCollisionInfos = 64;
|
|
struct CollisionDebugInfo
|
|
{
|
|
Vector3 position;
|
|
u32 time;
|
|
s32 component1;
|
|
s32 component2;
|
|
f32 impulseMag;
|
|
char *mat1, *mat2;
|
|
u32 entType1, entType2;
|
|
bool hidden;
|
|
audCollisionType type;
|
|
}g_CollisionDebugInfo[g_NumDebugCollisionInfos];
|
|
|
|
void DebugAudioImpact(const Vector3 &pos, s32 component1, s32 component2, f32 impulseMag, audCollisionType type)
|
|
{
|
|
if((g_DebugAudioImpacts || g_DebugAudioRolls || g_DebugAudioScrapes) && impulseMag >= g_DebugImpactThresh)
|
|
{
|
|
g_CollisionDebugInfo[g_CollisionDebugIndex].time = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
g_CollisionDebugInfo[g_CollisionDebugIndex].position = pos;
|
|
g_CollisionDebugInfo[g_CollisionDebugIndex].component1 = component1;
|
|
g_CollisionDebugInfo[g_CollisionDebugIndex].component2 = component2;
|
|
g_CollisionDebugInfo[g_CollisionDebugIndex].impulseMag = impulseMag;
|
|
g_CollisionDebugInfo[g_CollisionDebugIndex].type = type;
|
|
g_CollisionDebugInfo[g_CollisionDebugIndex].hidden = false;
|
|
|
|
g_CollisionDebugIndex = (g_CollisionDebugIndex+1)%g_NumDebugCollisionInfos;
|
|
}
|
|
}
|
|
|
|
//Used for rag-rave point and click Macs creation
|
|
struct ModelAudioSettingsSetup
|
|
{
|
|
u32 numComponents;
|
|
u32 numMaterials;
|
|
u32 model;
|
|
char audioSettingsName[64];
|
|
char materialNames[64][64];
|
|
};
|
|
|
|
|
|
|
|
static ModelAudioSettingsSetup s_ModelSettingsCreator;
|
|
atArray<MaterialAudName> g_audCollisionMaterialNames;
|
|
static char s_PointAndClickMaterial[64] = {0};
|
|
static char s_PointAndClickMaterialOverride[64] = {0};
|
|
static char s_PointAndClickComponent[32] = "No frag selected";
|
|
static s32 s_PointAndClickComponentId = 0;
|
|
static Vector3 s_PointAndClickPos;
|
|
static float s_SimulateCollisionVel = 0.f;
|
|
static s32 s_SimulateCollisionWeight1 = AUDIO_WEIGHT_M;
|
|
static s32 s_SimulateCollisionWeight2 = AUDIO_WEIGHT_M;
|
|
|
|
static CEntity * s_PointAndClickEntity = NULL;
|
|
static bool s_SimulateCollisionSoftImpact = false;
|
|
static char s_AudioWeightNames[NUM_AUDIOWEIGHT][32];
|
|
|
|
//#define MODEL_NAME_
|
|
//static char** g_NewModelDisplayObjectList;
|
|
//char gCurrentDisplayObject[STORE_NAME_LENGTH];
|
|
//static char** g_SyncModelDisplayObjectList;
|
|
//static char** g_NewFragModelDisplayObjectList;
|
|
|
|
static RegdEnt s_SpawnedCollisionProp;
|
|
static bool s_SpawnRavePropThisFrame;
|
|
#endif // __BANK
|
|
|
|
bool audCollisionAudioEntity::sm_EnableModelEditor = false;
|
|
|
|
bool g_DebugAudioCollisions = false;
|
|
f32 g_MaxRollSpeed = 3.f;
|
|
f32 g_RollVolumeSmoothUp = 0.05f;
|
|
f32 g_RollVolumeSmoothDown = 0.05f;
|
|
|
|
atArray<CollisionMaterialSettings*> g_audCollisionMaterials;
|
|
fwPtrListSingleLink g_RecentlyCollidedMaterials;
|
|
|
|
CollisionMaterialSettings* g_audBikeMetalMaterial;
|
|
CollisionMaterialSettings *g_audGrenadeMaterial;
|
|
ModelAudioCollisionSettings *g_MacsDefault = NULL;
|
|
|
|
void audCollisionAudioEntity::Init()
|
|
{
|
|
naAudioEntity::Init();
|
|
audEntity::SetName("audCollisionAudioEntity");
|
|
|
|
m_CollisionEventHistoryAllocation.Reset();
|
|
m_CollisionEventFreeList.MakeAllFree();
|
|
|
|
m_CachedBulletImpacts.Reset();
|
|
m_CachedMeleeImpacts.Reset();
|
|
m_CachedFragmentBreaks.Reset();
|
|
m_CachedUprootEvents.Reset();
|
|
|
|
// made these local scope since they shouldn't be used outwith this function
|
|
const u32 maxMaterialSettingsNameLength = 64;
|
|
const char *baseMaterialSettingsNamePrefix = "AM_BASE_";
|
|
|
|
for(u32 eventIndex=0; eventIndex<g_MaxCollisionEventHistory; eventIndex++)
|
|
{
|
|
m_CollisionEventHistory[eventIndex].Init();
|
|
}
|
|
|
|
m_MeleeDamageImpactScalingCurve.Init(ATSTRINGHASH("MELEE_DAMAGE_TO_VOLUME", 0x7C5035A6));
|
|
m_MeleeFootstepImpactScalingCurve.Init(ATSTRINGHASH("MELEE_FOOTSTEP_TO_COLLISION_IMPACT", 0x9992D936));
|
|
m_UppedCollisionCategoryNameHash =ATSTRINGHASH("PEDS_COLLISIONS_LOUD_DYNAMIC", 0xE8C16DEA);
|
|
|
|
m_CustomMaterialIdFootLeft = PGTAMATERIALMGR->GetMaterialId(PGTAMATERIALMGR->FindMaterial(g_CustomMaterialNameFootLeft));
|
|
m_CustomMaterialIdFootRight = PGTAMATERIALMGR->GetMaterialId(PGTAMATERIALMGR->FindMaterial(g_CustomMaterialNameFootRight));
|
|
m_MaterialIdCarBody = PGTAMATERIALMGR->GetMaterialId(PGTAMATERIALMGR->FindMaterial(g_MaterialNameCarBody));
|
|
m_MaterialIdCarTyre = PGTAMATERIALMGR->GetMaterialId(PGTAMATERIALMGR->FindMaterial(g_MaterialNameCarTyre));
|
|
|
|
g_audCollisionMaterials.Resize(PGTAMATERIALMGR->GetNumMaterials());
|
|
|
|
BANK_ONLY(g_audCollisionMaterialNames.Resize(PGTAMATERIALMGR->GetNumMaterials()));
|
|
|
|
char materialSettingsName[maxMaterialSettingsNameLength];
|
|
for(u32 i = 0; i < static_cast<u32>(PGTAMATERIALMGR->GetNumMaterials()); i++)
|
|
{
|
|
const phMaterial &mat = PGTAMATERIALMGR->GetMaterial(i);
|
|
//Generate material settings game object name from material name.
|
|
formatf(materialSettingsName, "%s%s", baseMaterialSettingsNamePrefix, mat.GetName());
|
|
|
|
g_audCollisionMaterials[i] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(materialSettingsName);
|
|
if(!g_audCollisionMaterials[i])
|
|
{
|
|
naDisplayf("Missing audio material: %u %s", i, materialSettingsName);
|
|
g_audCollisionMaterials[i] = g_audCollisionMaterials[0];
|
|
naAssert(g_audCollisionMaterials[i]);
|
|
|
|
BANK_ONLY(strncpy(g_audCollisionMaterialNames[i].name, g_audCollisionMaterialNames[0].name, 64));
|
|
}
|
|
else
|
|
{
|
|
g_audCollisionMaterials[i]->MaterialID = i;
|
|
|
|
#if __BANK
|
|
strncpy(g_audCollisionMaterialNames[i].name, materialSettingsName, 64);
|
|
#endif
|
|
}
|
|
|
|
|
|
if(g_PedMaterialId == ~0U && PGTAMATERIALMGR->GetIsPed(i))
|
|
{
|
|
g_PedMaterialId = i;
|
|
}
|
|
}
|
|
|
|
// if a ped material hasn't been found then initialise it to zero
|
|
if (g_PedMaterialId >= (u32)g_audCollisionMaterials.GetCount())
|
|
{
|
|
g_PedMaterialId = 0;
|
|
}
|
|
|
|
g_audBikeMetalMaterial = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_BIKE_METAL", 0x61ABD259));
|
|
|
|
m_ImpactVolCurve.Init(ATSTRINGHASH("COLLISION_IMPACT_VOLUME", 0x4724E47F));
|
|
m_PedImpactVolCurve.Init(ATSTRINGHASH("COLLISION_PED_IMPACT_VOLUME", 0x104C3F26));
|
|
m_VehicleImpactVolCurve.Init(ATSTRINGHASH("COLLISION_VEHICLE_IMPACT_VOLUME", 0xF9E0D66));
|
|
m_VehicleImpactStartOffsetCurve.Init(ATSTRINGHASH("COLLISION_VEHICLE_IMPACT_START_OFFSET", 0x478DF76B));
|
|
m_ImpactStartOffsetCurve.Init(ATSTRINGHASH("COLLISION_IMPACT_START_OFFSET", 0xD081341));
|
|
m_PedImpactStartOffsetCurve.Init(ATSTRINGHASH("COLLISION_PED_IMPACT_START_OFFSET", 0x44E5E0B3));
|
|
m_ScrapeVolCurve.Init(ATSTRINGHASH("COLLISION_SCRAPE_VOLUME", 0x77D65D51));
|
|
m_SwingVolCurve.Init(ATSTRINGHASH("COLLISION_SWING_VOLUME", 0x65D4BFA2));
|
|
m_PedScrapeVolCurve.Init(ATSTRINGHASH("COLLISION_PED_SCRAPE_VOLUME", 0xC84D1FCE));
|
|
m_PlayerScrapeVolCurve.Init(ATSTRINGHASH("COLLISION_PLAYER_SCRAPE_VOLUME", 0x9FA4B3A0));
|
|
m_ScrapePitchCurve.Init(ATSTRINGHASH("COLLISION_SCRAPE_PITCH", 0x8682356F));
|
|
m_PedScrapePitchCurve.Init(ATSTRINGHASH("COLLISION_PED_SCRAPE_PITCH", 0xC29DDFED));
|
|
m_PlayerScrapePitchCurve.Init(ATSTRINGHASH("COLLISION_PLAYER_SCRAPE_PITCH", 0xDCEAFC5F));
|
|
m_VehicleScrapeCurve.Init(ATSTRINGHASH("COLLISION_VEHICLE_SCRAPE_VOLUME", 0x6B53544B));
|
|
m_VehicleScrapePitchCurve.Init(ATSTRINGHASH("COLLISION_VEHICLE_SCRAPE_PITCH", 0x4BB53B02));
|
|
m_RollVolCurve.Init(ATSTRINGHASH("COLLISION_ROLL_VOLUME", 0x9EC036B5));
|
|
m_RollPitchCurve.Init(ATSTRINGHASH("COLLISION_ROLL_PITCH", 0xDA4E293D));
|
|
m_RollResVolCurve.Init(ATSTRINGHASH("COLLISION_ROLL_RES_VOLUME", 0x14DB8CFE));
|
|
m_RollResPitchCurve.Init(ATSTRINGHASH("COLLISION_ROLL_RES_PITCH", 0xF710144F));
|
|
|
|
|
|
g_UppedCollisionCategory = g_AudioEngine.GetCategoryManager().GetCategoryPtr(m_UppedCollisionCategoryNameHash);
|
|
g_PedCollisionCategory = g_AudioEngine.GetCategoryManager().GetCategoryPtr(ATSTRINGHASH("PEDS_COLLISIONS", 0xACBC84EA));
|
|
g_DoorsCategory = g_AudioEngine.GetCategoryManager().GetCategoryPtr(ATSTRINGHASH("COLLISIONS", 0x9644F744)); //the is no door specific category at the moment so we'll just use the collision cat
|
|
|
|
g_VehiclePedMeleeMaterial = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_VEHICLE_PED_MELEE_MATERIAL", 0xF9FD6ECA));
|
|
g_audGrenadeMaterial = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_WEAPONS_GRENADE", 0x15F77876));
|
|
g_MacsDefault = audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(ATSTRINGHASH("MOD_DEFAULT", 0x5F03E8C2));
|
|
|
|
g_NullMaterialRef = audNorthAudioEngine::GetMetadataManager().GetObjectMetadataRefFromHash(g_NullMaterialHash);
|
|
|
|
#if __BANK
|
|
sysMemSet(&s_ModelSettingsCreator, 0, sizeof(s_ModelSettingsCreator));
|
|
for(s32 i = 0; i < NUM_AUDIOWEIGHT; ++i)
|
|
{
|
|
strcpy(s_AudioWeightNames[i], AudioWeight_ToString((AudioWeight)i));
|
|
}
|
|
if(PARAM_audiowidgets.Get())
|
|
{
|
|
audPedFootStepAudio::AddMaterialCollisionWidgets();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void audCollisionAudioEntity::InitClass()
|
|
{
|
|
sm_TimeBetweenShotsToRicoProb.Init(ATSTRINGHASH("TIME_BETWEEN_SHOTS_TO_RICCO_PROB", 0x222126F5));
|
|
sm_DistanceToBIPredelay.Init(ATSTRINGHASH("SQD_DISTANCE_TO_BI_PREDELAY", 0x1B11CC02));
|
|
sm_DistanceToHeadShotPredelay.Init(ATSTRINGHASH("SQD_DISTANCE_TO_PLAYER_HEADSHOT_PREDELAY", 0xE99A364));
|
|
sm_DistanceToRicochetsPredelay.Init(ATSTRINGHASH("SQD_DISTANCE_TO_RICOCHETS_PREDELAY", 0x7C0C3C8E));
|
|
}
|
|
|
|
void audCollisionAudioEntity::Shutdown()
|
|
{
|
|
audEntity::Shutdown();
|
|
}
|
|
|
|
void audCollisionAudioEntity::UpdateModelEditorSelector()
|
|
{
|
|
#if __BANK
|
|
|
|
//Code that enables point and click generation of Macs objects and also prints out useful
|
|
//information about materials, models, frags etc
|
|
if(sm_EnableModelEditor)
|
|
{
|
|
s32 physicsTypeFlags = ArchetypeFlags::GTA_ALL_TYPES_MOVER|ArchetypeFlags::GTA_RAGDOLL_TYPE|ArchetypeFlags::GTA_PROJECTILE_TYPE|ArchetypeFlags::GTA_BOX_VEHICLE_TYPE;
|
|
|
|
s32 component = -1;
|
|
bool checkMouseClickOnSelectEntity = (ioMouse::GetPressedButtons()&ioMouse::MOUSE_LEFT);
|
|
if(checkMouseClickOnSelectEntity)
|
|
{
|
|
s_PointAndClickEntity = NULL;
|
|
|
|
//Find the material clicked on
|
|
Vector3 mouseStart, mouseEnd;
|
|
CDebugScene::GetMousePointing(mouseStart, mouseEnd, false);
|
|
Vector3 StartPos;
|
|
|
|
WorldProbe::CShapeTestProbeDesc probeDesc;
|
|
WorldProbe::CShapeTestFixedResults<> probeResults;
|
|
probeDesc.SetResultsStructure(&probeResults);
|
|
probeDesc.SetStartAndEnd(mouseStart, mouseEnd);
|
|
probeDesc.SetIncludeFlags(physicsTypeFlags);
|
|
|
|
formatf(s_PointAndClickMaterial, "");
|
|
formatf(s_PointAndClickMaterialOverride, "");
|
|
//Reports the name of the material clicked on
|
|
if(WorldProbe::GetShapeTestManager()->SubmitTest(probeDesc))
|
|
{
|
|
phMaterialMgr::Id id = probeResults[0].GetHitMaterialId();
|
|
const phMaterial &mat = PGTAMATERIALMGR->GetMaterial(id);
|
|
strncpy(s_PointAndClickMaterial, mat.GetName(), 64);
|
|
s_PointAndClickPos = probeResults[0].GetHitPosition();
|
|
}
|
|
|
|
//If we've clicked on an entity display some interesting stuff and use it to set up the model creation settings
|
|
//It's not particularly pretty as it gradually accumulates bits and bobs, but it's only used in dev for model creation
|
|
//so doesn't need to be super slick
|
|
|
|
Vector3 offset = Vector3(0.f, 0.f, 0.f);
|
|
CEntity* pEntity = CDebugScene::GetEntityUnderMouse(&offset, &component, physicsTypeFlags);
|
|
if(pEntity)
|
|
{
|
|
s_PointAndClickEntity = pEntity;
|
|
// naDisplayf("");
|
|
char tmpstr[16] = {0};
|
|
s_PointAndClickComponentId = component;
|
|
sprintf(tmpstr, "%i", component);
|
|
strncpy(s_PointAndClickComponent, tmpstr, 32);
|
|
|
|
if(pEntity->GetFragInst()) //Do some display stuff and setup for frag props
|
|
{
|
|
fragInst *frag = pEntity->GetFragInst();
|
|
naDisplayf("Entity ptr : %p, Component : %i, numComponents: %i", pEntity, component, frag->GetTypePhysics()->GetNumChildren());
|
|
|
|
s_ModelSettingsCreator.numComponents = frag->GetTypePhysics()->GetNumChildren();
|
|
|
|
// fragTypeChild **components = pFragType->GetAllChildren();
|
|
naDisplayf("Attached components ===========");
|
|
for(s32 i=0; i<frag->GetTypePhysics()->GetNumChildren(); i++)
|
|
{
|
|
if(!frag->GetChildBroken(i))
|
|
{
|
|
naDisplayf(" Component %i", i);
|
|
}
|
|
}
|
|
naDisplayf("=====================");
|
|
phBoundComposite* compositeBound = static_cast<phBoundComposite*>(frag->GetArchetype()->GetBound());
|
|
naAssert(compositeBound && frag->GetArchetype()->GetBound()->GetType()==phBound::COMPOSITE);
|
|
naDisplayf("model has %i active bounds", compositeBound->GetNumActiveBounds());
|
|
naDisplayf("model has %i bounds", compositeBound->GetNumBounds());
|
|
}
|
|
else //Not a frag prop;
|
|
{
|
|
naDisplayf("Entity ptr : %p", pEntity);
|
|
}
|
|
|
|
if(pEntity->IsArchetypeSet())
|
|
{
|
|
CBaseModelInfo * model = pEntity->GetBaseModelInfo();
|
|
if(model) //If we can find a model for the entity we clicked on, populate the settings creator
|
|
{
|
|
naDisplayf("Selected model: %s", model->GetModelName());
|
|
|
|
const char *audioSettingsNamePrefix = "MOD_";
|
|
char audioSettingsName[64] = "";
|
|
strncpy(audioSettingsName, audioSettingsNamePrefix, 64);
|
|
strncat(audioSettingsName, model->GetModelName(), 64);
|
|
naDisplayf("Model settings ID: %s", audioSettingsName);
|
|
|
|
PopulateModelSettingsCreator(model, audioSettingsName, s_ModelSettingsCreator.numComponents);
|
|
}
|
|
else
|
|
{
|
|
naDisplayf("Couldn't get model from entity of type %i, model index: %d", pEntity->GetType(), pEntity->GetModelId().GetModelIndex());
|
|
strncpy(s_ModelSettingsCreator.audioSettingsName, "", 64);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
naDisplayf("Couldn't get model from entity of type %i, model %s", pEntity->GetType(), pEntity->GetModelName());
|
|
strncpy(s_ModelSettingsCreator.audioSettingsName, "", 64);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strncpy(s_ModelSettingsCreator.audioSettingsName, "", 64);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
void audCollisionAudioEntity::PreUpdateService(u32 timeInMs)
|
|
{
|
|
//Use sound manager timer to keep it in line with the rest of the collision code
|
|
timeInMs = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
|
|
#if __DEV
|
|
|
|
/*
|
|
if (g_AudioDebugEntity)
|
|
{
|
|
f32 maxImpulseMag0 = 0.0f;
|
|
f32 maxImpulseMag1 = 0.0f;
|
|
f32 numCollisions = 0.0f;
|
|
// look for collisions involving that entity
|
|
for(u32 eventIndex=0; eventIndex<g_MaxCollisionEventHistory; eventIndex++)
|
|
{
|
|
if(m_CollisionEventHistory[eventIndex].entities[0] == g_AudioDebugEntity || m_CollisionEventHistory[eventIndex].entities[1] == g_AudioDebugEntity)
|
|
{
|
|
maxImpulseMag0 = Max(m_CollisionEventHistory[eventIndex].impulseMagnitudes[0], maxImpulseMag0);
|
|
maxImpulseMag1 = Max(m_CollisionEventHistory[eventIndex].impulseMagnitudes[1], maxImpulseMag1);
|
|
numCollisions += 1.0f;
|
|
}
|
|
}
|
|
PF_SET(DebugNumCollisions, numCollisions);
|
|
PF_SET(DebugImpulseMag0, maxImpulseMag0);
|
|
PF_SET(DebugImpulseMag1, maxImpulseMag1);
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
PF_SET(DebugNumCollisions, 0.0f);
|
|
PF_SET(DebugImpulseMag0, 0.0f);
|
|
PF_SET(DebugImpulseMag1, 0.0f);
|
|
}
|
|
|
|
PF_SET(CollisionHistory, m_CollisionEventHistoryAllocation.CountOnBits());
|
|
#endif
|
|
|
|
sm_TimeSinceLastBulletHit += fwTimer::GetTimeStepInMilliseconds();
|
|
sm_TimeSinceLastAutomaticBulletHit += fwTimer::GetTimeStepInMilliseconds();
|
|
sm_TimeSinceLastShotgunBulletHit += fwTimer::GetTimeStepInMilliseconds();
|
|
for(u32 eventIndex=0; eventIndex<g_MaxCollisionEventHistory; eventIndex++)
|
|
{
|
|
const bool isAllocated = m_CollisionEventHistoryAllocation.IsSet(eventIndex);
|
|
if(isAllocated)
|
|
{
|
|
audCollisionEvent & collisionEvent = m_CollisionEventHistory[eventIndex];
|
|
|
|
//We can't start a roll or scrape without both entities so don't process if we've lost one
|
|
if(collisionEvent.entities[0] && collisionEvent.entities[1])
|
|
{
|
|
if(collisionEvent.type == AUD_COLLISION_TYPE_SCRAPE)
|
|
{
|
|
if(collisionEvent.GetFlag(AUD_COLLISION_FLAG_PLAY_SCRAPE))
|
|
{
|
|
if(!PlayScrapeSounds(&collisionEvent))
|
|
{
|
|
collisionEvent.scrapeStopTimeMs = 0;
|
|
}
|
|
collisionEvent.ClearFlag(AUD_COLLISION_FLAG_PLAY_SCRAPE);
|
|
}
|
|
UpdateScrapeSounds(&m_CollisionEventHistory[eventIndex]);
|
|
}
|
|
|
|
if(collisionEvent.type == AUD_COLLISION_TYPE_ROLL && collisionEvent.rollUpdatedThisFrame != 0)
|
|
{
|
|
if(collisionEvent.GetFlag(AUD_COLLISION_FLAG_PLAY_ROLL))
|
|
{
|
|
if(!PlayRollSounds(&collisionEvent))
|
|
{
|
|
collisionEvent.rollStopTimeMs = 0;
|
|
}
|
|
collisionEvent.ClearFlag(AUD_COLLISION_FLAG_PLAY_ROLL);
|
|
}
|
|
UpdateRollSounds(&collisionEvent);
|
|
}
|
|
}
|
|
|
|
if(timeInMs >= collisionEvent.scrapeStopTimeMs)
|
|
{
|
|
for(u8 i=0; i<2; i++)
|
|
{
|
|
if(collisionEvent.scrapeSounds[i] != NULL)
|
|
{
|
|
collisionEvent.scrapeSounds[i]->StopAndForget();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for(u8 i=0; i<2; i++)
|
|
{
|
|
if(collisionEvent.rollSounds[i] != NULL)
|
|
{
|
|
// See if it's been updated since last time, and if not, start ramping down it's volume. Then, either way, smooth its volume.
|
|
if (collisionEvent.rollUpdatedThisFrame == 0)
|
|
{
|
|
// Not been updated, so ramp down its volume
|
|
collisionEvent.rollSounds[i]->SetClientVariable(0.0f);
|
|
// If it's silent and we've been waiting a while, kill it
|
|
f32 playbackVolume = collisionEvent.rollSounds[i]->GetRequestedVolume();
|
|
if (playbackVolume<=g_SilenceVolume && timeInMs >= collisionEvent.rollStopTimeMs)
|
|
{
|
|
collisionEvent.rollSounds[i]->StopAndForget();
|
|
}
|
|
}
|
|
// Need to check we still have the sound, as we might have stopped it
|
|
|
|
if(collisionEvent.rollSounds[i] != NULL)
|
|
{
|
|
f32 volume = g_SilenceVolume;
|
|
collisionEvent.rollSounds[i]->GetClientVariable(volume);
|
|
f32 currentVolume = audDriverUtil::ComputeLinearVolumeFromDb(collisionEvent.rollSounds[i]->GetRequestedVolume());
|
|
f32 newVolume = 0.0f;
|
|
if (volume>currentVolume)
|
|
{
|
|
newVolume = Min(volume, (currentVolume + g_RollVolumeSmoothUp));
|
|
}
|
|
else
|
|
{
|
|
newVolume = Max(volume, (currentVolume - g_RollVolumeSmoothDown));
|
|
}
|
|
newVolume = Clamp(newVolume, 0.0f, 1.0f);
|
|
|
|
f32 newVolumedB = audDriverUtil::ComputeDbVolumeFromLinear(newVolume);
|
|
collisionEvent.rollSounds[i]->SetRequestedVolume(newVolumedB);
|
|
}
|
|
}
|
|
|
|
f32 rollVolume = 0.f, scrapeVolume = 0.f, blipVol = 0.f;
|
|
if(collisionEvent.rollSounds[i])
|
|
{
|
|
rollVolume = collisionEvent.rollSounds[i]->GetRequestedVolume();
|
|
}
|
|
if(collisionEvent.scrapeSounds[i])
|
|
{
|
|
scrapeVolume = collisionEvent.scrapeSounds[i]->GetRequestedVolume();
|
|
}
|
|
|
|
blipVol = Max(rollVolume, scrapeVolume);
|
|
|
|
if(blipVol > sm_PlayerContextVolumeForBlip)
|
|
{
|
|
audPlayerCollisionEvent * playerEvent = GetPlayerCollisionEventFromHistory(collisionEvent.entities[i]);
|
|
if(playerEvent)
|
|
{
|
|
playerEvent->lastImpactTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
playerEvent->blipMap = true;
|
|
playerEvent->position = VECTOR3_TO_VEC3V(collisionEvent.positions[i]);
|
|
playerEvent->blipAmount = audDriverUtil::ComputeLinearVolumeFromDb(blipVol);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mark it as not updated, so we know for next frame
|
|
collisionEvent.rollUpdatedThisFrame = 0;
|
|
|
|
}// if allocated
|
|
}// for each event
|
|
|
|
fwPtrNode * link = g_RecentlyCollidedMaterials.GetHeadPtr();
|
|
static u32 lastTime = 0;
|
|
|
|
f32 decrement = f32(g_CollisionCountDecreaseRate ) * (timeInMs-lastTime) * 0.001f;
|
|
|
|
if(decrement >= 1.f)
|
|
{
|
|
lastTime = timeInMs;
|
|
|
|
while(link)
|
|
{
|
|
CollisionMaterialSettings * material = (CollisionMaterialSettings*)link->GetPtr();
|
|
if(material->CollisionCount < decrement)
|
|
{
|
|
material->CollisionCount = 0;
|
|
material->VolumeThreshold = -100.f;
|
|
fwPtrNode * next = link->GetNextPtr();
|
|
link = next;
|
|
g_RecentlyCollidedMaterials.Remove(material);
|
|
}
|
|
else
|
|
{
|
|
material->CollisionCount -= (u32)(decrement);
|
|
material->VolumeThreshold -= decrement*10.f;
|
|
link = link->GetNextPtr();
|
|
}
|
|
}
|
|
}
|
|
#if __BANK
|
|
if( g_IncreaseFootstepsVolume)
|
|
{
|
|
if( !sm_IncreaseFootstepVolumeScene )
|
|
{
|
|
DYNAMICMIXER.StartScene(ATSTRINGHASH("PEDS_FOOTSTEP_DEBUG_SCENE", 0x6BD3FD2C),&sm_IncreaseFootstepVolumeScene);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( sm_IncreaseFootstepVolumeScene )
|
|
{
|
|
sm_IncreaseFootstepVolumeScene->Stop();
|
|
sm_IncreaseFootstepVolumeScene = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void audCollisionAudioEntity::ProcessMacsModelOverrides()
|
|
{
|
|
for(u32 chunkId = 0; chunkId < audNorthAudioEngine::GetMetadataManager().GetNumChunks(); chunkId++)
|
|
{
|
|
if(audNorthAudioEngine::GetMetadataManager().GetChunk(chunkId).enabled)
|
|
{
|
|
#if __BANK
|
|
if(PARAM_audiotag.Get())
|
|
{
|
|
g_CollisionAudioEntity.ReconnectPropMacsForChunk(chunkId);
|
|
}
|
|
#endif
|
|
g_CollisionAudioEntity.ProcessMacsModelOverridesForChunk(chunkId, ATSTRINGHASH("MACS_MODELS_OVERRIDES", 0x53F9AB71));
|
|
g_CollisionAudioEntity.ProcessMacsModelOverridesForChunk(chunkId, ATSTRINGHASH("MACS_MODEL_OVERRIDE_LIST_GEN9", 0x149D39FA));
|
|
}
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::ProcessMacsModelOverridesForChunk(u32 chunkId, u32 modelListHash)
|
|
{
|
|
audMetadataObjectInfo info;
|
|
audNorthAudioEngine::GetMetadataManager().GetObjectInfoFromSpecificChunk(modelListHash, chunkId, info);
|
|
if (info.GetObjectUntyped() && info.GetType() == MacsModelOverrideList::TYPE_ID)
|
|
{
|
|
const MacsModelOverrideList* overrides = info.GetObject<MacsModelOverrideList>();
|
|
for (int index = 0; index < overrides->numEntrys; index++)
|
|
{
|
|
CBaseModelInfo *base = CModelInfo::GetBaseModelInfoFromHashKey(overrides->Entry[index].Model, NULL);
|
|
//We only want to override models that don't have custom so that we can cleanly remove them later
|
|
if (base && (!base->GetAudioCollisionSettings() || base->GetAudioCollisionSettings() == g_MacsDefault))
|
|
|
|
{
|
|
auto mapOverride = sm_MacOverrideMap.Access(overrides->Entry[index].Model);
|
|
if (!mapOverride)
|
|
{
|
|
mapOverride = &sm_MacOverrideMap.Insert(overrides->Entry[index].Model).data;
|
|
}
|
|
(*mapOverride) = overrides->Entry[index].Macs;
|
|
|
|
BANK_ONLY(audDisplayf("Added MACS model override for model %s (MACS %s) from chunk %d", base->GetModelName(), audNorthAudioEngine::GetMetadataManager().GetObjectName(overrides->Entry[index].Macs), chunkId);)
|
|
}
|
|
else
|
|
{
|
|
BANK_ONLY(audWarningf("Failed to override model %u with MACS %s", overrides->Entry[index].Model, audNorthAudioEngine::GetMetadataManager().GetObjectName(overrides->Entry[index].Macs));)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::RemoveMacsModelOverridesForChunk(u32 chunkId, u32 modelListHash)
|
|
{
|
|
audMetadataObjectInfo info;
|
|
audNorthAudioEngine::GetMetadataManager().GetObjectInfoFromSpecificChunk(modelListHash, chunkId, info);
|
|
if(info.GetObjectUntyped() && info.GetType() == MacsModelOverrideList::TYPE_ID)
|
|
{
|
|
const MacsModelOverrideList* overrides = info.GetObject<MacsModelOverrideList>();
|
|
for(int index=0; index < overrides->numEntrys; index++)
|
|
{
|
|
CBaseModelInfo *base = CModelInfo::GetBaseModelInfoFromHashKey(overrides->Entry[index].Model, NULL);
|
|
if(base)
|
|
{
|
|
base->SetAudioCollisionSettings(audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(ATSTRINGHASH("MOD_DEFAULT", 0x05f03e8c2)), 0);
|
|
}
|
|
auto mapOverride = sm_MacOverrideMap.Access(overrides->Entry[index].Model);
|
|
if(mapOverride)
|
|
{
|
|
(*mapOverride) = ATSTRINGHASH("MOD_DEFAULT", 0x05f03e8c2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if __BANK
|
|
void audCollisionAudioEntity::ReconnectPropMacsForChunk(u32 chunkId)
|
|
{
|
|
for(int i=0; i < audNorthAudioEngine::GetMetadataManager().ComputeNumberOfObjects(); i++)
|
|
{
|
|
audMetadataObjectInfo info;
|
|
audNorthAudioEngine::GetMetadataManager().GetObjectInfoFromObjectIndex(i, info);
|
|
if(info.GetType() == ModelAudioCollisionSettings::TYPE_ID && info.GetChunkId() == chunkId)
|
|
{
|
|
CBaseModelInfo *base = CModelInfo::GetBaseModelInfoFromHashKey(atStringHash(&(info.GetName_Debug()[4])), NULL);
|
|
if(base)
|
|
{
|
|
base->SetAudioCollisionSettings(info.GetObject<ModelAudioCollisionSettings>(), atStringHash(info.GetName_Debug()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
void audCollisionAudioEntity::PostUpdate()
|
|
{
|
|
#if __BANK
|
|
if(s_UpdateCreatedSettings)
|
|
{
|
|
if(audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(s_ModelSettingsCreator.audioSettingsName))
|
|
{
|
|
CModelInfo::GetBaseModelInfoFromHashKey(s_ModelSettingsCreator.model, NULL)->SetAudioCollisionSettings(audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(s_ModelSettingsCreator.audioSettingsName), atStringHash(s_ModelSettingsCreator.audioSettingsName));
|
|
s_UpdateCreatedSettings = false;
|
|
}
|
|
}
|
|
|
|
if(s_SpawnRavePropThisFrame)
|
|
{
|
|
s_SpawnRavePropThisFrame = false;
|
|
SpawnRaveSettings();
|
|
}
|
|
|
|
GenerateWorldCollisionLists();
|
|
#endif
|
|
|
|
for(u32 eventIndex=0; eventIndex<g_MaxCollisionEventHistory; eventIndex++)
|
|
{
|
|
const bool isAllocated = m_CollisionEventHistoryAllocation.IsSet(eventIndex);
|
|
if(isAllocated)
|
|
{
|
|
audCollisionEvent & collisionEvent = m_CollisionEventHistory[eventIndex];
|
|
if( collisionEvent.rollSounds[0] == NULL &&
|
|
collisionEvent.rollSounds[1] == NULL &&
|
|
collisionEvent.scrapeSounds[0] == NULL &&
|
|
collisionEvent.scrapeSounds[1] == NULL
|
|
)
|
|
{
|
|
//naErrorf("Clearing collision time %d event materials %s, %s, entities %p, %p", audNorthAudioEngine::GetCurrentTimeInMs(), collisionEvent.materialSettings[0] ? audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(collisionEvent.materialSettings[0]->NameTableOffset) : "", collisionEvent.materialSettings[1]? audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(collisionEvent.materialSettings[1]->NameTableOffset): "", collisionEvent.entities[0], collisionEvent.entities[1]);
|
|
|
|
//We no longer have any active collision sounds, so NULL this event.
|
|
collisionEvent.Init();
|
|
m_CollisionEventHistoryAllocation.Clear(eventIndex);
|
|
m_CollisionEventFreeList.Free(eventIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
u32 now = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
|
|
for(int i=0; i < g_MaxSwingEventHistory; i++)
|
|
{
|
|
if(m_SwingEventHistoryAllocation.IsSet(i))
|
|
{
|
|
bool shouldRemoveEvent = true;
|
|
audSwingEvent &swingEvent = m_SwingEventHistory[i];
|
|
if(swingEvent.swingEnt)
|
|
{
|
|
const ModelAudioCollisionSettings * swingMacs = swingEvent.swingEnt->GetBaseModelInfo()->GetAudioCollisionSettings();
|
|
f32 swingSpeed = ((CPhysical*)swingEvent.swingEnt.Get())->GetVelocity().Mag()*1000.f;
|
|
|
|
f32 swingVolume = m_SwingVolCurve.CalculateRescaledValue(0.f, 1.f, swingMacs->MinSwingVel, swingMacs->MaxswingVel, swingSpeed);
|
|
swingEvent.volSmoother.SetRate(g_SwingVolSmoothRate);
|
|
f32 smoothedVol = swingEvent.volSmoother.CalculateValue(swingVolume, now-swingEvent.lastProcessTime);
|
|
f32 swingVolumeDb = audDriverUtil::ComputeDbVolumeFromLinear(smoothedVol);
|
|
|
|
#if __BANK
|
|
if(g_DebugSwingingProps)
|
|
{
|
|
naDisplayf("Swing speed %f, volumeDb %f on prop %s (%p)", swingSpeed, swingVolumeDb, swingEvent.swingEnt->GetModelName(), swingEvent.swingEnt.Get());
|
|
}
|
|
#endif
|
|
|
|
if(swingVolumeDb > -60.f)
|
|
{
|
|
|
|
shouldRemoveEvent = false;
|
|
bool needsToPlay = false;
|
|
if(!swingEvent.swingSound)
|
|
{
|
|
audSoundInitParams initParams;
|
|
initParams.EnvironmentGroup = swingEvent.swingEnt->GetAudioEnvironmentGroup();
|
|
CreateSound_PersistentReference(swingMacs->SwingSound, &swingEvent.swingSound, &initParams);
|
|
needsToPlay = true;
|
|
}
|
|
|
|
if(swingEvent.swingSound)
|
|
{
|
|
swingEvent.swingSound->SetRequestedVolume(swingVolumeDb);
|
|
swingEvent.swingSound->SetRequestedPosition(swingEvent.swingEnt->GetTransform().GetPosition());
|
|
swingEvent.swingSound->FindAndSetVariableValue(ATSTRINGHASH("LastImpactTime", 0x601C8B48),(f32)(now - swingEvent.lastImpactTime));
|
|
if(needsToPlay)
|
|
{
|
|
swingEvent.swingSound->PrepareAndPlay();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(shouldRemoveEvent)
|
|
{
|
|
swingEvent.Init();
|
|
m_SwingEventHistoryAllocation.Clear(i);
|
|
m_SwingEventFreeList.Free(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int i=0; i < g_MaxPlayerCollisionEventHistory; i++)
|
|
{
|
|
if(m_PlayerCollisionEventHistoryAllocation.IsSet(i))
|
|
{
|
|
audPlayerCollisionEvent & playerEvent = m_PlayerCollisionEventHistory[i];
|
|
bool shouldRemoveEvent = false;
|
|
|
|
if(playerEvent.hitEntity && playerEvent.hitEntity->GetIsPhysical())
|
|
{
|
|
CPhysical * physical = (CPhysical*)playerEvent.hitEntity.Get();
|
|
f32 speedSqr = physical->GetVelocity().Mag2();
|
|
|
|
if(speedSqr > sm_PlayerCollisionEventSpeedThreshold)
|
|
{
|
|
if(playerEvent.blipMap)
|
|
{
|
|
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
|
|
if (pPlayerPed)
|
|
{
|
|
playerEvent.position = pPlayerPed->GetTransform().GetPosition();
|
|
CMiniMap::CreateSonarBlipAndReportStealthNoise(pPlayerPed, playerEvent.position, CMiniMap::sm_Tunables.Sonar.fSoundRange_ObjectCollision * playerEvent.blipAmount, HUD_COLOUR_BLUEDARK, true, false);
|
|
}
|
|
}
|
|
|
|
playerEvent.lastImpactTime = now;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
shouldRemoveEvent = true;
|
|
}
|
|
|
|
if(now - playerEvent.lastImpactTime > sm_PlayerCollisionEventTimeout)
|
|
{
|
|
shouldRemoveEvent = true;
|
|
}
|
|
|
|
playerEvent.blipMap = false;
|
|
|
|
if(shouldRemoveEvent)
|
|
{
|
|
playerEvent.Init();
|
|
m_PlayerCollisionEventHistoryAllocation.Clear(i);
|
|
m_PlayerCollisionEventFreeList.Free(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if __BANK
|
|
void SpawnModel(CBaseModelInfo * pModel, fwModelId modelId)
|
|
{
|
|
//Spawn in an instance of the object so we can set up the MACS using the model editor
|
|
|
|
if(s_SpawnedCollisionProp)
|
|
{
|
|
if(CGameWorld::IsDeleteAllowed())
|
|
{
|
|
CGameWorld::Remove(s_SpawnedCollisionProp);
|
|
|
|
if(s_SpawnedCollisionProp->GetIsTypeObject())
|
|
{
|
|
CObjectPopulation::DestroyObject((CObject*)s_SpawnedCollisionProp.Get());
|
|
}
|
|
else
|
|
{
|
|
delete s_SpawnedCollisionProp;
|
|
}
|
|
}
|
|
|
|
s_SpawnedCollisionProp = NULL;
|
|
}
|
|
|
|
Matrix34 testMat;
|
|
testMat.Identity();
|
|
|
|
Vector3 vecCreateOffset = camInterface::GetFront();
|
|
vecCreateOffset.z = 0.0f;
|
|
vecCreateOffset *= 2.f + pModel->GetBoundingSphereRadius();
|
|
|
|
camDebugDirector & debugDirector = camInterface::GetDebugDirector();
|
|
testMat.d = debugDirector.IsFreeCamActive() ? camInterface::GetPos() : CGameWorld::FindLocalPlayerCoors();
|
|
testMat.d += vecCreateOffset;
|
|
|
|
if(debugDirector.IsFreeCamActive())
|
|
{
|
|
testMat.Set3x3(camInterface::GetMat());
|
|
}
|
|
else if(CGameWorld::FindLocalPlayer())
|
|
{
|
|
testMat.Set3x3(MAT34V_TO_MATRIX34(CGameWorld::FindLocalPlayer()->GetMatrix()));
|
|
}
|
|
|
|
if(pModel)
|
|
{
|
|
bool bForceLoad = false;
|
|
if(!CModelInfo::HaveAssetsLoaded(modelId))
|
|
{
|
|
CModelInfo::RequestAssets(modelId, STRFLAG_DONTDELETE|STRFLAG_FORCE_LOAD|STRFLAG_PRIORITY_LOAD);
|
|
bForceLoad = true;
|
|
}
|
|
|
|
if(bForceLoad)
|
|
{
|
|
CStreaming::LoadAllRequestedObjects(true);
|
|
}
|
|
|
|
if(pModel->GetIsTypeObject())
|
|
{
|
|
s_SpawnedCollisionProp = CObjectPopulation::CreateObject(modelId, ENTITY_OWNEDBY_RANDOM, true);
|
|
}
|
|
else
|
|
{
|
|
if (pModel->GetModelType() == MI_TYPE_COMPOSITE)
|
|
{
|
|
s_SpawnedCollisionProp = rage_new CCompEntity( ENTITY_OWNEDBY_AUDIO );
|
|
}
|
|
else if(pModel->GetClipDictionaryIndex() != -1 && pModel->GetHasAnimation())
|
|
{
|
|
s_SpawnedCollisionProp = rage_new CAnimatedBuilding( ENTITY_OWNEDBY_AUDIO );
|
|
}
|
|
else
|
|
{
|
|
s_SpawnedCollisionProp = rage_new CBuilding( ENTITY_OWNEDBY_DEBUG );
|
|
}
|
|
s_SpawnedCollisionProp->SetModelId(modelId);
|
|
}
|
|
}
|
|
|
|
if(s_SpawnedCollisionProp)
|
|
{
|
|
if (!s_SpawnedCollisionProp->GetTransform().IsMatrix())
|
|
{
|
|
#if ENABLE_MATRIX_MEMBER
|
|
Mat34V m(V_IDENTITY);
|
|
s_SpawnedCollisionProp->SetTransform(m);
|
|
s_SpawnedCollisionProp->SetTransformScale(1.0f, 1.0f);
|
|
|
|
#else
|
|
fwTransform *trans = rage_new fwMatrixTransform();
|
|
s_SpawnedCollisionProp->SetTransform(trans);
|
|
#endif
|
|
}
|
|
|
|
s_SpawnedCollisionProp->SetMatrix(testMat, true);
|
|
|
|
if (fragInst* pInst = s_SpawnedCollisionProp->GetFragInst())
|
|
{
|
|
pInst->SetResetMatrix(testMat);
|
|
}
|
|
|
|
CGameWorld::Add(s_SpawnedCollisionProp, CGameWorld::OUTSIDE );
|
|
}
|
|
|
|
// create physics for static xrefs
|
|
if (s_SpawnedCollisionProp && s_SpawnedCollisionProp->GetIsTypeBuilding()
|
|
&& s_SpawnedCollisionProp->GetCurrentPhysicsInst()==NULL && s_SpawnedCollisionProp->IsBaseFlagSet(fwEntity::HAS_PHYSICS_DICT))
|
|
{
|
|
// create physics
|
|
s_SpawnedCollisionProp->InitPhys();
|
|
if (s_SpawnedCollisionProp->GetCurrentPhysicsInst())
|
|
{
|
|
s_SpawnedCollisionProp->AddPhysics();
|
|
}
|
|
}
|
|
|
|
//If the entity being is an env cloth then we need to transform the cloth
|
|
//verts to the entity frame.
|
|
if(s_SpawnedCollisionProp && s_SpawnedCollisionProp->GetCurrentPhysicsInst() && s_SpawnedCollisionProp->GetCurrentPhysicsInst()->GetArchetype())
|
|
{
|
|
// TODO: Should this really be checking if it has the GTA_ENVCLOTH_OBJECT_TYPE flag, not that it has *only* the GTA_ENVCLOTH_OBJECT_TYPE flag?
|
|
if(PHLEVEL->GetInstanceTypeFlags(s_SpawnedCollisionProp->GetCurrentPhysicsInst()->GetLevelIndex()) == ArchetypeFlags::GTA_ENVCLOTH_OBJECT_TYPE)
|
|
{
|
|
//Now transform the cloth verts to the frame of the cloth entity.
|
|
phInst* pEntityInst=s_SpawnedCollisionProp->GetCurrentPhysicsInst();
|
|
Assertf(dynamic_cast<fragInst*>(pEntityInst), "Physical with cloth must be a frag");
|
|
fragInst* pEntityFragInst=static_cast<fragInst*>(pEntityInst);
|
|
Assertf(pEntityFragInst->GetCached(), "Cloth entity has no cache entry");
|
|
if(pEntityFragInst->GetCached())
|
|
{
|
|
fragCacheEntry* pEntityCacheEntry=pEntityFragInst->GetCacheEntry();
|
|
Assertf(pEntityCacheEntry, "Cloth entity has a null cache entry");
|
|
fragHierarchyInst* pEntityHierInst=pEntityCacheEntry->GetHierInst();
|
|
Assertf(pEntityHierInst, "Cloth entity has a null hier inst");
|
|
environmentCloth* pEnvCloth=pEntityHierInst->envCloth;
|
|
Matrix34 clothMatrix=MAT34V_TO_MATRIX34(s_SpawnedCollisionProp->GetMatrix());
|
|
|
|
pEnvCloth->SetInitialPosition( VECTOR3_TO_VEC3V(clothMatrix.d) );
|
|
}
|
|
}
|
|
}// if(s_SpawnedCollisionProp && s_SpawnedCollisionProp->GetCurrentPhysicsInst() && s_SpawnedCollisionProp->GetCurrentPhysicsInst()->GetArchetype())...
|
|
|
|
if (s_SpawnedCollisionProp && s_SpawnedCollisionProp->GetCurrentPhysicsInst())
|
|
{
|
|
PHSIM->ActivateObject(s_SpawnedCollisionProp->GetCurrentPhysicsInst());
|
|
}
|
|
|
|
u32 index = modelId.GetModelIndex();
|
|
Displayf("[NorthAudio] MACS editor: Spawned %s (index %d)", fwArchetypeManager::GetArchetypeName(index), index);
|
|
|
|
if(s_SpawnedCollisionProp && s_SpawnedCollisionProp->GetIsTypeObject())
|
|
{
|
|
(static_cast<CObject*>(s_SpawnedCollisionProp.Get()))->PlaceOnGroundProperly();
|
|
}
|
|
|
|
}
|
|
|
|
void SpawnPropFromPropName()
|
|
{
|
|
fwModelId modelId;
|
|
|
|
CBaseModelInfo * pModel = CModelInfo::GetBaseModelInfoFromName(s_PropSpawnName, &modelId);
|
|
if(!pModel)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Check if the model is using MOD_DEFAULT, has physics and is not a weapon or a ped or a vehicle
|
|
//if(!strcmp(pModel->GetModelName(), s_PropSpawnName))
|
|
{
|
|
SpawnModel(pModel, modelId);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::SpawnRaveSettings()
|
|
{
|
|
if(!s_RaveSelectedMaterialSettings || !s_RaveSelectedMaterialSettingsName[0])
|
|
{
|
|
naDisplayf("NO RAVE MODEL SETTINGS SELECTED");
|
|
return;
|
|
}
|
|
|
|
#if 0 //useful for debug
|
|
const u32 maxAudioSettingsNameLength = 128;
|
|
const char *audioSettingsNamePrefix = "MOD_";
|
|
char audioSettingsName[maxAudioSettingsNameLength];
|
|
#endif
|
|
|
|
//Iterate through all loaded models and fix any that have gotten out of synch
|
|
//const u32 maxModelInfos = fwArchetypeManager::GetMaxArchetypeIndex();
|
|
fwModelId modelId;
|
|
CBaseModelInfo* pModel = CModelInfo::GetBaseModelInfoFromName(&s_RaveSelectedMaterialSettingsName[4], &modelId);
|
|
|
|
if(!pModel)
|
|
{
|
|
naErrorf("Couldn't get model info for %s when spawning rave settings; perhaps it doesn't exist?", &s_RaveSelectedMaterialSettingsName[4]);
|
|
return;
|
|
}
|
|
|
|
SpawnModel(pModel, modelId);
|
|
return;
|
|
}
|
|
|
|
|
|
void audCollisionAudioEntity::HandleRaveSelectedMaterial(const ModelAudioCollisionSettings * settings)
|
|
{
|
|
if(settings)
|
|
{
|
|
s_RaveSelectedMaterialSettings = settings;
|
|
formatf(s_RaveSelectedMaterialSettingsName,"%s", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(settings->NameTableOffset));
|
|
|
|
if(s_AutoSpawnRaveObjects && sm_EnableModelEditor)
|
|
{
|
|
s_SpawnRavePropThisFrame = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::SimulateCollision()
|
|
{
|
|
audCollisionEvent collisionEvent;
|
|
collisionEvent.Init();
|
|
|
|
collisionEvent.impulseMagnitudes[0] = s_SimulateCollisionVel * GetAudioWeightScaling((AudioWeight)s_SimulateCollisionWeight1, (AudioWeight)s_SimulateCollisionWeight2);
|
|
collisionEvent.impulseMagnitudes[1] = 0.f;
|
|
collisionEvent.ClearFlag(AUD_COLLISION_FLAG_MELEE);
|
|
collisionEvent.entities[0] = s_PointAndClickEntity;
|
|
collisionEvent.positions[0] = s_PointAndClickPos;
|
|
if(strlen(s_PointAndClickMaterial) > 0)
|
|
{
|
|
s32 materialIndex = -1;
|
|
for(s32 i=0; i<g_audCollisionMaterialNames.GetCount(); ++i)
|
|
{
|
|
if(strstr(g_audCollisionMaterialNames[i].name, s_PointAndClickMaterial))
|
|
{
|
|
materialIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if(naVerifyf(materialIndex >=0, "Couldn't find the selected material in our audio materials list - something is out of sync"))
|
|
{
|
|
collisionEvent.materialSettings[0] = GetMaterialOverride(s_PointAndClickEntity, g_audCollisionMaterials[materialIndex], s_PointAndClickComponentId);
|
|
collisionEvent.materialSettings[1] = NULL;
|
|
g_CollisionAudioEntity.ProcessImpactSounds(&collisionEvent, s_SimulateCollisionSoftImpact);
|
|
}
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::ClearDebugHistory()
|
|
{
|
|
for(u32 i = 0; i < g_NumDebugCollisionInfos; i++)
|
|
{
|
|
g_CollisionDebugInfo[i].hidden = true;
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::PopulateModelSettingsCreator(CBaseModelInfo * model, const char * name, u32 numComponents)
|
|
{
|
|
//Make sure we're starting from a clean slate
|
|
sysMemSet(&s_ModelSettingsCreator, 0, sizeof(s_ModelSettingsCreator));
|
|
strncpy(s_ModelSettingsCreator.audioSettingsName, name, 64);
|
|
s_ModelSettingsCreator.numComponents = numComponents;
|
|
s_ModelSettingsCreator.model = model->GetHashKey();
|
|
|
|
phArchetype * phtype = model->GetPhysics();
|
|
|
|
if(strlen(s_PointAndClickMaterial) > 0)
|
|
{
|
|
CollisionMaterialSettings *materialSettings = NULL;
|
|
s32 materialIndex = -1;
|
|
for(s32 i=0; i<g_audCollisionMaterialNames.GetCount(); ++i)
|
|
{
|
|
if(strstr(g_audCollisionMaterialNames[i].name, s_PointAndClickMaterial))
|
|
{
|
|
materialIndex = i;
|
|
}
|
|
}
|
|
if(naVerifyf(materialIndex >=0, "Couldn't find the selected material in our audio materials list - something is out of sync"))
|
|
{
|
|
materialSettings = GetMaterialOverride(s_PointAndClickEntity, g_audCollisionMaterials[materialIndex], s_PointAndClickComponentId);
|
|
if(materialSettings)
|
|
{
|
|
strncpy(s_PointAndClickMaterialOverride, audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(materialSettings->NameTableOffset), 64);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if(phtype) //Simple, non-frag models go through this route
|
|
{
|
|
phBound * bound = phtype->GetBound();
|
|
if(bound USE_GRIDS_ONLY(&& bound->GetType() != phBound::GRID) && bound->GetType() != phBound::COMPOSITE)
|
|
{
|
|
s32 numMats = bound->GetNumMaterials();
|
|
CollisionMaterialSettings * usedMaterials[64] = {NULL}; //an array to keep track of what materials have been set in the model
|
|
naDisplayf("Num materials is %i", numMats);
|
|
|
|
for(s32 i=0; i< numMats; i++)
|
|
{
|
|
naDisplayf("Material id is %" I64FMT "u", bound->GetMaterialId(i));
|
|
|
|
//Check to see this material hasn't already been overridden
|
|
s32 matid = static_cast<s32>(PGTAMATERIALMGR->UnpackMtlId(bound->GetMaterialId(i)));
|
|
s32 j = 0;
|
|
bool bailout = false;
|
|
while(usedMaterials[j] && j<numMats)
|
|
{
|
|
if(usedMaterials[j] == g_audCollisionMaterials[matid])
|
|
{
|
|
bailout = true;
|
|
break;
|
|
}
|
|
j++;
|
|
}
|
|
|
|
if(bailout)
|
|
{
|
|
//material has already been overridden...try the next one
|
|
continue;
|
|
}
|
|
|
|
if(naVerifyf(s_ModelSettingsCreator.numMaterials<ModelAudioCollisionSettings::MAX_MATERIALOVERRIDES, "Model %s has more materials than the max material overrideson ModelAudioCollisonSettings: get a coder to increase this", model->GetModelName()))
|
|
{
|
|
usedMaterials[s_ModelSettingsCreator.numMaterials] = g_audCollisionMaterials[matid];
|
|
strncpy(s_ModelSettingsCreator.materialNames[s_ModelSettingsCreator.numMaterials], g_audCollisionMaterialNames[matid].name, 64);
|
|
s_ModelSettingsCreator.numMaterials++;
|
|
}
|
|
}
|
|
}
|
|
else if(bound && (bound->GetType() == phBound::COMPOSITE))
|
|
{
|
|
phBoundComposite * composite = (phBoundComposite *)phtype->GetBound();
|
|
if(composite)
|
|
{
|
|
naDisplayf("Model has composite bounds");
|
|
CollisionMaterialSettings * usedMaterials[64] = {NULL}; //an array to keep track of what materials have been set in the model
|
|
s_ModelSettingsCreator.numComponents = composite->GetNumBounds();
|
|
for(s32 k=0; k<composite->GetNumBounds(); k++)
|
|
{
|
|
phBound * bound = composite->GetBound(k);
|
|
if(bound) //Since we got the composite bound from the model, not the entity, it should be the full unbroken bound...but we'll check anyhow
|
|
{
|
|
s32 numMats = bound->GetNumMaterials();
|
|
naDisplayf("Component/bound: %i", k);
|
|
|
|
for(s32 i=0; i< numMats; i++)
|
|
{
|
|
//Check to see this material hasn't already been overridden
|
|
s32 matid = static_cast<s32>(PGTAMATERIALMGR->UnpackMtlId(bound->GetMaterialId(i)));
|
|
bool bailout = false;
|
|
s32 j = 0;
|
|
while(usedMaterials[j] && j<64)
|
|
{
|
|
if(usedMaterials[j] == g_audCollisionMaterials[matid])
|
|
{
|
|
bailout = true;
|
|
break;
|
|
}
|
|
j++;
|
|
}
|
|
|
|
if(bailout)
|
|
{
|
|
//material has already been overridden...try the next one
|
|
continue;
|
|
}
|
|
|
|
if(naVerifyf(s_ModelSettingsCreator.numMaterials<ModelAudioCollisionSettings::MAX_MATERIALOVERRIDES, "Model %s has more materials than the max material overrideson ModelAudioCollisonSettings: get a coder to increase this", model->GetModelName()))
|
|
{
|
|
usedMaterials[s_ModelSettingsCreator.numMaterials] = g_audCollisionMaterials[matid];
|
|
strncpy(s_ModelSettingsCreator.materialNames[s_ModelSettingsCreator.numMaterials], g_audCollisionMaterialNames[matid].name, 64);
|
|
naDisplayf("Material name: %s", s_ModelSettingsCreator.materialNames[s_ModelSettingsCreator.numMaterials]);
|
|
s_ModelSettingsCreator.numMaterials++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Selected model for %s doesn't have a bound so we can't find it's materials", name);
|
|
sysMemSet(&s_ModelSettingsCreator, 0, sizeof(s_ModelSettingsCreator));
|
|
}
|
|
}
|
|
else if(model->GetFragType() && model->GetFragType()->GetPhysics(0)) //Not a simple model but could be a frag
|
|
{
|
|
phtype = (phArchetype *) model->GetFragType()->GetPhysics(0)->GetArchetype();
|
|
if(phtype)
|
|
{
|
|
naAssert(phtype->GetBound() && phtype->GetBound()->GetType()==phBound::COMPOSITE); //Just making sure we're actually looking at a composite bound
|
|
phBoundComposite * composite = (phBoundComposite *)phtype->GetBound();
|
|
if(composite)
|
|
{
|
|
naDisplayf("Model has composite bounds");
|
|
CollisionMaterialSettings * usedMaterials[64] = {NULL}; //an array to keep track of what materials have been set in the model
|
|
for(s32 k=0; k<composite->GetNumBounds(); k++)
|
|
{
|
|
phBound * bound = composite->GetBound(k);
|
|
if(bound) //Since we got the composite bound from the model, not the entity, it should be the full unbroken bound...but we'll check anyhow
|
|
{
|
|
s32 numMats = bound->GetNumMaterials();
|
|
naDisplayf("Component/bound: %i", k);
|
|
|
|
for(s32 i=0; i< numMats; i++)
|
|
{
|
|
//Check to see this material hasn't already been overridden
|
|
s32 matid = static_cast<s32>(PGTAMATERIALMGR->UnpackMtlId(bound->GetMaterialId(i)));
|
|
bool bailout = false;
|
|
s32 j = 0;
|
|
while(usedMaterials[j] && j<64)
|
|
{
|
|
if(usedMaterials[j] == g_audCollisionMaterials[matid])
|
|
{
|
|
bailout = true;
|
|
break;
|
|
}
|
|
j++;
|
|
}
|
|
|
|
if(bailout)
|
|
{
|
|
//material has already been overridden...try the next one
|
|
continue;
|
|
}
|
|
|
|
if(naVerifyf(s_ModelSettingsCreator.numMaterials<ModelAudioCollisionSettings::MAX_MATERIALOVERRIDES, "Model %s has more materials than the max material overrideson ModelAudioCollisonSettings: get a coder to increase this", model->GetModelName()))
|
|
{
|
|
usedMaterials[s_ModelSettingsCreator.numMaterials] = g_audCollisionMaterials[matid];
|
|
strncpy(s_ModelSettingsCreator.materialNames[s_ModelSettingsCreator.numMaterials], g_audCollisionMaterialNames[matid].name, 64);
|
|
naDisplayf("Material name: %s", s_ModelSettingsCreator.materialNames[s_ModelSettingsCreator.numMaterials]);
|
|
s_ModelSettingsCreator.numMaterials++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Selected model for %s doesn't have a bound so we can't find it's materials", name);
|
|
sysMemSet(&s_ModelSettingsCreator, 0, sizeof(s_ModelSettingsCreator));
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
naAssertf(phtype, "Selected model for %s doesn't have a phArchetype so we can't find it's materials", name);
|
|
sysMemSet(&s_ModelSettingsCreator, 0, sizeof(s_ModelSettingsCreator));
|
|
}
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::CreateModelAudioSettings()
|
|
{
|
|
|
|
if(strlen(s_ModelSettingsCreator.audioSettingsName) == 0)
|
|
{
|
|
return; //No model selected, nothing to create
|
|
}
|
|
|
|
naDisplayf("Model creator name is: %s", s_ModelSettingsCreator.audioSettingsName);
|
|
|
|
char xmlMsg[16384];
|
|
|
|
FormatMacsXml(xmlMsg, s_ModelSettingsCreator.audioSettingsName, s_ModelSettingsCreator.numComponents);
|
|
|
|
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");
|
|
|
|
//The intention here is to allow auditioning of newly assigned MACS without requiring a restart but for reasons unknown, instead it kills all sound...
|
|
if(success)
|
|
{
|
|
s_UpdateCreatedSettings = true;
|
|
//CModelInfo::GetBaseModelInfoFromHashKey(s_ModelSettingsCreator.model, NULL)->SetAudioCollisionSettings(audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(s_ModelSettingsCreator.audioSettingsName));
|
|
}
|
|
|
|
}
|
|
|
|
//Macs: Model Audio Collision Settings
|
|
//the array xmlMsg points to should be pretty big to take the xml message
|
|
void audCollisionAudioEntity::FormatMacsXml(char * xmlMsg, const char * settingsName, u32 numComponents, bool overrideMaterials, bool emptyComponents)
|
|
{
|
|
const audMetadataManager &metadataManager = SOUNDFACTORY.GetMetadataManager();
|
|
ModelAudioCollisionSettings * settings = audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(settingsName);
|
|
|
|
atString upperSettings(settingsName);
|
|
upperSettings.Uppercase();
|
|
|
|
atArray<atString> upperMaterials;
|
|
upperMaterials.Resize(s_ModelSettingsCreator.numMaterials);
|
|
|
|
for(int i=0; i<s_ModelSettingsCreator.numMaterials; i++)
|
|
{
|
|
upperMaterials[i] = s_ModelSettingsCreator.materialNames[i];
|
|
upperMaterials[i].Uppercase();
|
|
}
|
|
|
|
sprintf(xmlMsg, "<RAVEMessage>\n");
|
|
naDisplayf("%s", xmlMsg);
|
|
char tmpBuf[256] = {0};
|
|
sprintf(tmpBuf, " <EditObjects metadataType=\"GAMEOBJECTS\" chunkNameHash=\"%u\">\n", ATSTRINGHASH("BASE", 0x44E21C90));
|
|
naDisplayf("%s", tmpBuf);
|
|
strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <ModelAudioCollisionSettings name=\"%s\">\n", &upperSettings[0]);
|
|
naDisplayf("%s", tmpBuf);
|
|
strcat(xmlMsg, tmpBuf);
|
|
|
|
//If we already have an instance of this Macs, we preserve the non-material information
|
|
if (settings && g_PreserveMACSSettingsOnExport)
|
|
{
|
|
char breakSound[64] = "NULL_SOUND";
|
|
char destroySound[64] = "NULL_SOUND";
|
|
char uprootSound[64] = "NULL_SOUND";
|
|
char swingSound[64] = "NULL_SOUND";
|
|
char rainLoop[64] = "NULL_SOUND";
|
|
char shockwaveSound[64] = "NULL_SOUND";
|
|
char randomAmbient[64] = "NULL_SOUND";
|
|
char entityResonance[64] = "NULL_SOUND";
|
|
|
|
safecpy(breakSound, metadataManager.GetObjectName(settings->BreakSound));
|
|
safecpy(destroySound, metadataManager.GetObjectName(settings->DestroySound));
|
|
safecpy(uprootSound, metadataManager.GetObjectName(settings->UprootSound));
|
|
safecpy(swingSound, metadataManager.GetObjectName(settings->SwingSound));
|
|
safecpy(rainLoop, metadataManager.GetObjectName(settings->RainLoop));
|
|
safecpy(shockwaveSound, metadataManager.GetObjectName(settings->ShockwaveSound));
|
|
safecpy(randomAmbient, metadataManager.GetObjectName(settings->RandomAmbient));
|
|
safecpy(entityResonance, metadataManager.GetObjectName(settings->EntityResonance));
|
|
|
|
|
|
if (settings->MediumIntensity != 0u)
|
|
{
|
|
sprintf(tmpBuf, " <MediumIntensity>%s</MediumIntensity>\n", metadataManager.GetObjectName(settings->MediumIntensity)); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
}
|
|
|
|
if (settings->HighIntensity != 0u)
|
|
{
|
|
sprintf(tmpBuf, " <HighIntensity>%s</HighIntensity>\n", metadataManager.GetObjectName(settings->HighIntensity)); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
}
|
|
|
|
sprintf(tmpBuf, " <BreakSound>%s</BreakSound>\n", breakSound); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <DestroySound>%s</DestroySound>\n", destroySound); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <UprootSound>%s</UprootSound>\n", uprootSound); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
|
|
if (settings->WindSounds != 0u)
|
|
{
|
|
sprintf(tmpBuf, " <WindSounds>%s</WindSounds>\n", metadataManager.GetObjectName(settings->WindSounds)); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
}
|
|
|
|
sprintf(tmpBuf, " <SwingSound>%s</SwingSound>\n", swingSound); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <MinSwingVel>%.02f</MinSwingVel>\n", settings->MinSwingVel); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <MaxswingVel>%.02f</MaxswingVel>\n", settings->MaxswingVel); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <RainLoop>%s</RainLoop>\n", rainLoop); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <ShockwaveSound>%s</ShockwaveSound>\n", shockwaveSound); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <RandomAmbient>%s</RandomAmbient>\n", randomAmbient); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <EntityResonance>%s</EntityResonance>\n", entityResonance); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <Weight>%s</Weight>\n", AudioWeight_ToString((AudioWeight)settings->Weight)); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <IsResonant>%s</IsResonant>\n", AUD_GET_TRISTATE_VALUE(settings->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_ISRESONANT) == AUD_TRISTATE_TRUE ? "yes" : "no"); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <TurnOffRainVolOnProps>%s</TurnOffRainVolOnProps>\n", AUD_GET_TRISTATE_VALUE(settings->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_TURNOFFRAINVOLONPROPS) == AUD_TRISTATE_TRUE ? "yes" : "no"); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <SwingingProp>%s</SwingingProp>\n", AUD_GET_TRISTATE_VALUE(settings->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_SWINGINGPROP) == AUD_TRISTATE_TRUE ? "yes" : "no"); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <RainLoopTracksListener>%s</RainLoopTracksListener>\n", AUD_GET_TRISTATE_VALUE(settings->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_RAINLOOPTRACKSLISTENER) == AUD_TRISTATE_TRUE ? "yes" : "no"); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <PlaceRainLoopOnTopOfBounds>%s</PlaceRainLoopOnTopOfBounds>\n", AUD_GET_TRISTATE_VALUE(settings->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_PLACERAINLOOPONTOPOFBOUNDS) == AUD_TRISTATE_TRUE ? "yes" : "no"); naDisplayf("%s", tmpBuf); strcat(xmlMsg, tmpBuf);
|
|
}
|
|
|
|
if(overrideMaterials)
|
|
{
|
|
for(u32 i=0; i<s_ModelSettingsCreator.numMaterials; i++)
|
|
{
|
|
sprintf(tmpBuf, " <MaterialOverride>\n");
|
|
naDisplayf("%s", tmpBuf);
|
|
strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <Material>%s</Material>\n", &upperMaterials[i][0]);
|
|
naDisplayf("%s", tmpBuf);
|
|
strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <Override>%s</Override>\n", &upperMaterials[i][0]);
|
|
naDisplayf("%s", tmpBuf);
|
|
strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " </MaterialOverride>\n");
|
|
naDisplayf("%s", tmpBuf);
|
|
strcat(xmlMsg, tmpBuf);
|
|
}
|
|
}
|
|
if(emptyComponents)
|
|
{
|
|
for(u32 i=0; i < numComponents; i++)
|
|
{
|
|
sprintf(tmpBuf, " <FragComponentSetting>\n");
|
|
naDisplayf("%s", tmpBuf);
|
|
strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " </FragComponentSetting>\n");
|
|
naDisplayf("%s", tmpBuf);
|
|
strcat(xmlMsg, tmpBuf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(u32 i=0; i < numComponents; i++)
|
|
{
|
|
sprintf(tmpBuf, " <FragComponentSetting>\n");
|
|
naDisplayf("%s", tmpBuf);
|
|
strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " <ModelSettings>%s_%i</ModelSettings>\n", &upperSettings[0], i);
|
|
naDisplayf("%s", tmpBuf);
|
|
strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, " </FragComponentSetting>\n");
|
|
naDisplayf("%s", tmpBuf);
|
|
strcat(xmlMsg, tmpBuf);
|
|
}
|
|
}
|
|
sprintf(tmpBuf, " </ModelAudioCollisionSettings>\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 %d characters long \n", istrlen(xmlMsg));
|
|
|
|
|
|
}
|
|
|
|
void audCollisionAudioEntity::CreateIndividualFragModelAudioSettings()
|
|
{
|
|
if((strlen(s_ModelSettingsCreator.audioSettingsName) == 0) || !s_ModelSettingsCreator.numComponents) //only fraggable models should be set up here
|
|
{
|
|
naDisplayf("Non frag model in CreateFragModelAudioSettings: bailing out");
|
|
naDisplayf("Num components: %i", s_ModelSettingsCreator.numComponents);
|
|
return;
|
|
}
|
|
|
|
naDisplayf("Model creator name is: %s", s_ModelSettingsCreator.audioSettingsName);
|
|
|
|
|
|
char xmlMsg[4096];
|
|
char componentName[128];
|
|
char suffix[8];
|
|
|
|
|
|
sprintf(componentName, "%s", s_ModelSettingsCreator.audioSettingsName);
|
|
sprintf(suffix, "_%i", s_PointAndClickComponentId);
|
|
strcat(componentName, suffix);
|
|
naDisplayf("Model component name is: %s", componentName);
|
|
FormatMacsXml(xmlMsg, componentName, 0);
|
|
|
|
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 audCollisionAudioEntity::CreateFragModelAudioSettings()
|
|
{
|
|
if((strlen(s_ModelSettingsCreator.audioSettingsName) == 0) || s_ModelSettingsCreator.numComponents <= 1) //only fraggable models should be set up here
|
|
{
|
|
naDisplayf("Non frag model in CreateFragModelAudioSettings: bailing out");
|
|
naDisplayf("Num components: %i", s_ModelSettingsCreator.numComponents);
|
|
return;
|
|
}
|
|
|
|
naDisplayf("Model creator name is: %s", s_ModelSettingsCreator.audioSettingsName);
|
|
|
|
u32 numComponents = s_ModelSettingsCreator.numComponents;
|
|
|
|
char xmlMsg[4096];
|
|
char componentName[128];
|
|
char suffix[8];
|
|
|
|
|
|
//Format the frag component settings
|
|
for(u32 i = 0; i < numComponents; i++)
|
|
{
|
|
sprintf(componentName, "%s", s_ModelSettingsCreator.audioSettingsName);
|
|
sprintf(suffix, "_%i", i);
|
|
strcat(componentName, suffix);
|
|
naDisplayf("Model component name is: %s", componentName);
|
|
FormatMacsXml(xmlMsg, componentName, 0, false);
|
|
|
|
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");
|
|
|
|
|
|
//Okay this is a bit of a hack...RAVE doesn't like having messages sent too frequently and
|
|
//can lose some randomly while still claiming success if they are, so we'll spend a while
|
|
//sitting round twiddling our thumbs after each one. If Macs seem to be randomly missing components
|
|
//for frag models it could be worth checking that it's waiting long enough between messages.
|
|
|
|
u32 time = 100000;
|
|
naDisplayf("Waiting for RAVE... Component %i", i);
|
|
while(time)
|
|
{
|
|
time--;
|
|
//Do some pointless stuff to take up time
|
|
u32 fudge = time*time;
|
|
(void)fudge;
|
|
sprintf(xmlMsg, "Waiting for rave %i", fudge);
|
|
}
|
|
naDisplayf("Done waiting :)");
|
|
}
|
|
|
|
//Format the base settings object
|
|
FormatMacsXml(xmlMsg, s_ModelSettingsCreator.audioSettingsName, numComponents, true, false);
|
|
|
|
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");
|
|
|
|
if(success)
|
|
{
|
|
CModelInfo::GetBaseModelInfoFromHashKey(s_ModelSettingsCreator.model, NULL)->SetAudioCollisionSettings(audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(s_ModelSettingsCreator.audioSettingsName), atStringHash(s_ModelSettingsCreator.audioSettingsName));
|
|
}
|
|
|
|
}
|
|
|
|
void audCollisionAudioEntity::SynchronizeMacsAndModels()
|
|
{
|
|
const u32 maxAudioSettingsNameLength = 128;
|
|
const char *audioSettingsNamePrefix = "MOD_";
|
|
char audioSettingsName[maxAudioSettingsNameLength];
|
|
|
|
fiStream *stream = fiStream::Create("MACS_SynchronizeList.txt");
|
|
|
|
if(!stream)
|
|
{
|
|
naErrorf("Couldn't open x:gta5/tools/bin/MACS_SynchronizeList.txt");
|
|
return;
|
|
}
|
|
|
|
naDisplayf("Writing sync list to: x:gta5/tools/bin/MACS_SynchronizeList.txt");
|
|
|
|
|
|
//Iterate through all loaded models and fix any that have gotten out of synch
|
|
const u32 maxModelInfos = CModelInfo::GetMaxModelInfos();
|
|
for(u32 i=0; i< maxModelInfos; i++)
|
|
{
|
|
CBaseModelInfo * pModel = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(i)));
|
|
if(!pModel)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fwModelId modelId((strLocalIndex(i)));
|
|
|
|
//Try get a Macs for the current model
|
|
strcpy(audioSettingsName, audioSettingsNamePrefix);
|
|
strcat(audioSettingsName, CModelInfo::GetBaseModelInfoName(modelId));
|
|
ModelAudioCollisionSettings * moda = audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(audioSettingsName);
|
|
|
|
if(moda && moda != g_MacsDefault && pModel->GetPhysics() && AreMacsMaterialsOutOfDate(moda))
|
|
{
|
|
const fragType* pFragType = pModel->GetFragType();
|
|
u32 numChildren = 0;
|
|
if(pFragType)
|
|
{
|
|
numChildren = pFragType->GetPhysics(0)->GetNumChildren();
|
|
}
|
|
|
|
PopulateModelSettingsCreator(pModel, audioSettingsName, numChildren);
|
|
|
|
if(numChildren)
|
|
{
|
|
Displayf("[North Audio] ======= MACS Frag materials out of date: %s", audioSettingsName);
|
|
fprintf(stream, "[North Audio] ======= MACS Frag materials out of date: %s\n", CModelInfo::GetBaseModelInfoName(modelId));
|
|
//CreateFragModelAudioSettings();
|
|
}
|
|
else
|
|
{
|
|
Displayf("[North Audio] ======= MACS materials out of date: %s", audioSettingsName);
|
|
fprintf(stream, "[North Audio] ======= MACS materials out of date: %s\n", CModelInfo::GetBaseModelInfoName(modelId));
|
|
//CreateModelAudioSettings();
|
|
}
|
|
}
|
|
}
|
|
|
|
stream->Close();
|
|
}
|
|
|
|
|
|
static s32 newModelSpawnIndex = 0;
|
|
static s32 syncModelSpawnIndex = 0;
|
|
static s32 batchModelCreateStartIndex = 0;
|
|
static s32 batchModelCreateStopIndex = 0;
|
|
|
|
void audCollisionAudioEntity::BatchCreateMacs()
|
|
{
|
|
const u32 maxAudioSettingsNameLength = 128;
|
|
const char *audioSettingsNamePrefix = "MOD_";
|
|
char audioSettingsName[maxAudioSettingsNameLength];
|
|
|
|
u32 currentIndex = 0;
|
|
//Iterate through all loaded models and fix any that have gotten out of synch
|
|
const s32 maxModelInfos = CModelInfo::GetMaxModelInfos();
|
|
for(u32 i=0; i< maxModelInfos; i++)
|
|
{
|
|
CBaseModelInfo * pModel = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(i)));
|
|
if(!pModel)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fwModelId modelId((strLocalIndex(i)));
|
|
|
|
//Try get a Macs for the current model
|
|
strcpy(audioSettingsName, audioSettingsNamePrefix);
|
|
strcat(audioSettingsName, CModelInfo::GetBaseModelInfoName(modelId));
|
|
|
|
//Check if the model is using MOD_DEFAULT, has physics and is not a weapon or a ped or a vehicle
|
|
if(pModel->GetAudioCollisionSettings() == g_MacsDefault && pModel->GetPhysics() && pModel->GetModelType() != MI_TYPE_WEAPON && pModel->GetModelType() != MI_TYPE_PED && pModel->GetModelType() != MI_TYPE_VEHICLE)
|
|
{
|
|
if(batchModelCreateStartIndex <= currentIndex )
|
|
{
|
|
const fragType* pFragType = pModel->GetFragType();
|
|
u32 numChildren = 0;
|
|
if(pFragType)
|
|
{
|
|
numChildren = pFragType->GetPhysics(0)->GetNumChildren();
|
|
}
|
|
PopulateModelSettingsCreator(pModel, audioSettingsName, numChildren);
|
|
CreateModelAudioSettings();
|
|
}
|
|
currentIndex++;
|
|
if(batchModelCreateStopIndex < currentIndex)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FindNewModels();
|
|
|
|
}
|
|
|
|
void audCollisionAudioEntity::AbortWorldCollisionListGeneration()
|
|
{
|
|
sm_ProcessSectors = false;
|
|
//naEnvironment::sm_DrawAudioWorldSectors = false;
|
|
sm_IsWaitingToInit = true;
|
|
CWarpManager::WaitUntilTheSceneIsLoaded(false);
|
|
CWarpManager::FadeCamera(true);
|
|
fiStream *rescueFile = fiStream::Open("C:/AudioCollisionListRescue",false);
|
|
if(rescueFile)
|
|
{
|
|
sm_CurrentSectorX = 0;
|
|
sm_CurrentSectorY = 0;
|
|
rescueFile->WriteInt(&sm_CurrentSectorX,1);
|
|
rescueFile->WriteInt(&sm_CurrentSectorY,1);
|
|
rescueFile->Close();
|
|
}
|
|
if(sm_NewMoveableFile)
|
|
{
|
|
sm_NewMoveableFile->Close();
|
|
sm_NewMoveableFile = NULL;
|
|
}
|
|
if(sm_NewStaticFile)
|
|
{
|
|
sm_NewStaticFile->Close();
|
|
sm_NewStaticFile = NULL;
|
|
}
|
|
if(sm_DefaultMatFile)
|
|
{
|
|
sm_DefaultMatFile->Close();
|
|
sm_DefaultMatFile = NULL;
|
|
}
|
|
if(sm_ChangedMacsFile)
|
|
{
|
|
sm_ChangedMacsFile->Close();
|
|
sm_ChangedMacsFile = NULL;
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::GenerateWorldCollisionLists()
|
|
{
|
|
if(sm_ProcessSectors && sm_IsWaitingToInit)
|
|
{
|
|
InitWorldCollisionListGenerator();
|
|
}
|
|
else if(sm_ProcessSectors)
|
|
{
|
|
if(CWarpManager::IsActive())
|
|
{
|
|
// Waiting for the scene to be loaded.
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
WriteCollisionListData();
|
|
sm_CurrentSectorX ++;
|
|
if(sm_CurrentSectorX == g_audNumWorldSectorsX*3 )
|
|
{
|
|
sm_CurrentSectorX = 0;
|
|
sm_CurrentSectorY ++;
|
|
}
|
|
if(sm_CurrentSectorY == g_audNumWorldSectorsY*3 )
|
|
{
|
|
naDisplayf("All sectors have been processed, terminating...");
|
|
fiStream *rescueFile = fiStream::Open("C:/AudioCollisionListRescue");
|
|
if(rescueFile)
|
|
{
|
|
sm_CurrentSectorX = 0;
|
|
sm_CurrentSectorY = 0;
|
|
rescueFile->WriteInt(&sm_CurrentSectorX,1);
|
|
rescueFile->WriteInt(&sm_CurrentSectorY,1);
|
|
rescueFile->Close();
|
|
}
|
|
sm_ProcessSectors = false;
|
|
CWarpManager::WaitUntilTheSceneIsLoaded(false);
|
|
CWarpManager::FadeCamera(true);
|
|
sm_IsWaitingToInit = true;
|
|
if(sm_NewMoveableFile)
|
|
{
|
|
sm_NewMoveableFile->Close();
|
|
sm_NewMoveableFile = NULL;
|
|
}
|
|
if(sm_NewStaticFile)
|
|
{
|
|
sm_NewStaticFile->Close();
|
|
sm_NewStaticFile = NULL;
|
|
}
|
|
if(sm_DefaultMatFile)
|
|
{
|
|
sm_DefaultMatFile->Close();
|
|
sm_DefaultMatFile = NULL;
|
|
}
|
|
if(sm_ChangedMacsFile)
|
|
{
|
|
sm_ChangedMacsFile->Close();
|
|
sm_ChangedMacsFile = NULL;
|
|
}
|
|
naDisplayf("Done");
|
|
return;
|
|
}
|
|
}
|
|
Vector3 centreFirstSector;
|
|
centreFirstSector.SetX(g_audWorldSectorsMinX + 0.5f * g_audWidthOfSector/3);
|
|
centreFirstSector.SetY(g_audWorldSectorsMinY + 0.5f * g_audDepthOfSector/3);
|
|
naDisplayf("processing sector %d", (sm_CurrentSectorY * g_audNumWorldSectorsX*3) + sm_CurrentSectorX);
|
|
Vector3 sectorCentre = centreFirstSector;
|
|
sectorCentre.SetX(sectorCentre.GetX() + sm_CurrentSectorX*g_audWidthOfSector/3);
|
|
sectorCentre.SetY(sectorCentre.GetY() + sm_CurrentSectorY*g_audDepthOfSector/3);
|
|
naDisplayf("Wrapping player to the center sector");
|
|
naDisplayf("Loading scene....");
|
|
CWarpManager::WaitUntilTheSceneIsLoaded(true);
|
|
CWarpManager::FadeCamera(false);
|
|
CWarpManager::SetWarp(sectorCentre, sectorCentre, 0.f, true, true, 600.f);
|
|
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::InitWorldCollisionListGenerator()
|
|
{
|
|
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
|
|
{
|
|
AbortWorldCollisionListGeneration();
|
|
}
|
|
naDisplayf("Checking if we have to resume the process or start a new one.");
|
|
sm_CurrentSectorX = 0;
|
|
sm_CurrentSectorY = 0;
|
|
|
|
bool abort = false;
|
|
|
|
if(sm_NewMoveableFile)
|
|
{
|
|
sm_NewMoveableFile->Close();
|
|
sm_NewMoveableFile = NULL;
|
|
}
|
|
if(sm_NewStaticFile)
|
|
{
|
|
sm_NewStaticFile->Close();
|
|
sm_NewStaticFile = NULL;
|
|
}
|
|
if(sm_DefaultMatFile)
|
|
{
|
|
sm_DefaultMatFile->Close();
|
|
sm_DefaultMatFile = NULL;
|
|
}
|
|
if(sm_ChangedMacsFile)
|
|
{
|
|
sm_ChangedMacsFile->Close();
|
|
sm_ChangedMacsFile = NULL;
|
|
}
|
|
|
|
sm_NewMoveableFile = fiStream::Open("C:/AudioCollisionNewMoveable.txt",false);
|
|
if(!sm_NewMoveableFile)
|
|
{
|
|
sm_NewMoveableFile = fiStream::Create("C:/AudioCollisionNewMoveable.txt");
|
|
|
|
if(!naVerifyf(sm_NewMoveableFile,"Fail opening/creating the movable file, aborting the process."))
|
|
{
|
|
AbortWorldCollisionListGeneration();
|
|
abort = true;
|
|
}
|
|
}
|
|
sm_NewStaticFile = fiStream::Open("C:/AudioCollisionNewStatic.txt",false);
|
|
if(!sm_NewStaticFile)
|
|
{
|
|
sm_NewStaticFile = fiStream::Create("C:/AudioCollisionNewStatic.txt");
|
|
|
|
if(!naVerifyf(sm_NewStaticFile,"Fail opening/creating the static file, aborting the process."))
|
|
{
|
|
AbortWorldCollisionListGeneration();
|
|
abort = true;
|
|
}
|
|
}
|
|
sm_DefaultMatFile = fiStream::Open("C:/AudioCollisionDefault.txt",false);
|
|
if(!sm_DefaultMatFile)
|
|
{
|
|
sm_DefaultMatFile = fiStream::Create("C:/AudioCollisionDefault.txt");
|
|
|
|
if(!naVerifyf(sm_DefaultMatFile,"Fail opening/creating the default file, aborting the process."))
|
|
{
|
|
AbortWorldCollisionListGeneration();
|
|
abort = true;
|
|
}
|
|
}
|
|
sm_ChangedMacsFile = fiStream::Open("C:/AudioCollisionChanged.txt",false);
|
|
if(!sm_ChangedMacsFile)
|
|
{
|
|
sm_ChangedMacsFile = fiStream::Create("C:/AudioCollisionChanged.txt");
|
|
|
|
if(!naVerifyf(sm_ChangedMacsFile,"Fail opening/creating the default file, aborting the process."))
|
|
{
|
|
AbortWorldCollisionListGeneration();
|
|
abort = true;
|
|
}
|
|
}
|
|
|
|
|
|
if(!abort)
|
|
{
|
|
//Check if we are resuming a process.
|
|
fiStream *rescueFile = fiStream::Open("C:/AudioCollisionListRescue");
|
|
if(rescueFile)
|
|
{
|
|
rescueFile->ReadInt(&sm_CurrentSectorX,1);
|
|
rescueFile->ReadInt(&sm_CurrentSectorY,1);
|
|
rescueFile->Close();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(sm_NewMoveableFile)
|
|
{
|
|
sm_NewMoveableFile->Close();
|
|
}
|
|
if(sm_NewStaticFile)
|
|
{
|
|
sm_NewStaticFile->Close();
|
|
}
|
|
if(sm_DefaultMatFile)
|
|
{
|
|
sm_DefaultMatFile->Close();
|
|
}
|
|
if(sm_ChangedMacsFile)
|
|
{
|
|
sm_ChangedMacsFile->Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::WriteCollisionListData()
|
|
{
|
|
// 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:/AudioCollisionListRescue",false);
|
|
if(!rescueFile)
|
|
{
|
|
rescueFile = fiStream::Create("C:/AudioCollisionListRescue");
|
|
}
|
|
if(naVerifyf(rescueFile,"Fail opening/creating the file, aborting the process."))
|
|
{
|
|
rescueFile->WriteInt(&sm_CurrentSectorX,1);
|
|
rescueFile->WriteInt(&sm_CurrentSectorY,1);
|
|
rescueFile->Close();
|
|
}
|
|
else
|
|
{
|
|
AbortWorldCollisionListGeneration();
|
|
}
|
|
|
|
naDisplayf("Scene loaded : Writing data files");
|
|
if(naVerifyf(sm_NewMoveableFile,"Fail opening/creating the file, aborting the process."))
|
|
{
|
|
FindNewModels(sm_NewMoveableFile, true);
|
|
}
|
|
else
|
|
{
|
|
AbortWorldCollisionListGeneration();
|
|
}
|
|
if(naVerifyf(sm_NewStaticFile,"Fail opening/creating the file, aborting the process."))
|
|
{
|
|
FindNewModels(sm_NewStaticFile, false);
|
|
}
|
|
else
|
|
{
|
|
AbortWorldCollisionListGeneration();
|
|
}
|
|
if(naVerifyf(sm_DefaultMatFile,"Fail opening/creating the file, aborting the process."))
|
|
{
|
|
FindDefaultMaterialModels(sm_DefaultMatFile);
|
|
}
|
|
else
|
|
{
|
|
AbortWorldCollisionListGeneration();
|
|
}
|
|
if(naVerifyf(sm_ChangedMacsFile,"Fail opening/creating the file, aborting the process."))
|
|
{
|
|
FindChangedModels(sm_ChangedMacsFile);
|
|
}
|
|
else
|
|
{
|
|
AbortWorldCollisionListGeneration();
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::FindNewModels(fiStream *stream, bool findMovable)
|
|
{
|
|
const u32 maxAudioSettingsNameLength = 128;
|
|
const char *audioSettingsNamePrefix = "MOD_";
|
|
char audioSettingsName[maxAudioSettingsNameLength];
|
|
|
|
s32 spawnCount = 0;
|
|
|
|
//Iterate through all loaded models and fix any that have gotten out of synch
|
|
const u32 maxModelInfos = CModelInfo::GetMaxModelInfos();
|
|
for(u32 i=0; i< maxModelInfos; i++)
|
|
{
|
|
CBaseModelInfo * pModel = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(i)));
|
|
if(!pModel || !pModel->GetIsTypeObject() || !(pModel->GetPhysics() ||(pModel->GetFragType() && pModel->GetFragType()->GetPhysics(0))))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if((findMovable && pModel->GetIsFixed()) || (!findMovable && !pModel->GetIsFixed()) || pModel->GetAudioCollisionSettings() != g_MacsDefault)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fwModelId modelId((strLocalIndex(i)));
|
|
|
|
//Try get a Macs for the current model
|
|
strcpy(audioSettingsName, audioSettingsNamePrefix);
|
|
strcat(audioSettingsName, CModelInfo::GetBaseModelInfoName(modelId));
|
|
|
|
fprintf(stream, "%s\r\n", CModelInfo::GetBaseModelInfoName(modelId));
|
|
|
|
++spawnCount;
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::FindLocalDefaultMaterialModels()
|
|
{
|
|
fiStream * defaultMatFile = fiStream::Open("C:/AudioCollisionLocalDefault.txt",false);
|
|
if(!defaultMatFile)
|
|
{
|
|
defaultMatFile = fiStream::Create("C:/AudioCollisionLocalDefault.txt");
|
|
|
|
if(!naVerifyf(defaultMatFile,"Fail opening/creating the default file, aborting the process."))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
FindDefaultMaterialModels(defaultMatFile);
|
|
|
|
defaultMatFile->Close();
|
|
}
|
|
|
|
void audCollisionAudioEntity::FindDefaultMaterialModels(fiStream *stream)
|
|
{
|
|
const u32 maxAudioSettingsNameLength = 128;
|
|
const char *audioSettingsNamePrefix = "MOD_";
|
|
char audioSettingsName[maxAudioSettingsNameLength];
|
|
|
|
s32 spawnCount = 0;
|
|
|
|
//Iterate through all loaded models and fix any that have gotten out of synch
|
|
const u32 maxModelInfos = CModelInfo::GetMaxModelInfos();
|
|
for(u32 i=0; i< maxModelInfos; i++)
|
|
{
|
|
CBaseModelInfo * pModel = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(i)));
|
|
if(!pModel || !pModel->GetIsTypeObject() || !(pModel->GetPhysics() ||(pModel->GetFragType() && pModel->GetFragType()->GetPhysics(0))))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const fragType* pFragType = pModel->GetFragType();
|
|
u32 numChildren = 0;
|
|
if(pFragType)
|
|
{
|
|
numChildren = pFragType->GetPhysics(0)->GetNumChildren();
|
|
}
|
|
|
|
PopulateModelSettingsCreator(pModel, audioSettingsName, numChildren);
|
|
|
|
bool foundDefaultMaterial = false;
|
|
for(int j=0; j<s_ModelSettingsCreator.numMaterials; j++)
|
|
{
|
|
if(strstr(s_ModelSettingsCreator.materialNames[j], "default"))
|
|
{
|
|
foundDefaultMaterial = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!foundDefaultMaterial)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fwModelId modelId((strLocalIndex(i)));
|
|
|
|
//Try get a Macs for the current model
|
|
strcpy(audioSettingsName, audioSettingsNamePrefix);
|
|
strcat(audioSettingsName, CModelInfo::GetBaseModelInfoName(modelId));
|
|
|
|
fprintf(stream, "%s\r\n", CModelInfo::GetBaseModelInfoName(modelId));
|
|
|
|
++spawnCount;
|
|
}
|
|
}
|
|
|
|
|
|
void audCollisionAudioEntity::FindChangedModels(fiStream *stream)
|
|
{
|
|
const u32 maxAudioSettingsNameLength = 128;
|
|
const char *audioSettingsNamePrefix = "MOD_";
|
|
char audioSettingsName[maxAudioSettingsNameLength];
|
|
|
|
s32 spawnCount = 0;
|
|
|
|
//Iterate through all loaded models and fix any that have gotten out of synch
|
|
const u32 maxModelInfos = CModelInfo::GetMaxModelInfos();
|
|
for(u32 i=0; i< maxModelInfos; i++)
|
|
{
|
|
CBaseModelInfo * pModel = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(i)));
|
|
if(!pModel || !pModel->GetIsTypeObject() || !(pModel->GetPhysics() ||(pModel->GetFragType() && pModel->GetFragType()->GetPhysics(0))))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const fragType* pFragType = pModel->GetFragType();
|
|
u32 numChildren = 0;
|
|
if(pFragType)
|
|
{
|
|
numChildren = pFragType->GetPhysics(0)->GetNumChildren();
|
|
}
|
|
|
|
PopulateModelSettingsCreator(pModel, audioSettingsName, numChildren);
|
|
|
|
if(pModel->GetAudioCollisionSettings() == g_MacsDefault || !AreMacsMaterialsOutOfDate(pModel->GetAudioCollisionSettings()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fwModelId modelId((strLocalIndex(i)));
|
|
|
|
//Try get a Macs for the current model
|
|
strcpy(audioSettingsName, audioSettingsNamePrefix);
|
|
strcat(audioSettingsName, CModelInfo::GetBaseModelInfoName(modelId));
|
|
|
|
fprintf(stream, "%s\r\n", CModelInfo::GetBaseModelInfoName(modelId));
|
|
|
|
++spawnCount;
|
|
}
|
|
}
|
|
|
|
//Looks for models that are still using MOD_DEFAULT and provides a way to spawn them so that they can be properly set up
|
|
void audCollisionAudioEntity::FindNewModels()
|
|
{
|
|
const u32 maxAudioSettingsNameLength = 128;
|
|
const char *audioSettingsNamePrefix = "MOD_";
|
|
char audioSettingsName[maxAudioSettingsNameLength];
|
|
|
|
s32 spawnCount = 0;
|
|
|
|
fiStream *stream = fiStream::Create("MACS_NewModelsList.txt");
|
|
|
|
if(!stream)
|
|
{
|
|
naErrorf("Couldn't open Desktop/MACS_NewModelsList.txt");
|
|
return;
|
|
}
|
|
|
|
naDisplayf("Writing sync list to: Desktop/MACS_NewModelsList.txt");
|
|
|
|
|
|
//Iterate through all loaded models and fix any that have gotten out of synch
|
|
const u32 maxModelInfos = CModelInfo::GetMaxModelInfos();
|
|
for(u32 i=0; i< maxModelInfos; i++)
|
|
{
|
|
CBaseModelInfo * pModel = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(i)));
|
|
if(!pModel)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fwModelId modelId((strLocalIndex(i)));
|
|
|
|
//Try get a Macs for the current model
|
|
strcpy(audioSettingsName, audioSettingsNamePrefix);
|
|
strcat(audioSettingsName, CModelInfo::GetBaseModelInfoName(modelId));
|
|
|
|
//Check if the model is using MOD_DEFAULT, has physics and is not a weapon or a ped or a vehicle
|
|
if(pModel->GetAudioCollisionSettings() == g_MacsDefault && (pModel->GetFragType() || pModel->GetFragType()->GetPhysics(0)) && pModel->GetIsTypeObject())
|
|
{
|
|
const fragType* pFragType = pModel->GetFragType();
|
|
u32 numChildren = 0;
|
|
if(pFragType)
|
|
{
|
|
numChildren = pFragType->GetPhysics(0)->GetNumChildren();
|
|
}
|
|
|
|
if(numChildren)
|
|
{
|
|
fprintf(stream, "%i: MACS Frag materials absent: %s\r\n", spawnCount, CModelInfo::GetBaseModelInfoName(modelId));
|
|
}
|
|
else
|
|
{
|
|
fprintf(stream, "%i: MACS materials absent: %s\r\n", spawnCount, CModelInfo::GetBaseModelInfoName(modelId));
|
|
}
|
|
|
|
++spawnCount;
|
|
}
|
|
}
|
|
|
|
stream->Close();
|
|
}
|
|
|
|
void audCollisionAudioEntity::SpawnSyncModelObject()
|
|
{
|
|
const u32 maxAudioSettingsNameLength = 128;
|
|
const char *audioSettingsNamePrefix = "MOD_";
|
|
char audioSettingsName[maxAudioSettingsNameLength];
|
|
|
|
s32 spawnCount = 0;
|
|
bool spawnModel = true;
|
|
|
|
fiStream *stream = fiStream::Create("MACS_SynchronizeList.txt");
|
|
|
|
if(!stream)
|
|
{
|
|
naErrorf("Couldn't open MACS_SynchronizeList.txt");
|
|
return;
|
|
}
|
|
|
|
//Iterate through all loaded models and fix any that have gotten out of synch
|
|
const u32 maxModelInfos = CModelInfo::GetMaxModelInfos();
|
|
for(u32 i=0; i< maxModelInfos; i++)
|
|
{
|
|
CBaseModelInfo * pModel = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(i)));
|
|
if(!pModel)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fwModelId modelId((strLocalIndex(i)));
|
|
|
|
//Try get a Macs for the current model
|
|
strcpy(audioSettingsName, audioSettingsNamePrefix);
|
|
strcat(audioSettingsName, CModelInfo::GetBaseModelInfoName(modelId));
|
|
|
|
//Check if the model has collision settings, has physics and is not a weapon or a ped or a vehicle
|
|
if(pModel->GetAudioCollisionSettings() && AreMacsMaterialsOutOfDate(pModel->GetAudioCollisionSettings()) && pModel->GetPhysics() && pModel->GetModelType() != MI_TYPE_WEAPON && pModel->GetModelType() != MI_TYPE_PED && pModel->GetModelType() != MI_TYPE_VEHICLE)
|
|
{
|
|
// Displayf("Model type is %d", pModel->GetModelType());
|
|
|
|
if(spawnCount == syncModelSpawnIndex && spawnModel)
|
|
{
|
|
//Spawn in an instance of the object so we can set up the MACS using the model editor
|
|
spawnModel = false;
|
|
RegdEnt displayObject(NULL);
|
|
Matrix34 testMat;
|
|
testMat.Identity();
|
|
|
|
Vector3 vecCreateOffset = camInterface::GetFront();
|
|
vecCreateOffset.z = 0.0f;
|
|
vecCreateOffset *= 2.f + pModel->GetBoundingSphereRadius();
|
|
|
|
camDebugDirector & debugDirector = camInterface::GetDebugDirector();
|
|
testMat.d = debugDirector.IsFreeCamActive() ? camInterface::GetPos() : CGameWorld::FindLocalPlayerCoors();
|
|
testMat.d += vecCreateOffset;
|
|
|
|
if(debugDirector.IsFreeCamActive())
|
|
{
|
|
testMat.Set3x3(camInterface::GetMat());
|
|
}
|
|
else if(CGameWorld::FindLocalPlayer())
|
|
{
|
|
testMat.Set3x3(MAT34V_TO_MATRIX34(CGameWorld::FindLocalPlayer()->GetMatrix()));
|
|
}
|
|
|
|
//if(pModel->GetIsTypeObject())
|
|
{
|
|
|
|
bool bForceLoad = false;
|
|
if(!CModelInfo::HaveAssetsLoaded(modelId))
|
|
{
|
|
CModelInfo::RequestAssets(modelId, STRFLAG_DONTDELETE|STRFLAG_FORCE_LOAD|STRFLAG_PRIORITY_LOAD);
|
|
bForceLoad = true;
|
|
}
|
|
|
|
if(bForceLoad)
|
|
{
|
|
CStreaming::LoadAllRequestedObjects(true);
|
|
}
|
|
}
|
|
|
|
if(pModel->GetIsTypeObject())
|
|
{
|
|
fwModelId modelId((strLocalIndex(i)));
|
|
displayObject = CObjectPopulation::CreateObject(modelId, ENTITY_OWNEDBY_RANDOM, true);
|
|
}
|
|
else
|
|
{
|
|
displayObject = rage_new CBuilding( ENTITY_OWNEDBY_AUDIO );
|
|
displayObject->SetModelId(fwModelId(strLocalIndex(i)));
|
|
}
|
|
|
|
if(displayObject)
|
|
{
|
|
displayObject->SetMatrix(testMat);
|
|
CGameWorld::Add(displayObject, CGameWorld::OUTSIDE );
|
|
|
|
syncModelSpawnIndex++;
|
|
|
|
Displayf("[NorthAudio] MACS editor: Spawned %s (index %d)", CModelInfo::GetBaseModelInfoName(modelId), i);
|
|
|
|
if(displayObject->GetIsTypeObject())
|
|
{
|
|
(static_cast<CObject*>(displayObject.Get()))->PlaceOnGroundProperly();
|
|
}
|
|
}
|
|
}
|
|
|
|
++spawnCount;
|
|
}
|
|
}
|
|
|
|
syncModelSpawnIndex = syncModelSpawnIndex < spawnCount ? syncModelSpawnIndex : 0;
|
|
|
|
stream->Close();
|
|
|
|
}
|
|
|
|
void audCollisionAudioEntity::SpawnNewModelObject()
|
|
{
|
|
const u32 maxAudioSettingsNameLength = 128;
|
|
const char *audioSettingsNamePrefix = "MOD_";
|
|
char audioSettingsName[maxAudioSettingsNameLength];
|
|
|
|
s32 spawnCount = 0;
|
|
bool spawnModel = true;
|
|
|
|
fiStream *stream = fiStream::Create("MACS_NewModelsList.txt");
|
|
|
|
if(!stream)
|
|
{
|
|
naErrorf("Couldn't open MACS_NewModelsList.txt");
|
|
return;
|
|
}
|
|
|
|
//Iterate through all loaded models and fix any that have gotten out of synch
|
|
const u32 maxModelInfos = CModelInfo::GetMaxModelInfos();
|
|
for(u32 i=0; i< maxModelInfos; i++)
|
|
{
|
|
CBaseModelInfo * pModel = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(i)));
|
|
if(!pModel)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fwModelId modelId((strLocalIndex(i)));
|
|
|
|
//Try get a Macs for the current model
|
|
strcpy(audioSettingsName, audioSettingsNamePrefix);
|
|
strcat(audioSettingsName, CModelInfo::GetBaseModelInfoName(modelId));
|
|
|
|
//Check if the model is using MOD_DEFAULT, has physics and is not a weapon or a ped or a vehicle
|
|
if(pModel->GetAudioCollisionSettings() == g_MacsDefault && pModel->GetPhysics() && pModel->GetModelType() != MI_TYPE_WEAPON && pModel->GetModelType() != MI_TYPE_PED && pModel->GetModelType() != MI_TYPE_VEHICLE)
|
|
{
|
|
// Displayf("Model type is %d", pModel->GetModelType());
|
|
|
|
if(spawnCount == newModelSpawnIndex && spawnModel)
|
|
{
|
|
//Spawn in an instance of the object so we can set up the MACS using the model editor
|
|
spawnModel = false;
|
|
RegdEnt displayObject(NULL);
|
|
Matrix34 testMat;
|
|
testMat.Identity();
|
|
|
|
Vector3 vecCreateOffset = camInterface::GetFront();
|
|
vecCreateOffset.z = 0.0f;
|
|
vecCreateOffset *= 2.f + pModel->GetBoundingSphereRadius();
|
|
|
|
camDebugDirector & debugDirector = camInterface::GetDebugDirector();
|
|
testMat.d = debugDirector.IsFreeCamActive() ? camInterface::GetPos() : CGameWorld::FindLocalPlayerCoors();
|
|
testMat.d += vecCreateOffset;
|
|
|
|
if(debugDirector.IsFreeCamActive())
|
|
{
|
|
testMat.Set3x3(camInterface::GetMat());
|
|
}
|
|
else if(CGameWorld::FindLocalPlayer())
|
|
{
|
|
testMat.Set3x3(MAT34V_TO_MATRIX34(CGameWorld::FindLocalPlayer()->GetMatrix()));
|
|
}
|
|
|
|
//if(pModel->GetIsTypeObject())
|
|
{
|
|
|
|
bool bForceLoad = false;
|
|
if(!CModelInfo::HaveAssetsLoaded(modelId))
|
|
{
|
|
CModelInfo::RequestAssets(modelId, STRFLAG_DONTDELETE|STRFLAG_FORCE_LOAD|STRFLAG_PRIORITY_LOAD);
|
|
bForceLoad = true;
|
|
}
|
|
|
|
if(bForceLoad)
|
|
{
|
|
CStreaming::LoadAllRequestedObjects(true);
|
|
}
|
|
}
|
|
|
|
if(pModel->GetIsTypeObject())
|
|
{
|
|
fwModelId modelId((strLocalIndex(i)));
|
|
displayObject = CObjectPopulation::CreateObject(modelId, ENTITY_OWNEDBY_RANDOM, true);
|
|
}
|
|
else
|
|
{
|
|
displayObject = rage_new CBuilding( ENTITY_OWNEDBY_AUDIO );
|
|
displayObject->SetModelId(fwModelId(strLocalIndex(i)));
|
|
}
|
|
|
|
if(displayObject)
|
|
{
|
|
displayObject->SetMatrix(testMat);
|
|
CGameWorld::Add(displayObject, CGameWorld::OUTSIDE );
|
|
|
|
newModelSpawnIndex++;
|
|
|
|
Displayf("[NorthAudio] MACS editor: Spawned %s (index %d)", CModelInfo::GetBaseModelInfoName(modelId), i);
|
|
|
|
if(displayObject->GetIsTypeObject())
|
|
{
|
|
(static_cast<CObject*>(displayObject.Get()))->PlaceOnGroundProperly();
|
|
}
|
|
}
|
|
}
|
|
|
|
++spawnCount;
|
|
}
|
|
}
|
|
|
|
newModelSpawnIndex = newModelSpawnIndex < spawnCount ? newModelSpawnIndex : 0;
|
|
|
|
stream->Close();
|
|
|
|
}
|
|
|
|
bool audCollisionAudioEntity::AreMacsMaterialsOutOfDate(const ModelAudioCollisionSettings * macs)
|
|
{
|
|
//Check we've got the right number of materials
|
|
if(macs->numMaterialOverrides > 1 && macs->numMaterialOverrides != s_ModelSettingsCreator.numMaterials)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(macs->numMaterialOverrides > 1)
|
|
{
|
|
//Check none of the materials have changed
|
|
for(s32 i=0; i < macs->numMaterialOverrides; i++)
|
|
{
|
|
bool foundMaterial = false;
|
|
for(int j=0; j < s_ModelSettingsCreator.numMaterials; j++)
|
|
{
|
|
if(audNorthAudioEngine::GetObject<CollisionMaterialSettings>(macs->MaterialOverride[i].Material) ==
|
|
audNorthAudioEngine::GetObject<CollisionMaterialSettings>(s_ModelSettingsCreator.materialNames[j]) )
|
|
{
|
|
foundMaterial = true;
|
|
}
|
|
}
|
|
if(!foundMaterial)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
int numFrags = GetNumFragComponentsFromModelSettings(macs);
|
|
|
|
for(int i=0; i< numFrags; i++)
|
|
{
|
|
const ModelAudioCollisionSettings* fragMacs = GetFragComponentMaterialSettings(macs, i, true);
|
|
if(fragMacs && AreMacsMaterialsOutOfDate(fragMacs))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void audCollisionAudioEntity::OpenCurrentModelInRave()
|
|
{
|
|
atString upSettingName(s_ModelSettingsCreator.audioSettingsName);
|
|
upSettingName.Uppercase();
|
|
|
|
if((strlen(s_ModelSettingsCreator.audioSettingsName) > 0) && audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(s_ModelSettingsCreator.audioSettingsName))
|
|
{
|
|
char xmlMsg[512] = {"<RAVEMessage>\n"};
|
|
char tmpBuf[256] = {0};
|
|
sprintf(tmpBuf, " <ViewObject name=\"%s\" metadataType=\"GameObjects\" chunkNameHash=\"%u\"/>\n", &upSettingName[0], ATSTRINGHASH("BASE", 0x44E21C90));
|
|
strcat(xmlMsg, tmpBuf);
|
|
sprintf(tmpBuf, "</RAVEMessage>\n");
|
|
strcat(xmlMsg, tmpBuf);
|
|
|
|
naDisplayf("XML message is %d characters long \n", istrlen(xmlMsg));
|
|
naDisplayf("%s", 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");
|
|
}
|
|
else
|
|
{
|
|
naDisplayf("===========================");
|
|
naDisplayf("Model doesn't have a ModelAudioCollisionSettings game object yet");
|
|
naDisplayf("===========================");
|
|
}
|
|
}
|
|
|
|
|
|
# endif //__BANK
|
|
|
|
void audCollisionAudioEntity::ReportUproot(CEntity *entity)
|
|
{
|
|
#if __BANK
|
|
if(g_DisableCollisionAudio)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
if(m_CachedUprootEvents.IsFull())
|
|
{
|
|
naErrorf("Cached uproot event array is full");
|
|
return;
|
|
}
|
|
|
|
if(entity && entity->GetBaseModelInfo())
|
|
{
|
|
const ModelAudioCollisionSettings *settings = entity->GetBaseModelInfo()->GetAudioCollisionSettings();
|
|
|
|
if(settings && settings->UprootSound && settings->UprootSound != g_NullSoundHash)
|
|
{
|
|
PF_INCREMENT(audCollisionAudioEntity_Breaks);
|
|
|
|
audUprootEvent & uproot = m_CachedUprootEvents.Append();
|
|
audSoundInitParams initParams;
|
|
// Need to work out now what occlusion group to use
|
|
uproot.entity = entity;
|
|
uproot.pos = entity->GetTransform().GetPosition();
|
|
uproot.uprootSound = settings->UprootSound;
|
|
}
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::TriggerDeferredUproots()
|
|
{
|
|
for(int i=0; i<m_CachedUprootEvents.GetCount(); i++)
|
|
{
|
|
audSoundInitParams initParams;
|
|
// Need to work out now what occlusion group to use
|
|
if(m_CachedUprootEvents[i].entity)
|
|
{
|
|
initParams.EnvironmentGroup = GetOcclusionGroup(m_CachedUprootEvents[i].pos, m_CachedUprootEvents[i].entity);
|
|
}
|
|
initParams.Position = VEC3V_TO_VECTOR3(m_CachedUprootEvents[i].pos);
|
|
if(g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
CreateAndPlaySound(m_CachedUprootEvents[i].uprootSound, &initParams);
|
|
}
|
|
}
|
|
|
|
m_CachedUprootEvents.Reset();
|
|
}
|
|
|
|
void audCollisionAudioEntity::ReportFragmentBreak(const phInst *instance, u32 componentId, bool isDestroyed)
|
|
{
|
|
#if __BANK
|
|
if(g_DisableCollisionAudio)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if(instance == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(m_CachedFragmentBreaks.IsFull())
|
|
{
|
|
naErrorf("Cached fragment break array is full");
|
|
return;
|
|
}
|
|
|
|
CollisionMaterialSettings *materialSettings = NULL;
|
|
CEntity *entity = CPhysics::GetEntityFromInst((phInst *)instance);
|
|
if(entity && entity->GetBaseModelInfo())
|
|
{
|
|
//<const_cast> to let us poke the last frag time
|
|
ModelAudioCollisionSettings *settings = const_cast<ModelAudioCollisionSettings*>(entity->GetBaseModelInfo()->GetAudioCollisionSettings());
|
|
if(!settings)
|
|
{
|
|
Errorf("Base model %s doesn't have a collision settings", entity->GetModelName());
|
|
return;
|
|
}
|
|
|
|
if(g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) - settings->LastFragTime < 1000)
|
|
{
|
|
return;
|
|
}
|
|
|
|
settings->LastFragTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
|
|
audSoundInitParams initParams;
|
|
|
|
u32 breakSound;
|
|
|
|
if(GetNumFragComponentsFromModelSettings(settings))
|
|
{
|
|
const ModelAudioCollisionSettings * fragMod = GetFragComponentMaterialSettings(settings, componentId);
|
|
|
|
//naErrorf("Frag settings %s", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(fragMod->NameTableOffset));
|
|
|
|
//First try to get a break sound from the frag component Macs
|
|
if(fragMod)
|
|
{
|
|
breakSound = isDestroyed && fragMod->DestroySound != g_NullSoundHash ? fragMod->DestroySound : fragMod->BreakSound;
|
|
|
|
if(fragMod->BreakSound && fragMod->BreakSound != g_NullSoundHash)
|
|
{
|
|
audFragmentBreakEvent & fragBreak = m_CachedFragmentBreaks.Append();
|
|
fragBreak.entity = entity;
|
|
fragBreak.pos = entity->GetTransform().GetPosition();
|
|
fragBreak.breakSound = breakSound;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
breakSound = isDestroyed && settings->DestroySound != g_NullSoundHash ? settings->DestroySound : settings->BreakSound;
|
|
|
|
//If that fails look to the parent Macs
|
|
if(breakSound && breakSound != g_NullSoundHash)
|
|
{
|
|
audFragmentBreakEvent & fragBreak = m_CachedFragmentBreaks.Append();
|
|
fragBreak.entity = entity;
|
|
fragBreak.pos = entity->GetTransform().GetPosition();
|
|
fragBreak.breakSound = breakSound;
|
|
return;
|
|
}
|
|
|
|
s32 matid = 0;
|
|
CBaseModelInfo * modelInfo = entity->GetBaseModelInfo();
|
|
|
|
if (modelInfo->GetFragType() && modelInfo->GetFragType()->GetPhysics(0))
|
|
{
|
|
//Finally try get a material settings directly from the bound
|
|
phArchetype *phtype = (phArchetype *) modelInfo->GetFragType()->GetPhysics(0)->GetArchetype();
|
|
if(phtype)
|
|
{
|
|
naAssert(phtype->GetBound() && phtype->GetBound()->GetType()==phBound::COMPOSITE); //Just making sure we're actually looking at a composite bound
|
|
phBoundComposite * composite = (phBoundComposite *)phtype->GetBound();
|
|
if(composite)
|
|
{
|
|
//Get the bound/fragment that has broken off
|
|
phBound * bound = composite->GetBound(componentId);
|
|
if(bound) //Since we got the composite bound from the model, not the entity, it should be the full unbroken bound...but we'll check anyhow
|
|
{
|
|
//since there is no specifies break sound for the component, we'll take a guess at the material to use
|
|
matid = static_cast<s32>(PGTAMATERIALMGR->UnpackMtlId(bound->GetMaterialId(0)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
materialSettings = GetMaterialOverride(entity, g_audCollisionMaterials[matid], componentId);
|
|
|
|
if(materialSettings != NULL)
|
|
{
|
|
breakSound = isDestroyed && materialSettings->DestroySound != g_NullSoundHash ? materialSettings->DestroySound : materialSettings->BreakSound;
|
|
if(breakSound && breakSound != g_NullSoundHash)
|
|
{
|
|
audFragmentBreakEvent & fragBreak = m_CachedFragmentBreaks.Append();
|
|
fragBreak.entity = entity;
|
|
fragBreak.pos = entity->GetTransform().GetPosition();
|
|
fragBreak.breakSound = breakSound;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::TriggerDeferredFragmentBreaks()
|
|
{
|
|
for(int i=0; i<m_CachedFragmentBreaks.GetCount(); i++)
|
|
{
|
|
audSoundInitParams initParams;
|
|
if(m_CachedFragmentBreaks[i].entity)
|
|
{
|
|
initParams.EnvironmentGroup = GetOcclusionGroup(m_CachedFragmentBreaks[i].pos, m_CachedFragmentBreaks[i].entity);
|
|
}
|
|
initParams.Position = VEC3V_TO_VECTOR3(m_CachedFragmentBreaks[i].pos);
|
|
if(g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
CreateAndPlaySound(m_CachedFragmentBreaks[i].breakSound, &initParams);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(m_CachedFragmentBreaks[i].breakSound, &initParams, NULL, NULL, eCollisionAudioEntity));
|
|
}
|
|
}
|
|
|
|
m_CachedFragmentBreaks.Reset();
|
|
}
|
|
|
|
BANK_ONLY(bool g_PedsHitLikeGirls = false);
|
|
|
|
void audCollisionAudioEntity::ReportMeleeCollision(const WorldProbe::CShapeTestHitPoint* pResult, f32 damageDone, CEntity *hittingEntity, const bool isBullet, const bool isFootstep, const bool isPunch, const bool isKick)
|
|
{
|
|
#if __BANK
|
|
if(g_DisableCollisionAudio)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
bool isPlayerHit = false;
|
|
if(hittingEntity && hittingEntity->GetIsTypePed() && ((CPed*)hittingEntity)->IsLocalPlayer())
|
|
{
|
|
isPlayerHit = true;
|
|
}
|
|
|
|
TriggerDeferredMeleeImpact(pResult, damageDone, 1.f, isBullet, isPlayerHit, isFootstep, hittingEntity, isPunch, isKick);
|
|
|
|
}
|
|
|
|
|
|
void audCollisionAudioEntity::ReportHeliBladeCollision(const WorldProbe::CShapeTestHitPoint* pResult, CVehicle* vehicle)
|
|
{
|
|
#if __BANK
|
|
if(g_DisableCollisionAudio)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
if(!pResult)
|
|
return;
|
|
|
|
//Extract the entity involved in this collision.
|
|
CEntity *entity = NULL;
|
|
const phInst *instance = pResult->GetHitInst();
|
|
if(instance)
|
|
{
|
|
entity = CPhysics::GetEntityFromInst((phInst *)instance);
|
|
if(entity == NULL)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(m_CachedMeleeImpacts.IsFull())
|
|
{
|
|
naErrorf("Cached melee impacts array is full");
|
|
return;
|
|
}
|
|
|
|
audCollisionEvent *collisionEvent = &m_CachedMeleeImpacts.Append();
|
|
if(collisionEvent == NULL)
|
|
{
|
|
// Assertf(0, "The audio collision event history is full!");
|
|
return;
|
|
}
|
|
|
|
collisionEvent->impulseMagnitudes[0] = 3.f;
|
|
|
|
collisionEvent->type = AUD_COLLISION_TYPE_IMPACT;
|
|
|
|
PopulateAudioCollisionEventFromIntersection(collisionEvent, pResult);
|
|
|
|
if(vehicle->GetVehicleType() == VEHICLE_TYPE_HELI)
|
|
{
|
|
static_cast<audHeliAudioEntity*>(vehicle->GetVehicleAudioEntity())->TriggerRotorImpactSounds();
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::PopulateAudioCollisionEventFromIntersection(audCollisionEvent *collisionEvent, const WorldProbe::CShapeTestHitPoint* pResult)
|
|
{
|
|
phMaterialMgr::Id materialId = pResult->GetHitMaterialId();
|
|
collisionEvent->positions[0] = pResult->GetHitPosition();
|
|
u16 componentId = pResult->GetHitComponent();
|
|
const phInst *instance = pResult->GetHitInst();
|
|
if(instance)
|
|
{
|
|
collisionEvent->entities[0] = CPhysics::GetEntityFromInst((phInst *)instance);
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->entities[0] = NULL;
|
|
}
|
|
|
|
collisionEvent->components[0] = componentId;
|
|
collisionEvent->components[1] = 0;
|
|
|
|
collisionEvent->materialSettings[0] = NULL;
|
|
collisionEvent->materialSettings[1] = NULL;
|
|
|
|
collisionEvent->materialIds[0] = materialId;
|
|
if(collisionEvent->entities[0] && collisionEvent->entities[0]->GetIsTypeVehicle())
|
|
{
|
|
CVehicle * vehicle = (CVehicle*)collisionEvent->entities[0].Get();
|
|
vehicle->GetVehicleAudioEntity()->GetCollisionAudio().HandleMelee();
|
|
|
|
VehicleCollisionSettings * vehicleCollisionSettings = vehicle->GetVehicleAudioEntity()->GetVehicleCollisionSettings();
|
|
if(vehicleCollisionSettings)
|
|
{
|
|
CollisionMaterialSettings * settings = collisionEvent->materialSettings[0], *overrideSettings = NULL;
|
|
for(s32 i=0; i<vehicleCollisionSettings->numMeleeMaterialOverrides; i++)
|
|
{
|
|
if(vehicleCollisionSettings->MeleeMaterialOverride[0].Material.IsValid() && vehicleCollisionSettings->MeleeMaterialOverride[i].Override.IsValid() && settings == audNorthAudioEngine::GetObject<CollisionMaterialSettings>(vehicleCollisionSettings->MeleeMaterialOverride[i].Material))
|
|
{
|
|
overrideSettings = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(vehicleCollisionSettings->MeleeMaterialOverride[i].Override);
|
|
}
|
|
}
|
|
|
|
if(collisionEvent->GetFlag(AUD_COLLISION_FLAG_BULLET) && vehicle->GetArchetype()->GetModelNameHash() == 0x153E1B0A) // Ortega's trailer, prevents bullets from making the big collision boom
|
|
{
|
|
overrideSettings = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_CAR_METAL", 0xA0231769));
|
|
}
|
|
if(overrideSettings)
|
|
{
|
|
collisionEvent->materialSettings[0] = overrideSettings;
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->materialSettings[0] = vehicle->GetVehicleAudioEntity()->GetCollisionAudio().GetVehicleCollisionMaterialSettings();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!collisionEvent->materialSettings[0])
|
|
{
|
|
s32 mati = 0;
|
|
if(naVerifyf(PGTAMATERIALMGR->UnpackMtlId(pResult->GetHitMaterialId())<(phMaterialMgr::Id)g_audCollisionMaterials.GetCount(), "Unpacked mtl id too large"))
|
|
{
|
|
if(naVerifyf(PGTAMATERIALMGR->UnpackMtlId(pResult->GetHitMaterialId())<(phMaterialMgr::Id)0xffff, "Unpacked mtl id too big for s32 cast"))
|
|
{
|
|
mati = static_cast<s32>(PGTAMATERIALMGR->UnpackMtlId(materialId));
|
|
}
|
|
}
|
|
#if __BANK
|
|
if(g_PrintMeleeImpactMaterial)
|
|
{
|
|
char matname[128] = {0};
|
|
PGTAMATERIALMGR->GetMaterialName(pResult->GetMaterialId(), matname, 128);
|
|
audDisplayf("Melee phys material: %s", matname);
|
|
}
|
|
#endif
|
|
collisionEvent->materialSettings[0] = GetMaterialOverride(collisionEvent->entities[0],g_audCollisionMaterials[mati], collisionEvent->components[0]);
|
|
|
|
#if __BANK
|
|
if(g_PrintMeleeImpactMaterial && collisionEvent->materialSettings[0])
|
|
{
|
|
audDisplayf("Melee material collision settings: %s", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(collisionEvent->materialSettings[0]->NameTableOffset));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
CPed *ped = NULL;
|
|
|
|
if(collisionEvent->entities[0]->GetIsTypePed())
|
|
{
|
|
ped = ((CPed *)(collisionEvent->entities[0].Get()));
|
|
}
|
|
|
|
if(ped && ped->GetPedType() == PEDTYPE_ANIMAL)
|
|
{
|
|
collisionEvent->impulseMagnitudes[0] *= g_AnimalImpactScalar;
|
|
|
|
if(ped->GetCapsuleInfo()->IsBird())
|
|
{
|
|
collisionEvent->materialSettings[0] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_BIRD", 0xBDC5E9E2));
|
|
}
|
|
else if(ped->GetCapsuleInfo()->IsFish())
|
|
{
|
|
collisionEvent->materialSettings[0] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_FISH", 0x51392531));
|
|
}
|
|
else if(ped->GetCapsuleInfo()->IsQuadruped())
|
|
{
|
|
switch(ped->GetArchetype()->GetModelNameHash())
|
|
{
|
|
case 0x97207223: //ATSTRINGHASH("A_C_Horse", 0x97207223)
|
|
case 0xFCFA9E1E: //ATSTRINGHASH("A_C_Cow", 0xFCFA9E1E)
|
|
case 0xD86B5A95: //ATSTRINGHASH("a_c_deer", 0xD86B5A95)
|
|
collisionEvent->materialSettings[0] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_QUADPED_LARGE", 0x628B6797));
|
|
break;
|
|
case 0x14EC17EA: //ATSTRINGHASH("a_c_chop", 0x14EC17EA)
|
|
case 0xCE5FF074: //ATSTRINGHASH("a_c_boar", 0xCE5FF074)
|
|
case 0x644AC75E: //ATSTRINGHASH("a_c_coyote", 0x644AC75E)
|
|
case 0x1250D7BA: //ATSTRINGHASH("a_c_mtlion", 0x1250D7BA)
|
|
case 0xB11BAB56: //ATSTRINGHASH("a_c_pig", 0xB11BAB56)
|
|
case 0x349F33E1: //ATSTRINGHASH("a_c_retriever", 0x349F33E1)
|
|
case 0xC2D06F53: //ATSTRINGHASH("a_c_rhesus", 0xC2D06F53)
|
|
case 0x9563221D: //ATSTRINGHASH("a_c_rottweiler", 0x9563221D)
|
|
case 0xA8683715: //ATSTRINGHASH("a_c_chimp", 0xA8683715)
|
|
case 0x573201B8: //ATSTRINGHASH("a_c_cat_01", 0x573201B8)
|
|
case 0xFC4E657D: //ATSTRINGHASH("A_C_POODLE ", 0xFC4E657D)
|
|
case 0x6D362854: //ATSTRINGHASH("A_C_PUG", 0x6D362854)
|
|
case 0xAD7844BB: //ATSTRINGHASH("A_C_WESTY", 0xAD7844BB)
|
|
collisionEvent->materialSettings[0] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_QUADPED_MED", 0xEDA0B3E4));
|
|
break;
|
|
case 0xC3B52966: //ATSTRINGHASH("a_c_rat", 0xC3B52966)
|
|
case 0xDFB55C81: //ATSTRINGHASH("a_c_rabbit_01", 0xDFB55C81)
|
|
collisionEvent->materialSettings[0] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_QUADPED_SMALL", 0xB5D17E88));
|
|
break;
|
|
default:
|
|
char animalMaterial[32];
|
|
formatf(animalMaterial, "AM_ANIMAL_%08X", ped->GetArchetype()->GetModelNameHash());
|
|
CollisionMaterialSettings * animalSettings = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(animalMaterial);
|
|
if(animalSettings)
|
|
{
|
|
collisionEvent->materialSettings[0] = animalSettings;
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->materialSettings[0] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_QUADPED_MED", 0xEDA0B3E4));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(collisionEvent->GetFlag(AUD_COLLISION_FLAG_MELEE))
|
|
{
|
|
CollisionMaterialSettings * settings = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(collisionEvent->materialSettings[0]->MeleeOverideMaterial);
|
|
if(settings)
|
|
{
|
|
collisionEvent->materialSettings[0] = settings;
|
|
}
|
|
}
|
|
|
|
#if __BANK
|
|
if(g_PrintMeleeImpactMaterial)
|
|
{
|
|
audDisplayf("Melee impact material: %s", collisionEvent->materialSettings[0]? audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(collisionEvent->materialSettings[0]->NameTableOffset): "none");
|
|
}
|
|
#endif
|
|
|
|
collisionEvent->lastImpactTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
}
|
|
|
|
CollisionMaterialSettings * audCollisionAudioEntity::GetMaterialFromIntersection(phIntersection *intersection)
|
|
{
|
|
const CEntity *entity = CPhysics::GetEntityFromInst((phInst *)intersection->GetInstance());
|
|
return GetMaterialSettingsFromMatId(entity, intersection->GetMaterialId(), intersection->GetComponent());
|
|
}
|
|
|
|
CollisionMaterialSettings * audCollisionAudioEntity::GetMaterialFromShapeTestSafe(const WorldProbe::CShapeTestHitPoint *intersection)
|
|
{
|
|
const CEntity *entity = intersection->GetHitEntity();
|
|
return GetMaterialSettingsFromMatId(entity, intersection->GetMaterialId(), intersection->GetComponent());
|
|
}
|
|
|
|
bool audCollisionAudioEntity::PlayScrapeSounds(audCollisionEvent *collisionEvent)
|
|
{
|
|
u8 numSoundsPlayed = 0;
|
|
// f32 scrapeSpeed = ComputeScrapeSpeed(collisionEvent);
|
|
|
|
if(collisionEvent->scrapeMagnitudes[0] > 0.f || collisionEvent->scrapeMagnitudes[1] > 0.f)
|
|
|
|
{
|
|
s32 pitch;
|
|
f32 volume;
|
|
//f32 scaledScrapeSpeed;
|
|
|
|
bool isWet = g_weather.GetWetness() > 0.f;
|
|
if(collisionEvent->entities[0] && collisionEvent->entities[0]->GetIsTypePed() && isWet)
|
|
{
|
|
CPed * ped = ((CPed *)collisionEvent->entities[0].Get());
|
|
if(ped->GetIsInInterior() || !ped->GetIsInExterior())
|
|
{
|
|
isWet = false;
|
|
}
|
|
}
|
|
if(collisionEvent->entities[1] && collisionEvent->entities[1]->GetIsTypePed() && isWet)
|
|
{
|
|
CPed * ped = ((CPed *)collisionEvent->entities[1].Get());
|
|
if(ped->GetIsInInterior() || !ped->GetIsInExterior())
|
|
{
|
|
isWet = false;
|
|
}
|
|
}
|
|
|
|
for(u8 i=0; i<2; i++)
|
|
{
|
|
|
|
|
|
bool isPedCollision = collisionEvent->entities[i] && collisionEvent->entities[i]->GetIsTypePed();
|
|
bool otherIsPedCollision = collisionEvent->entities[(i + 1) & 1] && collisionEvent->entities[(i + 1) & 1]->GetIsTypePed();
|
|
|
|
|
|
CollisionMaterialSettings *materialSettings = collisionEvent->materialSettings[i];
|
|
CollisionMaterialSettings *otherMaterialSettings = collisionEvent->materialSettings[(i + 1) & 1];
|
|
|
|
if(isWet && isPedCollision)
|
|
{
|
|
materialSettings = otherMaterialSettings;
|
|
}
|
|
|
|
if(materialSettings == NULL || materialSettings->ScrapeSound == g_NullSoundHash || collisionEvent->scrapeSounds[i])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
u32 scrapeSound = materialSettings->ScrapeSound;
|
|
audMetadataRef wetScrapeLayer;
|
|
|
|
if(otherMaterialSettings)
|
|
{
|
|
//We don't want to play a scrape if the other material is a lower level of hardness
|
|
if(!isPedCollision && otherIsPedCollision)
|
|
{
|
|
if(isWet)
|
|
{
|
|
CollisionMaterialSettings * wetMaterial = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(materialSettings->WetMaterialReference);
|
|
if(wetMaterial)
|
|
{
|
|
materialSettings = wetMaterial;
|
|
}
|
|
}
|
|
if(materialSettings->PedScrapeSound != g_NullSoundHash)
|
|
{
|
|
scrapeSound = materialSettings->PedScrapeSound;
|
|
}
|
|
else if(materialSettings->HardImpact != g_NullSoundHash &&
|
|
!(AUD_GET_TRISTATE_VALUE(materialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_LOOSESURFACE) == AUD_TRISTATE_TRUE)
|
|
&& !(AUD_GET_TRISTATE_VALUE(materialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_SCRAPESFORPEDS) == AUD_TRISTATE_TRUE))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else if(isPedCollision && isWet)
|
|
{
|
|
wetScrapeLayer = ((CPed*)collisionEvent->entities[i].Get())->GetPedAudioEntity()->GetPedCollisionSoundset()->Find(ATSTRINGHASH("WetScrapeLayer", 0x901A79D0));
|
|
}
|
|
else
|
|
{
|
|
if(otherMaterialSettings->HardImpact == g_NullSoundHash)
|
|
{
|
|
if( (materialSettings->HardImpact != g_NullSoundHash) ||
|
|
(materialSettings->SolidImpact != g_NullSoundHash && otherMaterialSettings->SolidImpact == g_NullSoundHash))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if((otherMaterialSettings && AUD_GET_TRISTATE_VALUE(otherMaterialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_ISSOFTFORLIGHTPROPS) == AUD_TRISTATE_TRUE &&
|
|
GetAudioWeight(collisionEvent->entities[i]) <= AUDIO_WEIGHT_L) && (materialSettings->HardImpact || materialSettings->SolidImpact))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//If the other material is a loose surface then we don't play our scrape; this assumes only map materials are loose and never scrape
|
|
if(!isPedCollision && AUD_GET_TRISTATE_VALUE(otherMaterialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_LOOSESURFACE) == AUD_TRISTATE_TRUE)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//Ensure we don't play the same scrape sound twice for a single collision event.
|
|
if((numSoundsPlayed == 0) || (materialSettings != otherMaterialSettings) || (isWet && isPedCollision))
|
|
{
|
|
//Take the hardness of the other surface into account.
|
|
//TODO: if we decide we want this, probably move it into the lower level
|
|
//scaledScrapeSpeed = collisionEvent->scrapeMagnitudes[i];//scrapeSpeed; // * GetHardnessOfMaterial(otherMaterialSettings);
|
|
|
|
ComputePitchAndVolumeFromScrapeSpeed(collisionEvent, i, pitch, volume);
|
|
|
|
if(volume <= g_SilenceVolume)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(materialSettings->CollisionCount >= g_CollisionMaterialCountThreshold)
|
|
{
|
|
if(volume > materialSettings->VolumeThreshold)
|
|
{
|
|
materialSettings->VolumeThreshold = volume;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
audSoundInitParams initParams;
|
|
|
|
|
|
// Need to work out now what occlusion group to use
|
|
initParams.EnvironmentGroup = GetOcclusionGroup(collisionEvent);
|
|
|
|
PF_INCREMENT(audCollisionAudioEntity_Scrapes);
|
|
if(isPedCollision && isWet)
|
|
{
|
|
CreateSound_PersistentReference(wetScrapeLayer, &(collisionEvent->scrapeSounds[i]),
|
|
&initParams);
|
|
}
|
|
else
|
|
{
|
|
CreateSound_PersistentReference(scrapeSound, &(collisionEvent->scrapeSounds[i]),
|
|
&initParams);
|
|
}
|
|
|
|
if(collisionEvent->scrapeSounds[i])
|
|
{
|
|
collisionEvent->scrapeSounds[i]->SetRequestedPitch(pitch);
|
|
collisionEvent->scrapeSounds[i]->SetRequestedVolume(volume);
|
|
collisionEvent->scrapeSounds[i]->SetRequestedPosition(collisionEvent->positions[i]);
|
|
|
|
collisionEvent->scrapeSounds[i]->PrepareAndPlay();
|
|
|
|
//naErrorf("Playing scrape sounds material %s, entity %p, index %d, time %d", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(materialSettings->NameTableOffset), collisionEvent->entities[i].Get(), i, fwTimer::GetTimeInMilliseconds());
|
|
|
|
#if __BANK
|
|
DebugAudioImpact(collisionEvent->positions[0], collisionEvent->components[0], collisionEvent->components[1], collisionEvent->impulseMagnitudes[0], (audCollisionType)collisionEvent->type);
|
|
#endif // #if __BANK
|
|
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::ShouldRecord())
|
|
{
|
|
// We only need the entity to figure out the occlusion group so following what the 'GetOcclusionGroup' function does
|
|
// we need entity[0] if it is valid...and entity[1] if not.
|
|
CEntity* pEntity = NULL;
|
|
CEntity* pEntity2 = NULL;
|
|
naEnvironmentGroup* pGroup = NULL;
|
|
|
|
if(collisionEvent->entities[0])
|
|
{
|
|
pEntity = collisionEvent->entities[0];
|
|
pEntity2 = collisionEvent->entities[1];
|
|
pGroup = (naEnvironmentGroup*)pEntity->GetAudioEnvironmentGroup();
|
|
}
|
|
if(pGroup == NULL && collisionEvent->entities[1])
|
|
{
|
|
pEntity = collisionEvent->entities[0];
|
|
pEntity2 = collisionEvent->entities[1];
|
|
pGroup = (naEnvironmentGroup*)pEntity->GetAudioEnvironmentGroup();
|
|
}
|
|
|
|
if(pGroup)
|
|
{
|
|
CReplayMgr::RecordPersistantFx<CPacketCollisionPlayPacket>(
|
|
CPacketCollisionPlayPacket(materialSettings->ScrapeSound,
|
|
pitch,
|
|
volume,
|
|
collisionEvent->positions,
|
|
i),
|
|
CTrackedEventInfo<tTrackedSoundType>(collisionEvent->scrapeSounds[i]),
|
|
pEntity,
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
CReplayMgr::RecordPersistantFx<CPacketCollisionPlayPacket>(
|
|
CPacketCollisionPlayPacket(materialSettings->ScrapeSound,
|
|
pitch,
|
|
volume,
|
|
collisionEvent->positions,
|
|
collisionEvent->materialIds,
|
|
i),
|
|
CTrackedEventInfo<tTrackedSoundType>(collisionEvent->scrapeSounds[i]),
|
|
pEntity,
|
|
pEntity2,
|
|
true);
|
|
}
|
|
}
|
|
#endif // GTA_REPLAY
|
|
numSoundsPlayed++;
|
|
|
|
if(collisionEvent->materialSettings[i] && g_UseCollisionIntensityCulling)
|
|
{
|
|
if(!g_RecentlyCollidedMaterials.IsMemberOfList(collisionEvent->materialSettings[i]))
|
|
{
|
|
g_RecentlyCollidedMaterials.Add(collisionEvent->materialSettings[i]);
|
|
}
|
|
collisionEvent->materialSettings[i]->CollisionCount++;
|
|
}
|
|
|
|
#if __BANK
|
|
PF_INCREMENT(CollisonsProcessed);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (numSoundsPlayed > 0);
|
|
}
|
|
|
|
void audCollisionAudioEntity::UpdateScrapeSounds(audCollisionEvent *collisionEvent)
|
|
{
|
|
//f32 scrapeSpeed = ComputeScrapeSpeed(collisionEvent);
|
|
s32 pitch;
|
|
f32 volume;
|
|
for(u8 i=0; i<2; i++)
|
|
{
|
|
if(collisionEvent->scrapeSounds[i] && collisionEvent->materialSettings[i])
|
|
{
|
|
if(collisionEvent->scrapeMagnitudes[i] && collisionEvent->scrapeMagnitudes[i] > collisionEvent->materialSettings[i]->MinScrapeSpeed)
|
|
{
|
|
ComputePitchAndVolumeFromScrapeSpeed(collisionEvent, i, pitch, volume);
|
|
|
|
if(volume < (collisionEvent->scrapeSounds[i]->GetRequestedVolume() - 0.003f*(float)collisionEvent->scrapeSounds[i]->GetSimpleReleaseTime()))
|
|
{
|
|
volume = collisionEvent->scrapeSounds[i]->GetRequestedVolume() - 0.003f*(float)collisionEvent->scrapeSounds[i]->GetSimpleReleaseTime();
|
|
}
|
|
|
|
if(pitch < (collisionEvent->scrapeSounds[i]->GetRequestedPitch() - 0.003f*(float)collisionEvent->scrapeSounds[i]->GetSimpleReleaseTime()))
|
|
{
|
|
pitch = (s32)(collisionEvent->scrapeSounds[i]->GetRequestedPitch() - 0.003f*(float)collisionEvent->scrapeSounds[i]->GetSimpleReleaseTime());
|
|
}
|
|
|
|
collisionEvent->scrapeSounds[i]->SetRequestedPitch(pitch);
|
|
collisionEvent->scrapeSounds[i]->SetRequestedVolume(volume);
|
|
collisionEvent->scrapeSounds[i]->SetRequestedPosition(collisionEvent->positions[i]);
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->scrapeSounds[i]->StopAndForget();
|
|
|
|
//naErrorf("Stopping scrape sound on material %s time %u", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(collisionEvent->materialSettings[i]->NameTableOffset), fwTimer::GetTimeInMilliseconds());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool audCollisionAudioEntity::PlayRollSounds(audCollisionEvent *collisionEvent)
|
|
{
|
|
u8 numSoundsPlayed = 0;
|
|
// f32 rollSpeed = ComputeRollSpeed(collisionEvent);
|
|
|
|
if(collisionEvent->rollMagnitudes[0] > 0.0f || collisionEvent->rollMagnitudes[1] > 0.f)
|
|
{
|
|
for(u8 i=0; i<2; i++)
|
|
{
|
|
CollisionMaterialSettings *materialSettings = collisionEvent->materialSettings[i];
|
|
CollisionMaterialSettings *otherMaterialSettings = collisionEvent->materialSettings[(i+1)&1];
|
|
|
|
if(materialSettings == NULL || collisionEvent->rollSounds[i])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Check to see if we're a map material or if we're on a material that allows roll sounds
|
|
u32 rollSound = materialSettings->RollSound;
|
|
|
|
|
|
if(rollSound == g_NullSoundHash)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
s32 pitch;
|
|
f32 volume;
|
|
|
|
ComputePitchAndVolumeFromRollSpeed(collisionEvent, i, pitch, volume);
|
|
|
|
CollisionMaterialSettings * otherSettings = collisionEvent->materialSettings[(i+1)&1];
|
|
|
|
if(otherSettings)
|
|
{
|
|
if(otherSettings->HardImpact && otherSettings->HardImpact != g_NullSoundHash)
|
|
{
|
|
//roll volume stays the same
|
|
}
|
|
else if(otherSettings->SolidImpact && otherSettings->SolidImpact != g_NullSoundHash)
|
|
{
|
|
volume+= g_SolidRollAttenuation;
|
|
}
|
|
else
|
|
{
|
|
volume += g_SoftRollAttenuation;
|
|
}
|
|
}
|
|
|
|
if(collisionEvent->materialSettings[i]->CollisionCount >= g_CollisionMaterialCountThreshold)
|
|
{
|
|
if(volume > collisionEvent->materialSettings[i]->VolumeThreshold)
|
|
{
|
|
collisionEvent->materialSettings[i]->VolumeThreshold = volume;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//Ensure we don't play the same scrape sound twice for a single collision event.
|
|
if((numSoundsPlayed == 0) || (materialSettings != otherMaterialSettings))
|
|
{
|
|
audSoundInitParams initParams;
|
|
// Need to work out now what occlusion group to use
|
|
initParams.EnvironmentGroup = GetOcclusionGroup(collisionEvent);
|
|
|
|
PF_INCREMENT(audCollisionAudioEntity_Rolls);
|
|
CreateSound_PersistentReference(rollSound, &(collisionEvent->rollSounds[i]),
|
|
&initParams);
|
|
if(collisionEvent->rollSounds[i])
|
|
{
|
|
collisionEvent->rollSounds[i]->SetClientVariable(0.0f);
|
|
collisionEvent->rollSounds[i]->SetRequestedVolume(g_SilenceVolume);
|
|
collisionEvent->rollSounds[i]->SetRequestedPosition(collisionEvent->positions[i]);
|
|
|
|
collisionEvent->rollSounds[i]->PrepareAndPlay();
|
|
numSoundsPlayed++;
|
|
|
|
//naErrorf("Playing roll sounds material %s, entity %p", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(materialSettings->NameTableOffset), collisionEvent->entities[i]);
|
|
#if __BANK
|
|
DebugAudioImpact(collisionEvent->positions[0], collisionEvent->components[0], collisionEvent->components[1], collisionEvent->impulseMagnitudes[0], (audCollisionType)collisionEvent->type);
|
|
#endif // #if __BANK
|
|
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::ShouldRecord())
|
|
{
|
|
CEntity* pEntity = NULL;
|
|
CEntity* pEntity2 = NULL;
|
|
naEnvironmentGroup* pGroup = NULL;
|
|
|
|
if(collisionEvent->entities[0])
|
|
{
|
|
pEntity = collisionEvent->entities[0];
|
|
pEntity2 = collisionEvent->entities[1];
|
|
pGroup = (naEnvironmentGroup*)pEntity->GetAudioEnvironmentGroup();
|
|
}
|
|
if(pGroup == NULL && collisionEvent->entities[1])
|
|
{
|
|
pEntity = collisionEvent->entities[0];
|
|
pEntity2 = collisionEvent->entities[1];
|
|
pGroup = (naEnvironmentGroup*)pEntity->GetAudioEnvironmentGroup();
|
|
}
|
|
|
|
if(pGroup)
|
|
{
|
|
CReplayMgr::RecordPersistantFx<CPacketCollisionPlayPacket>(
|
|
CPacketCollisionPlayPacket(rollSound,
|
|
g_SilenceVolume,
|
|
collisionEvent->positions,
|
|
i),
|
|
CTrackedEventInfo<tTrackedSoundType>(collisionEvent->rollSounds[i]),
|
|
pEntity,
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
CReplayMgr::RecordPersistantFx<CPacketCollisionPlayPacket>(
|
|
CPacketCollisionPlayPacket(rollSound,
|
|
g_SilenceVolume,
|
|
collisionEvent->positions,
|
|
collisionEvent->materialIds,
|
|
i),
|
|
CTrackedEventInfo<tTrackedSoundType>(collisionEvent->rollSounds[i]),
|
|
pEntity,
|
|
pEntity2,
|
|
true);
|
|
}
|
|
}
|
|
#endif // GTA_REPLAY
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (numSoundsPlayed > 0);
|
|
}
|
|
|
|
|
|
void audCollisionAudioEntity::UpdateRollSounds(audCollisionEvent *collisionEvent)
|
|
{
|
|
// f32 rollSpeed = ComputeRollSpeed(collisionEvent);
|
|
s32 pitch;
|
|
f32 volume;
|
|
|
|
bool isMap[2] = {collisionEvent->material0IsMap, collisionEvent->material1IsMap};
|
|
|
|
for(u8 i=0; i<2; i++)
|
|
{
|
|
if(collisionEvent->rollSounds[i] && collisionEvent->materialSettings[i])
|
|
{
|
|
f32 minRollSpeed = (isMap[i] && g_MapUsesPropRollSpeedSettings) || !collisionEvent->materialSettings[(i+1)&1] ?
|
|
collisionEvent->materialSettings[i]->MinRollSpeed : collisionEvent->materialSettings[(i+1)&1]->MinRollSpeed;
|
|
|
|
if(collisionEvent->rollMagnitudes[i] > minRollSpeed)
|
|
{
|
|
ComputePitchAndVolumeFromRollSpeed(collisionEvent, i, pitch, volume);
|
|
|
|
CollisionMaterialSettings * otherSettings = collisionEvent->materialSettings[(i+1)&1];
|
|
|
|
if(otherSettings)
|
|
{
|
|
if(otherSettings->HardImpact && otherSettings->HardImpact != g_NullSoundHash)
|
|
{
|
|
//roll volume stays the same
|
|
}
|
|
else if(otherSettings->SolidImpact && otherSettings->SolidImpact != g_NullSoundHash)
|
|
{
|
|
volume+= g_SolidRollAttenuation;
|
|
}
|
|
else
|
|
{
|
|
volume += g_SoftRollAttenuation;
|
|
}
|
|
}
|
|
|
|
//FIXME: Pitch seems to be coming out with nan's at the moment
|
|
// collisionEvent->rollSounds[i]->SetRequestedPitch(pitch);
|
|
collisionEvent->rollSounds[i]->SetClientVariable(volume);
|
|
collisionEvent->rollSounds[i]->SetRequestedVolume(volume);
|
|
collisionEvent->rollSounds[i]->SetRequestedPosition(collisionEvent->positions[i]);
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->rollSounds[i]->StopAndForget();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
f32 audCollisionAudioEntity::ComputeScaledImpulseMagnitude(audCollisionEvent *collisionEvent, u32 index)
|
|
{
|
|
CollisionMaterialSettings *materialSettings = collisionEvent->materialSettings[index];
|
|
if(!naVerifyf(materialSettings, "Invalid CollisionMaterialSettings passed into ComputeScaledaImpulseMagnitude"))
|
|
{
|
|
return 0.f;
|
|
}
|
|
|
|
|
|
f32 maxImpulse = materialSettings->MaxImpulseMag;
|
|
f32 minImpulse = materialSettings->MinImpulseMag;
|
|
f32 impulseMagScalar = materialSettings->ImpulseMagScalar * s_ImpactMagScalar;
|
|
|
|
// impulse mag scalar is fixed point
|
|
const f32 scaledMaxImpulseMag = (f32)maxImpulse;
|
|
const f32 scaledMinImpulseMag = (f32)minImpulse;
|
|
|
|
f32 scaledImpulseMag = Max(collisionEvent->impulseMagnitudes[index]*impulseMagScalar - scaledMinImpulseMag, 0.0f);
|
|
scaledImpulseMag = Min(scaledImpulseMag / scaledMaxImpulseMag, 1.0f);
|
|
|
|
return scaledImpulseMag;
|
|
}
|
|
|
|
f32 audCollisionAudioEntity::ComputeScaledImpactMagnitude(f32 impactMag, CollisionMaterialSettings * materialSettings)
|
|
{
|
|
if(!naVerifyf(materialSettings, "Invalid CollisionMaterialSettings passed into ComputeScaledaImpulseMagnitude"))
|
|
{
|
|
return 0.f;
|
|
}
|
|
|
|
|
|
f32 maxImpulse = materialSettings->MaxImpulseMag;
|
|
f32 minImpulse = materialSettings->MinImpulseMag;
|
|
f32 impulseMagScalar = materialSettings->ImpulseMagScalar * s_ImpactMagScalar;
|
|
|
|
// impulse mag scalar is fixed point
|
|
const f32 scaledMaxImpulseMag = (f32)maxImpulse;
|
|
const f32 scaledMinImpulseMag = (f32)minImpulse;
|
|
|
|
f32 scaledImpulseMag = Max(impactMag*impulseMagScalar - scaledMinImpulseMag, 0.0f);
|
|
scaledImpulseMag = Min(scaledImpulseMag / scaledMaxImpulseMag, 1.0f);
|
|
|
|
return scaledImpulseMag;
|
|
}
|
|
|
|
void audCollisionAudioEntity::ComputeStartOffsetAndVolumeFromImpulseMagnitude(audCollisionEvent *collisionEvent, u32 index, u32 &startOffset, f32 &volume, bool isPed)
|
|
{
|
|
startOffset = 0;
|
|
volume = g_SilenceVolume;
|
|
|
|
CollisionMaterialSettings *materialSettings = collisionEvent->materialSettings[index];
|
|
|
|
if(materialSettings)
|
|
{
|
|
//Scale the impulse magnitude to a 0->1 range.
|
|
f32 scaledImpulseMagnitude = ComputeScaledImpulseMagnitude(collisionEvent, index);
|
|
if(scaledImpulseMagnitude > 0.f)
|
|
{
|
|
// naDisplayf("Scaled impulse magnitude: %f", scaledImpulseMagnitude);
|
|
//Derive a start offset percentage from a curve.
|
|
if (AUD_GET_TRISTATE_VALUE(collisionEvent->materialSettings[index]->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_USECUSTOMCURVES) != AUD_TRISTATE_TRUE)
|
|
{
|
|
startOffset = (u32)floorf((isPed?m_ImpactStartOffsetCurve.CalculateValue(scaledImpulseMagnitude):m_ImpactStartOffsetCurve.CalculateValue(scaledImpulseMagnitude)) * 100.0f);
|
|
}
|
|
else
|
|
{
|
|
audCurve curve;
|
|
if(curve.Init(materialSettings->ImpactStartOffsetCurve))
|
|
{
|
|
startOffset = (u32)floorf(curve.CalculateValue(scaledImpulseMagnitude) * 100.0f);
|
|
}
|
|
}
|
|
|
|
//Derive a volume from a curve.
|
|
if (AUD_GET_TRISTATE_VALUE(collisionEvent->materialSettings[index]->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_USECUSTOMCURVES) != AUD_TRISTATE_TRUE)
|
|
{
|
|
f32 volumeLinear = isPed ? m_PedImpactVolCurve.CalculateValue(scaledImpulseMagnitude):m_ImpactVolCurve.CalculateValue(scaledImpulseMagnitude);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
else
|
|
{
|
|
audCurve curve;
|
|
if(curve.Init(materialSettings->ImpactVolCurve))
|
|
{
|
|
f32 volumeLinear = curve.CalculateValue(scaledImpulseMagnitude);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::ComputeStartOffsetAndVolumeFromImpactMagnitude(f32 impactMag, CollisionMaterialSettings *materialSettings, u32 &startOffset, f32 &volume, bool isPed)
|
|
{
|
|
startOffset = 0;
|
|
volume = g_SilenceVolume;
|
|
|
|
if(materialSettings)
|
|
{
|
|
//Scale the impulse magnitude to a 0->1 range.
|
|
f32 scaledImpulseMagnitude = ComputeScaledImpactMagnitude(impactMag, materialSettings);
|
|
if(scaledImpulseMagnitude > 0.f)
|
|
{
|
|
// naDisplayf("Scaled impulse magnitude: %f", scaledImpulseMagnitude);
|
|
//Derive a start offset percentage from a curve.
|
|
if (AUD_GET_TRISTATE_VALUE(materialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_USECUSTOMCURVES) != AUD_TRISTATE_TRUE)
|
|
{
|
|
startOffset = (u32)floorf(( isPed?m_PedImpactStartOffsetCurve.CalculateValue(scaledImpulseMagnitude):m_ImpactStartOffsetCurve.CalculateValue(scaledImpulseMagnitude)) * 100.0f);
|
|
}
|
|
else
|
|
{
|
|
audCurve curve;
|
|
if(curve.Init(materialSettings->ImpactStartOffsetCurve))
|
|
{
|
|
startOffset = (u32)floorf(curve.CalculateValue(scaledImpulseMagnitude) * 100.0f);
|
|
}
|
|
}
|
|
|
|
//Derive a volume from a curve.
|
|
if (AUD_GET_TRISTATE_VALUE(materialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_USECUSTOMCURVES) != AUD_TRISTATE_TRUE)
|
|
{
|
|
f32 volumeLinear = isPed? m_PedImpactVolCurve.CalculateValue(scaledImpulseMagnitude):m_ImpactVolCurve.CalculateValue(scaledImpulseMagnitude);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
else
|
|
{
|
|
audCurve curve;
|
|
if(curve.Init(materialSettings->ImpactVolCurve))
|
|
{
|
|
f32 volumeLinear = curve.CalculateValue(scaledImpulseMagnitude);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
f32 audCollisionAudioEntity::ComputeScrapeSpeed(audCollisionEvent *collisionEvent)
|
|
{
|
|
CEntity *entity = collisionEvent->entities[0];
|
|
Vector3 moveSpeed(0.0f, 0.0f, 0.0f);
|
|
Vector3 turnSpeed(0.0f, 0.0f, 0.0f);
|
|
if(entity && entity->GetIsPhysical())
|
|
{
|
|
moveSpeed = ((CPhysical *)entity)->GetVelocity();
|
|
turnSpeed = ((CPhysical *)entity)->GetAngVelocity();
|
|
}
|
|
|
|
CEntity *otherEntity = collisionEvent->entities[1];
|
|
Vector3 otherMoveSpeed(0.0f, 0.0f, 0.0f);
|
|
Vector3 otherTurnSpeed(0.0f, 0.0f, 0.0f);
|
|
if(otherEntity && otherEntity->GetIsPhysical())
|
|
{
|
|
otherMoveSpeed = ((CPhysical *)otherEntity)->GetVelocity();
|
|
otherTurnSpeed = ((CPhysical *)otherEntity)->GetAngVelocity();
|
|
}
|
|
|
|
f32 relativeMoveSpeed = (moveSpeed - otherMoveSpeed).Mag();
|
|
f32 relativeTurnSpeed = (turnSpeed - otherTurnSpeed).Mag();
|
|
f32 scrapeSpeed = Max(relativeMoveSpeed, relativeTurnSpeed);
|
|
|
|
return scrapeSpeed;
|
|
}
|
|
|
|
void audCollisionAudioEntity::ComputePitchAndVolumeFromRollSpeed(audCollisionEvent *collisionEvent, u32 index, s32 &pitch, f32 &volume)
|
|
{
|
|
pitch = 0;
|
|
volume = g_SilenceVolume;
|
|
|
|
CollisionMaterialSettings *materialSettings = collisionEvent->materialSettings[index];
|
|
|
|
bool isMap[2] = {collisionEvent->material0IsMap, collisionEvent->material1IsMap};
|
|
|
|
if(materialSettings)
|
|
{
|
|
f32 minRollSpeed = materialSettings->MinRollSpeed;
|
|
f32 maxRollSpeed = materialSettings->MaxRollSpeed;
|
|
|
|
if(collisionEvent->materialSettings[(index+1)&1] && isMap[index] && g_MapUsesPropRollSpeedSettings)
|
|
{
|
|
minRollSpeed = collisionEvent->materialSettings[(index+1)&1]->MinRollSpeed;
|
|
maxRollSpeed = collisionEvent->materialSettings[(index+1)&1]->MaxRollSpeed;
|
|
naAssertf(maxRollSpeed > 0.f, "Sound Designers: looks like material %s has an invalid MaxRollSpeed (%f); it's probably not doing what you want it to.", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(collisionEvent->materialSettings[(index+1)&1]->NameTableOffset), maxRollSpeed);
|
|
}
|
|
|
|
f32 scaledRollSpeed = maxRollSpeed > 0.f ? Min((collisionEvent->rollMagnitudes[index]-minRollSpeed) / maxRollSpeed, 1.0f) : 0.f;
|
|
|
|
//Derive a pitch from a curve.
|
|
if (AUD_GET_TRISTATE_VALUE(collisionEvent->materialSettings[index]->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_USECUSTOMCURVES) != AUD_TRISTATE_TRUE)
|
|
{
|
|
pitch = audDriverUtil::ConvertRatioToPitch(m_RollPitchCurve.CalculateValue(scaledRollSpeed));
|
|
}
|
|
else
|
|
{
|
|
audCurve curve;
|
|
if(curve.Init(materialSettings->ScrapePitchCurve))
|
|
{
|
|
pitch = audDriverUtil::ConvertRatioToPitch(curve.CalculateValue(scaledRollSpeed));
|
|
}
|
|
}
|
|
|
|
//Derive a volume from a curve.
|
|
if (AUD_GET_TRISTATE_VALUE(collisionEvent->materialSettings[index]->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_USECUSTOMCURVES) != AUD_TRISTATE_TRUE)
|
|
{
|
|
f32 volumeLinear = m_RollVolCurve.CalculateValue(scaledRollSpeed);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
else
|
|
{
|
|
audCurve curve;
|
|
if(curve.Init(materialSettings->ScrapeVolCurve))
|
|
{
|
|
f32 volumeLinear = curve.CalculateValue(scaledRollSpeed);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pitch = 0;
|
|
volume = -100.f;
|
|
}
|
|
}
|
|
|
|
f32 audCollisionAudioEntity::ComputeRollSpeed(audCollisionEvent *collisionEvent)
|
|
{
|
|
CEntity *entity = collisionEvent->entities[0];
|
|
Vector3 moveSpeed(0.0f, 0.0f, 0.0f);
|
|
Vector3 turnSpeed(0.0f, 0.0f, 0.0f);
|
|
if(entity && entity->GetIsPhysical())
|
|
{
|
|
moveSpeed = ((CPhysical *)entity)->GetVelocity();
|
|
turnSpeed = ((CPhysical *)entity)->GetAngVelocity();
|
|
}
|
|
|
|
CEntity *otherEntity = collisionEvent->entities[1];
|
|
Vector3 otherMoveSpeed(0.0f, 0.0f, 0.0f);
|
|
Vector3 otherTurnSpeed(0.0f, 0.0f, 0.0f);
|
|
if(otherEntity && otherEntity->GetIsPhysical())
|
|
{
|
|
otherMoveSpeed = ((CPhysical *)otherEntity)->GetVelocity();
|
|
otherTurnSpeed = ((CPhysical *)otherEntity)->GetAngVelocity();
|
|
}
|
|
|
|
f32 rollSpeed = Max(turnSpeed.Mag(), otherTurnSpeed.Mag());
|
|
|
|
return rollSpeed;
|
|
}
|
|
|
|
bool audCollisionAudioEntity::EntityIsInWater(CEntity * entity)
|
|
{
|
|
if(entity && entity->GetIsPhysical() && ((CPhysical*)entity)->GetIsInWater())
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void audCollisionAudioEntity::ComputePitchAndVolumeFromScrapeSpeed(audCollisionEvent *collisionEvent, u32 index, s32 &pitch, f32 &volume)
|
|
{
|
|
pitch = 0;
|
|
volume = g_SilenceVolume;
|
|
int otherIndex = (index+1)&1;
|
|
|
|
f32 wetness = g_weather.GetWetness();
|
|
|
|
CollisionMaterialSettings *materialSettings = collisionEvent->materialSettings[index];
|
|
|
|
if(collisionEvent->entities[otherIndex]->GetType() == ENTITY_TYPE_VEHICLE)
|
|
{
|
|
((CVehicle *)collisionEvent->entities[otherIndex].Get())->GetVehicleAudioEntity()->GetCollisionAudio().ComputePitchAndVolumeFromScrapeSpeed(collisionEvent, index, pitch, volume);
|
|
return;
|
|
}
|
|
|
|
if(EntityIsInWater(collisionEvent->entities[index]) || EntityIsInWater(collisionEvent->entities[otherIndex]))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// this allows only very heavy objects to scrape on vehicles(crates inside titan plane in exile1)
|
|
bool secondMaterialisOk = true;
|
|
if(!collisionEvent->materialSettings[otherIndex])
|
|
{
|
|
if((collisionEvent->entities[index] && collisionEvent->entities[otherIndex]) &&
|
|
(collisionEvent->entities[otherIndex]->GetType() != ENTITY_TYPE_VEHICLE ||
|
|
GetAudioWeight(collisionEvent->entities[index]) != AUDIO_WEIGHT_VH ||
|
|
collisionEvent->entities[index]->GetType() != ENTITY_TYPE_OBJECT))
|
|
{
|
|
secondMaterialisOk = false;
|
|
}
|
|
}
|
|
|
|
if(collisionEvent->materialSettings[index] && secondMaterialisOk)
|
|
{
|
|
bool isMap[2] = {collisionEvent->material0IsMap, collisionEvent->material1IsMap};
|
|
|
|
bool isPed = collisionEvent->entities[index] && collisionEvent->entities[index]->GetIsTypePed();
|
|
bool isPlayer = false;
|
|
if(isPed)
|
|
{
|
|
if(((CPed *)collisionEvent->entities[index].Get())->IsLocalPlayer())
|
|
{
|
|
isPlayer = true;
|
|
}
|
|
}
|
|
bool otherIsPed = collisionEvent->entities[otherIndex] && collisionEvent->entities[otherIndex]->GetIsTypePed();
|
|
bool otherIsPlayer = false;
|
|
if(otherIsPlayer)
|
|
{
|
|
if(((CPed *)collisionEvent->entities[otherIndex].Get())->IsLocalPlayer())
|
|
{
|
|
otherIndex = true;
|
|
}
|
|
}
|
|
|
|
f32 minScrapeSpeed = materialSettings->MinScrapeSpeed;
|
|
f32 maxScrapeSpeed = materialSettings->MaxScrapeSpeed;
|
|
|
|
if((otherIsPed && !isPed) || (collisionEvent->materialSettings[otherIndex] && isMap[index] && g_MapUsesPropRollSpeedSettings))
|
|
{
|
|
minScrapeSpeed = collisionEvent->materialSettings[otherIndex]->MinScrapeSpeed;
|
|
maxScrapeSpeed = collisionEvent->materialSettings[otherIndex]->MaxScrapeSpeed;
|
|
|
|
}
|
|
|
|
//Scale the scrape speed to a 0->1 range.
|
|
f32 scaledScrapeSpeed = ClampRange((collisionEvent->scrapeMagnitudes[index]- minScrapeSpeed) / (maxScrapeSpeed-minScrapeSpeed), 0.f, 1.0f);
|
|
|
|
if(!FPIsFinite(scaledScrapeSpeed))
|
|
{
|
|
naDebugf1("Got a NAN in ComputePitchAndVolumeFromScrapeSpeed");
|
|
scaledScrapeSpeed = 0.f;
|
|
}
|
|
|
|
//Derive a pitch from a curve.
|
|
if(isPed || otherIsPed)
|
|
{
|
|
#if __BANK
|
|
if(g_DisplayPedScrapeMaterial)
|
|
{
|
|
naDisplayf("Ped scrape material: %s", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(collisionEvent->materialSettings[index]->NameTableOffset));
|
|
}
|
|
#endif
|
|
u32 pedIndex = isPed ? index : otherIndex;
|
|
|
|
AnimalParams * animalParams = audNorthAudioEngine::GetObject<AnimalParams>(((CPed *)collisionEvent->entities[pedIndex].Get())->GetPedModelInfo()->GetAnimalAudioObject());
|
|
audCurve animalCurve;
|
|
if(animalParams)
|
|
{
|
|
animalCurve.Init(animalParams->ScrapePitchCurve);
|
|
}
|
|
|
|
if(isPlayer || otherIsPlayer)
|
|
{
|
|
pitch = audDriverUtil::ConvertRatioToPitch(m_PlayerScrapePitchCurve.CalculateValue(scaledScrapeSpeed));
|
|
}
|
|
else if(animalParams && animalCurve.IsValid())
|
|
{
|
|
pitch = audDriverUtil::ConvertRatioToPitch(m_PedScrapePitchCurve.CalculateValue(scaledScrapeSpeed));
|
|
}
|
|
else
|
|
{
|
|
pitch = audDriverUtil::ConvertRatioToPitch(m_PedScrapePitchCurve.CalculateValue(scaledScrapeSpeed));
|
|
}
|
|
}
|
|
else if (AUD_GET_TRISTATE_VALUE(collisionEvent->materialSettings[index]->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_USECUSTOMCURVES) != AUD_TRISTATE_TRUE)
|
|
{
|
|
pitch = audDriverUtil::ConvertRatioToPitch(m_ScrapePitchCurve.CalculateValue(scaledScrapeSpeed));
|
|
}
|
|
else
|
|
{
|
|
audCurve curve;
|
|
if(curve.Init(materialSettings->ScrapePitchCurve))
|
|
{
|
|
pitch = audDriverUtil::ConvertRatioToPitch(curve.CalculateValue(scaledScrapeSpeed));
|
|
}
|
|
}
|
|
|
|
//Derive a volume from a curve.
|
|
if(isPed || otherIsPed)
|
|
{
|
|
u32 pedIndex = isPed ? index : otherIndex;
|
|
|
|
AnimalParams * animalParams = audNorthAudioEngine::GetObject<AnimalParams>(((CPed *)collisionEvent->entities[pedIndex].Get())->GetPedModelInfo()->GetAnimalAudioObject());
|
|
audCurve animalCurve;
|
|
if(animalParams)
|
|
{
|
|
animalCurve.Init(animalParams->ScrapeVolCurve);
|
|
}
|
|
|
|
f32 volumeLinear = 0.f;
|
|
if(isPlayer || otherIsPlayer)
|
|
{
|
|
volumeLinear = m_PlayerScrapeVolCurve.CalculateValue(scaledScrapeSpeed);
|
|
}
|
|
else if(animalParams && animalCurve.IsValid()) //ANIMAL
|
|
{
|
|
volumeLinear = animalCurve.CalculateValue(scaledScrapeSpeed);
|
|
}
|
|
else
|
|
{
|
|
volumeLinear = m_PedScrapeVolCurve.CalculateValue(scaledScrapeSpeed);
|
|
}
|
|
if(!isPed && otherIsPed)
|
|
{
|
|
volumeLinear *= g_MaterialScrapeAgainstPedVolumeScaling;
|
|
}
|
|
if(wetness > 0.f && !collisionEvent->entities[pedIndex]->GetIsInInterior() && collisionEvent->entities[pedIndex]->GetIsInExterior())
|
|
{
|
|
if(isPed)
|
|
{
|
|
volumeLinear *= wetness;
|
|
}
|
|
else
|
|
{
|
|
volumeLinear *= g_PedWetSurfaceScrapeAtten;
|
|
}
|
|
}
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
else if (AUD_GET_TRISTATE_VALUE(collisionEvent->materialSettings[index]->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_USECUSTOMCURVES) != AUD_TRISTATE_TRUE)
|
|
{
|
|
f32 volumeLinear = m_ScrapeVolCurve.CalculateValue(scaledScrapeSpeed);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
else
|
|
{
|
|
audCurve curve;
|
|
if(curve.Init(materialSettings->ScrapeVolCurve))
|
|
{
|
|
f32 volumeLinear = curve.CalculateValue(scaledScrapeSpeed);
|
|
volume = audDriverUtil::ComputeDbVolumeFromLinear(volumeLinear);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pitch = 0;
|
|
volume = -100.f;
|
|
}
|
|
}
|
|
|
|
audCollisionEvent *audCollisionAudioEntity::GetRollCollisionEventFromHistory(CEntity* entityA, CEntity * entityB, u16 * outIndex)
|
|
{
|
|
for(u32 eventIndex=0; eventIndex<g_MaxCollisionEventHistory; eventIndex++)
|
|
{
|
|
const bool isAllocated = m_CollisionEventHistoryAllocation.IsSet(eventIndex);
|
|
if(isAllocated)
|
|
{
|
|
audCollisionEvent & collisionEvent = m_CollisionEventHistory[eventIndex];
|
|
|
|
bool foundEntityA = false, foundEntityB = false;
|
|
|
|
if(!entityA || (collisionEvent.entities[0]== entityA || collisionEvent.entities[1] == entityA))
|
|
{
|
|
foundEntityA = true;
|
|
}
|
|
if(!entityB || (collisionEvent.entities[0]== entityB || collisionEvent.entities[1] == entityB))
|
|
{
|
|
foundEntityB = true;
|
|
}
|
|
|
|
if(foundEntityA && foundEntityB)
|
|
{
|
|
*outIndex = (u16)eventIndex;
|
|
return &collisionEvent;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
audSwingEvent * audCollisionAudioEntity::GetSwingEventFromHistory(CEntity * entity)
|
|
{
|
|
for(int i=0; i<g_MaxPlayerCollisionEventHistory; i++)
|
|
{
|
|
const bool isAllocated = m_SwingEventHistoryAllocation.IsSet(i);
|
|
if(isAllocated)
|
|
{
|
|
audSwingEvent & colEvent = m_SwingEventHistory[i];
|
|
if(entity == colEvent.swingEnt.Get())
|
|
{
|
|
return &colEvent;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
audPlayerCollisionEvent * audCollisionAudioEntity::GetPlayerCollisionEventFromHistory(CEntity * entity)
|
|
{
|
|
for(int i=0; i<g_MaxPlayerCollisionEventHistory; i++)
|
|
{
|
|
const bool isAllocated = m_PlayerCollisionEventHistoryAllocation.IsSet(i);
|
|
if(isAllocated)
|
|
{
|
|
audPlayerCollisionEvent & colEvent = m_PlayerCollisionEventHistory[i];
|
|
if(entity == colEvent.hitEntity.Get())
|
|
{
|
|
return &colEvent;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void audCollisionAudioEntity::HandleEntitySwinging(CEntity * entity)
|
|
{
|
|
if(entity && entity->GetBaseModelInfo() && entity->GetBaseModelInfo()->GetAudioCollisionSettings() && AUD_GET_TRISTATE_VALUE(entity->GetBaseModelInfo()->GetAudioCollisionSettings()->Flags, FLAG_ID_MODELAUDIOCOLLISIONSETTINGS_SWINGINGPROP))
|
|
{
|
|
audSwingEvent * swingEvent = FindOrAllocateSwingEvent(entity);
|
|
if(swingEvent)
|
|
{
|
|
swingEvent->lastImpactTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
swingEvent->swingEnt = entity;
|
|
}
|
|
}
|
|
}
|
|
|
|
audSwingEvent * audCollisionAudioEntity::FindOrAllocateSwingEvent(CEntity * entity)
|
|
{
|
|
audSwingEvent * playerEvent = GetSwingEventFromHistory(entity);
|
|
|
|
if(playerEvent)
|
|
{
|
|
return playerEvent;
|
|
}
|
|
|
|
return GetFreeSwingEventFromHistory();
|
|
|
|
}
|
|
|
|
audPlayerCollisionEvent * audCollisionAudioEntity::FindOrAllocatePlayerCollisionEvent(CEntity * entity)
|
|
{
|
|
audPlayerCollisionEvent * playerEvent = GetPlayerCollisionEventFromHistory(entity);
|
|
|
|
if(playerEvent)
|
|
{
|
|
return playerEvent;
|
|
}
|
|
|
|
return GetFreePlayerCollisionEventFromHistory();
|
|
|
|
}
|
|
|
|
void audCollisionAudioEntity::HandlePlayerEvent(CEntity * entity)
|
|
{
|
|
audPlayerCollisionEvent* playerEvent = FindOrAllocatePlayerCollisionEvent(entity);
|
|
if(playerEvent)
|
|
{
|
|
playerEvent->lastImpactTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
playerEvent->hitEntity = entity;
|
|
}
|
|
}
|
|
|
|
|
|
audCollisionEvent *audCollisionAudioEntity::GetFreeCollisionEventFromHistory(u16 *index)
|
|
{
|
|
audCollisionEvent *event = NULL;
|
|
|
|
const s32 historyIndex = m_CollisionEventFreeList.Allocate();
|
|
if(historyIndex == -1)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
audAssertf(m_CollisionEventHistoryAllocation.IsClear(historyIndex), "Allocation bitset disagrees with freelist, index %p", index);
|
|
const u16 eventIndex = (u16)historyIndex;
|
|
|
|
event = &(m_CollisionEventHistory[eventIndex]);
|
|
m_CollisionEventHistoryAllocation.Set(eventIndex);
|
|
event->historyIndex = eventIndex;
|
|
if(index)
|
|
{
|
|
naAssertf(eventIndex < 0xffff, "Collision history index is out of bounds: reduce g_MaxCollisionEventHistory (!!!!)");
|
|
*index = (u16)eventIndex;
|
|
}
|
|
|
|
return event;
|
|
}
|
|
|
|
audSwingEvent *audCollisionAudioEntity::GetFreeSwingEventFromHistory(u16 *index)
|
|
{
|
|
audSwingEvent *event = NULL;
|
|
|
|
const s32 historyIndex = m_SwingEventFreeList.Allocate();
|
|
if(historyIndex == -1)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
audAssertf(m_SwingEventHistoryAllocation.IsClear(historyIndex), "Allocation bitset disagrees with freelist, index %p", index);
|
|
const u16 eventIndex = (u16)historyIndex;
|
|
|
|
event = &(m_SwingEventHistory[eventIndex]);
|
|
event->Init();
|
|
m_SwingEventHistoryAllocation.Set(eventIndex);
|
|
event->historyIndex = eventIndex;
|
|
if(index)
|
|
{
|
|
naAssertf(eventIndex < 0xffff, "Collision history index is out of bounds: reduce g_MaxPlayerCollisionEventHistory (!!!!)");
|
|
*index = (u16)eventIndex;
|
|
}
|
|
|
|
return event;
|
|
}
|
|
|
|
audPlayerCollisionEvent *audCollisionAudioEntity::GetFreePlayerCollisionEventFromHistory(u16 *index)
|
|
{
|
|
audPlayerCollisionEvent *event = NULL;
|
|
|
|
const s32 historyIndex = m_PlayerCollisionEventFreeList.Allocate();
|
|
if(historyIndex == -1)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
audAssertf(m_PlayerCollisionEventHistoryAllocation.IsClear(historyIndex), "Allocation bitset disagrees with freelist, index %p", index);
|
|
const u16 eventIndex = (u16)historyIndex;
|
|
|
|
event = &(m_PlayerCollisionEventHistory[eventIndex]);
|
|
m_PlayerCollisionEventHistoryAllocation.Set(eventIndex);
|
|
event->historyIndex = eventIndex;
|
|
if(index)
|
|
{
|
|
naAssertf(eventIndex < 0xffff, "Collision history index is out of bounds: reduce g_MaxPlayerCollisionEventHistory (!!!!)");
|
|
*index = (u16)eventIndex;
|
|
}
|
|
|
|
return event;
|
|
}
|
|
|
|
void audCollisionAudioEntity::TriggerDeferredBulletImpact(const u32 reqHash, const Vector3 &pos, f32 volume, f32 frequencyScaling,bool playerShot,bool isRicochets, CEntity * entity, const phMaterialMgr::Id matID, CAmmoInfo::SpecialType ammoType, bool isHeadShot, u32 slowMoSoundHash)
|
|
{
|
|
u32 hash = reqHash;
|
|
if(hash == ATSTRINGHASH("VEHICLE_BULLET_IMPACT_MULTI", 0x95862DA3))
|
|
{
|
|
if(playerShot && g_FrontendAudioEntity.GetSpecialAbilityMode() == audFrontendAudioEntity::kSpecialAbilityModeFranklin)
|
|
{
|
|
hash = ATSTRINGHASH("FRANKLIN_BULLET_VEHICLE_MASTER", 0x961AC398);
|
|
}
|
|
else if(ammoType == CAmmoInfo::FMJ)
|
|
{
|
|
audSoundSet specialImpactSoundset;
|
|
|
|
if(specialImpactSoundset.Init(ATSTRINGHASH("SPECIAL_AMMO_TYPE_BULLET_IMPACTS", 0x5F8529E3)))
|
|
{
|
|
const audSoundSetFieldRef soundRef = audSoundSetFieldRef(specialImpactSoundset, ATSTRINGHASH("VEHICLE_IMPACT_FMJ", 0x884237D8));
|
|
const audSoundSetFieldRef slowMoSoundRef = audSoundSetFieldRef(specialImpactSoundset, ATSTRINGHASH("VEHICLE_IMPACT_FMJ_SLOW_MO", 0x5D0000DD));
|
|
TriggerDeferredBulletImpact(soundRef, pos, volume, frequencyScaling, playerShot, isRicochets, entity, matID, ammoType, isHeadShot, slowMoSoundRef);
|
|
}
|
|
}
|
|
}
|
|
else if(hash == ATSTRINGHASH("GLASS_BULLET_STRIKE_MASTER", 0xAA251F8E))
|
|
{
|
|
if(ammoType == CAmmoInfo::FMJ)
|
|
{
|
|
audSoundSet specialImpactSoundset;
|
|
|
|
if(specialImpactSoundset.Init(ATSTRINGHASH("SPECIAL_AMMO_TYPE_BULLET_IMPACTS", 0x5F8529E3)))
|
|
{
|
|
const audSoundSetFieldRef soundRef = audSoundSetFieldRef(specialImpactSoundset, ATSTRINGHASH("VEHICLE_GLASS_IMPACT_FMJ", 0x9DF7FD80));
|
|
const audSoundSetFieldRef slowMoSoundRef = audSoundSetFieldRef(specialImpactSoundset, ATSTRINGHASH("VEHICLE_GLASS_IMPACT_FMJ_SLOW_MO", 0x2CFE212A));
|
|
TriggerDeferredBulletImpact(soundRef, pos, volume, frequencyScaling, playerShot, isRicochets, entity, matID, ammoType, isHeadShot, slowMoSoundRef);
|
|
}
|
|
}
|
|
}
|
|
|
|
const audSoundSetFieldRef soundRef = hash;
|
|
const audSoundSetFieldRef slowMoSoundRef = slowMoSoundHash;
|
|
TriggerDeferredBulletImpact(soundRef, pos, volume, frequencyScaling, playerShot, isRicochets, entity, matID, ammoType, isHeadShot, slowMoSoundRef);
|
|
}
|
|
|
|
void audCollisionAudioEntity::TriggerDeferredBulletImpact(const audSoundSetFieldRef soundRef, const Vector3 &pos, f32 volume, f32 UNUSED_PARAM(frequencyScaling),
|
|
bool playerShot,bool isRicochets, CEntity * entity, const phMaterialMgr::Id matID, CAmmoInfo::SpecialType ammoType, bool isHeadShot, audSoundSetFieldRef slowMoSoundRef)
|
|
{
|
|
if(!m_CachedBulletImpacts.IsFull())
|
|
{
|
|
// B*5073502 - Ignore bullet impacts when we're in an online cutscene
|
|
if (NetworkInterface::IsGameInProgress() && audNorthAudioEngine::IsCutsceneActive())
|
|
{
|
|
return;
|
|
}
|
|
|
|
audBulletImpact &impact = m_CachedBulletImpacts.Append();
|
|
impact.soundRef = soundRef;
|
|
impact.slowMotionSoundRef = slowMoSoundRef;
|
|
impact.pos = pos;
|
|
impact.volume = volume;
|
|
impact.entity = entity;
|
|
impact.matID = matID;
|
|
impact.ammoType = ammoType;
|
|
|
|
f32 distanceToListener = g_AudioEngine.GetEnvironment().ComputeSqdDistanceToPanningListener(VECTOR3_TO_VEC3V(pos));
|
|
|
|
if(playerShot && isRicochets)
|
|
{
|
|
impact.rollOffScale = sm_PlayerRicochetsRollOffScaleFactor;
|
|
impact.predelay = (u16)sm_DistanceToRicochetsPredelay.CalculateValue(distanceToListener);
|
|
}
|
|
else
|
|
{
|
|
impact.predelay = (u16)sm_DistanceToBIPredelay.CalculateValue(distanceToListener);
|
|
if(playerShot)
|
|
{
|
|
impact.rollOffScale = sm_PlayerBIRollOffScaleFactor;
|
|
if(isHeadShot)
|
|
{
|
|
impact.predelay = (u16)sm_DistanceToHeadShotPredelay.CalculateValue(distanceToListener);
|
|
impact.rollOffScale = sm_PlayerHeadShotRollOffScaleFactor;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
impact.rollOffScale = 1.f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::TriggerDeferredMeleeImpact(const WorldProbe::CShapeTestHitPoint* pResult, const f32 damageDone, f32 impulseMagnitudeDamping, const bool isBullet,bool playerShot, bool isFootstep, CEntity * hittingEntity, bool isPunch, bool isKick)
|
|
{
|
|
if(m_CachedMeleeImpacts.IsFull())
|
|
{
|
|
naErrorf("Cached melee impacts array is full");
|
|
return;
|
|
}
|
|
|
|
CEntity *entity = NULL;
|
|
const phInst *instance = pResult->GetHitInst();
|
|
if(instance)
|
|
{
|
|
entity = CPhysics::GetEntityFromInst((phInst *)instance);
|
|
if(entity == NULL)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
HandleEntitySwinging(entity);
|
|
|
|
audCollisionEvent &collisionEvent = m_CachedMeleeImpacts.Append();
|
|
|
|
collisionEvent.SetFlag(AUD_COLLISION_FLAG_MELEE);
|
|
if(isFootstep)
|
|
{
|
|
collisionEvent.SetFlag(AUD_COLLISION_FLAG_FOOTSTEP);
|
|
}
|
|
if(isBullet)
|
|
{
|
|
collisionEvent.SetFlag(AUD_COLLISION_FLAG_BULLET);
|
|
}
|
|
if(isPunch)
|
|
{
|
|
collisionEvent.SetFlag(AUD_COLLISION_FLAG_PUNCH);
|
|
}
|
|
else if(isKick)
|
|
{
|
|
collisionEvent.SetFlag(AUD_COLLISION_FLAG_KICK);
|
|
}
|
|
|
|
collisionEvent.entities[0] = entity;
|
|
collisionEvent.entities[1] = hittingEntity;
|
|
|
|
if(playerShot)
|
|
{
|
|
collisionEvent.rollOffScale = sm_PlayerBIRollOffScaleFactor;
|
|
}
|
|
if(isBullet)
|
|
{
|
|
f32 distanceToListener = g_AudioEngine.GetEnvironment().ComputeSqdDistanceToPanningListener(VECTOR3_TO_VEC3V(pResult->GetHitPosition()));
|
|
collisionEvent.predelay = (u16)sm_DistanceToBIPredelay.CalculateValue(distanceToListener);
|
|
}
|
|
|
|
collisionEvent.type = AUD_COLLISION_TYPE_IMPACT;
|
|
|
|
PopulateAudioCollisionEventFromIntersection(&collisionEvent, pResult);
|
|
|
|
//Apply a bit of a hack to ensure that we hit vehicles hard enough to be consistent with other entity types.
|
|
f32 maxImpulseMag = 100.f;
|
|
Assert(entity);
|
|
if(!isBullet)
|
|
{
|
|
if(entity->GetType() == ENTITY_TYPE_VEHICLE)
|
|
{
|
|
maxImpulseMag = g_MaxImpulseMagnitudeForMeleeDamageDoneVehicle;
|
|
}
|
|
else
|
|
{
|
|
maxImpulseMag = g_MaxImpulseMagnitudeForMeleeDamageDoneGeneral;
|
|
}
|
|
}
|
|
|
|
if(isFootstep)
|
|
{
|
|
collisionEvent.impulseMagnitudes[0] = maxImpulseMag * m_MeleeFootstepImpactScalingCurve.CalculateValue(
|
|
damageDone / g_MaxMeleeDamageDoneForCollision);
|
|
}
|
|
else
|
|
{
|
|
collisionEvent.impulseMagnitudes[0] = impulseMagnitudeDamping * maxImpulseMag * m_MeleeDamageImpactScalingCurve.CalculateValue(
|
|
damageDone / g_MaxMeleeDamageDoneForCollision);
|
|
}
|
|
|
|
}
|
|
|
|
void audCollisionAudioEntity::TriggerDeferedImpacts()
|
|
{
|
|
if(g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
for(s32 i = 0; i < m_CachedBulletImpacts.GetCount(); i++)
|
|
{
|
|
audSoundInitParams initParams;
|
|
|
|
initParams.Predelay = (u32)m_CachedBulletImpacts[i].predelay;
|
|
naAssert(m_CachedBulletImpacts[i].rollOffScale != 0.f);
|
|
initParams.VolumeCurveScale = m_CachedBulletImpacts[i].rollOffScale;
|
|
initParams.Position = m_CachedBulletImpacts[i].pos;
|
|
initParams.EnvironmentGroup = CreateBulletImpactEnvironmentGroup(VECTOR3_TO_VEC3V(m_CachedBulletImpacts[i].pos), m_CachedBulletImpacts[i].entity, m_CachedBulletImpacts[i].matID);
|
|
|
|
f32 fFatalShotVolumeBoost = 0.0f;
|
|
if(m_CachedBulletImpacts[i].entity && m_CachedBulletImpacts[i].entity->GetIsTypePed())
|
|
{
|
|
CEntity* entity = m_CachedBulletImpacts[i].entity.Get();
|
|
CPed* ped = static_cast<CPed*>(entity);
|
|
if(ped->IsFatallyInjured() && ped->IsPlayer())
|
|
{
|
|
fFatalShotVolumeBoost = 20.0f;
|
|
initParams.Predelay = 0;
|
|
CreateAndPlaySound(m_CachedBulletImpacts[i].slowMotionSoundRef.GetMetadataRef(), &initParams);
|
|
}
|
|
}
|
|
|
|
initParams.Volume = m_CachedBulletImpacts[i].volume + fFatalShotVolumeBoost;
|
|
|
|
CreateAndPlaySound(m_CachedBulletImpacts[i].soundRef.GetMetadataRef(), &initParams);
|
|
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::ShouldRecord())
|
|
{
|
|
audSoundSetFieldRef resolvedSlowMoSoundRef = m_CachedBulletImpacts[i].slowMotionSoundRef != g_NullMaterialRef? m_CachedBulletImpacts[i].slowMotionSoundRef : g_NullSoundHash;
|
|
CReplayMgr::RecordFx<CPacketBulletImpactNew>(CPacketBulletImpactNew(m_CachedBulletImpacts[i].soundRef, resolvedSlowMoSoundRef, initParams.Position, initParams.Volume));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
m_CachedBulletImpacts.Reset();
|
|
|
|
for(s32 i = 0; i < m_CachedMeleeImpacts.GetCount(); i++)
|
|
{
|
|
ProcessImpactSounds(&m_CachedMeleeImpacts[i]);
|
|
m_CachedMeleeImpacts[i].Init();
|
|
}
|
|
m_CachedMeleeImpacts.Reset();
|
|
|
|
TriggerDeferredFragmentBreaks();
|
|
TriggerDeferredUproots();
|
|
}
|
|
|
|
void audCollisionAudioEntity::ReportBulletHit(u32 weaponAudioHash, const WorldProbe::CShapeTestHitPoint* pResult, const Vector3 &vWeaponPosition, WeaponSettings * weaponSettings, CAmmoInfo::SpecialType ammoType, bool playerShot, bool bulletHitPlayer, bool isAutomatic, bool isShotgun, f32 timeBetweenShots)
|
|
{
|
|
#if __BANK
|
|
if(g_DisableCollisionAudio)
|
|
return;
|
|
#endif
|
|
if(!pResult)
|
|
{
|
|
return;
|
|
}
|
|
if(weaponSettings)
|
|
{
|
|
if(g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) < weaponSettings->LastBulletImpactTime + weaponSettings->BulletImpactTimeFilter)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool dontPlayBI = g_ScriptAudioEntity.ShouldDuckForScriptedConversation();
|
|
dontPlayBI = dontPlayBI && !playerShot && !bulletHitPlayer;
|
|
|
|
dontPlayBI = dontPlayBI && sm_DialogueSuppressBI;
|
|
|
|
if(dontPlayBI)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(weaponSettings)
|
|
{
|
|
weaponSettings->LastBulletImpactTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
}
|
|
|
|
CollisionMaterialSettings *materialSettings = NULL;
|
|
|
|
Vector3 vBulletPath = pResult->GetHitPosition() - vWeaponPosition;
|
|
Vector3 vCameraPosition = VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetPanningListenerPosition());
|
|
f32 cameraDistanceFromGun = (vCameraPosition-vWeaponPosition).Mag();
|
|
f32 cameraDistanceFromImpact = (vCameraPosition- pResult->GetHitPosition()).Mag();
|
|
f32 impactCloserDistance = cameraDistanceFromGun - cameraDistanceFromImpact;
|
|
impactCloserDistance = Clamp(impactCloserDistance, g_MinBulletImpactDampingDistance, g_MaxBulletImpactDampingDistance);
|
|
f32 distanceRange = Max(1.0f, g_MaxBulletImpactDampingDistance-g_MinBulletImpactDampingDistance);
|
|
f32 impactCloserRatio = (impactCloserDistance - g_MinBulletImpactDampingDistance) / distanceRange;
|
|
f32 impactCloserAttenuation = g_MaxBulletImpactCloserAttenuation * impactCloserRatio;
|
|
impactCloserRatio = Clamp(impactCloserRatio, 0.0f, 1.0f);
|
|
f32 impactCloserImpulseDamping = 1.0f-impactCloserRatio + g_MaxBulletImpactCloserImpulseDamping * impactCloserRatio;
|
|
|
|
CEntity* pHitEntity = CPhysics::GetEntityFromInst(const_cast<phInst *>(pResult->GetHitInst()));
|
|
|
|
bool doesMeleeImpacts = weaponSettings && AUD_GET_TRISTATE_VALUE(weaponSettings->Flags, FLAG_ID_WEAPONSETTINGS_DOESMELEEIMPACTS) == AUD_TRISTATE_TRUE;
|
|
|
|
if(weaponSettings == audNorthAudioEngine::GetObject<WeaponSettings>(ATSTRINGHASH("W_SPL_SNOWBALL_PLAYER", 0xC622EAD0)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(pHitEntity && pHitEntity->GetType() == ENTITY_TYPE_PED && !doesMeleeImpacts)
|
|
{
|
|
bool isHeadshot = false;
|
|
CPed *pHitPed = (CPed*)pHitEntity;
|
|
naAssertf(pHitPed, "Casting a CEntity to a CPed has somehow made it null!");
|
|
if(pHitPed->GetRagdollInst() && pResult->GetHitComponent() < pHitPed->GetRagdollInst()->GetTypePhysics()->GetNumChildren())
|
|
{
|
|
s32 boneIndex = pHitPed->GetRagdollInst()->GetType()->GetBoneIndexFromID(
|
|
pHitPed->GetRagdollInst()->GetTypePhysics()->GetAllChildren()[pResult->GetHitComponent()]->GetBoneID());
|
|
s32 headBoneIndex = pHitPed->GetBoneIndexFromBoneTag(BONETAG_HEAD);
|
|
if( headBoneIndex != BONETAG_INVALID )
|
|
{
|
|
isHeadshot = (boneIndex == headBoneIndex);
|
|
// only play the headshot sound if the ped is alive
|
|
isHeadshot = (isHeadshot && !pHitPed->IsDead());
|
|
}
|
|
}
|
|
|
|
audSoundSetFieldRef soundRef;
|
|
audSoundSetFieldRef slowMoSoundRef;
|
|
|
|
// Overrides for special ammo types
|
|
if(ammoType != CAmmoInfo::SpecialType::None)
|
|
{
|
|
audSoundSet specialImpactSoundset;
|
|
|
|
if(specialImpactSoundset.Init(ATSTRINGHASH("SPECIAL_AMMO_TYPE_BULLET_IMPACTS", 0x5F8529E3)))
|
|
{
|
|
switch(ammoType)
|
|
{
|
|
case CAmmoInfo::ArmorPiercing:
|
|
soundRef.Set(specialImpactSoundset, ATSTRINGHASH("PED_IMPACT_ARMOUR_PIERCING", 0xE7C5BED6));
|
|
slowMoSoundRef.Set(specialImpactSoundset, ATSTRINGHASH("PED_IMPACT_ARMOUR_PIERCING_SLOW_MO", 0x2B62268D));
|
|
break;
|
|
|
|
case CAmmoInfo::HollowPoint:
|
|
soundRef.Set(specialImpactSoundset, ATSTRINGHASH("PED_IMPACT_HOLLOW_POINT", 0x89F23D95));
|
|
slowMoSoundRef.Set(specialImpactSoundset, ATSTRINGHASH("PED_IMPACT_HOLLOW_POINT_SLOW_MO", 0xC3AC5A50));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!soundRef.IsValid())
|
|
{
|
|
u32 hitComponent = pResult->GetHitComponent();
|
|
if(hitComponent >= RAGDOLL_SPINE0)
|
|
{
|
|
audSoundSet bulletSounds;
|
|
audPedAudioEntity * audioEnt = pHitPed->GetPedAudioEntity();
|
|
if(audioEnt)
|
|
{
|
|
const ClothAudioSettings * clothSettings = audioEnt->GetFootStepAudio().GetCachedUpperBodyClothSounds();
|
|
if(clothSettings)
|
|
{
|
|
bulletSounds.Init(clothSettings->BulletImpacts);
|
|
if(isHeadshot)
|
|
{
|
|
soundRef.Set(bulletSounds, ATSTRINGHASH("head", 0x84533F51));
|
|
slowMoSoundRef.Set(bulletSounds, ATSTRINGHASH("headSlowMo", 0x19DAA68C));
|
|
}
|
|
else if(hitComponent >= RAGDOLL_UPPER_ARM_LEFT && hitComponent <= RAGDOLL_HAND_RIGHT)
|
|
{
|
|
soundRef.Set(bulletSounds, ATSTRINGHASH("arms", 0x903C14B));
|
|
slowMoSoundRef.Set(bulletSounds, ATSTRINGHASH("armsSlowMo", 0x67DED9B5));
|
|
}
|
|
else
|
|
{
|
|
soundRef.Set(bulletSounds, ATSTRINGHASH("torso", 0x7EA8C1D8));
|
|
slowMoSoundRef.Set(bulletSounds, ATSTRINGHASH("torsoSlowMo", 0x436BF408));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
audSoundSet bulletSounds;
|
|
audPedAudioEntity * audioEnt = pHitPed->GetPedAudioEntity();
|
|
if(audioEnt)
|
|
{
|
|
const ClothAudioSettings * clothSettings = audioEnt->GetFootStepAudio().GetCachedLowerBodyClothSounds();
|
|
if(clothSettings)
|
|
{
|
|
bulletSounds.Init(clothSettings->BulletImpacts);
|
|
soundRef.Set(bulletSounds, ATSTRINGHASH("legs", 0x8650F673));
|
|
slowMoSoundRef.Set(bulletSounds, ATSTRINGHASH("legsSlowMo", 0x91D3A75E));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(weaponSettings && weaponSettings->HitPedSound != g_NullSoundHash)
|
|
{
|
|
audSoundInitParams initParams;
|
|
initParams.Position = pResult->GetHitPosition();
|
|
if(g_FrontendAudioEntity.GetSpecialAbilityMode() != audFrontendAudioEntity::kSpecialAbilityModeTrevor)
|
|
{
|
|
CreateDeferredSound(weaponSettings->HitPedSound, pHitEntity, &initParams, true);
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(weaponSettings->HitPedSound, &initParams, pHitEntity, NULL, eCollisionAudioEntity));
|
|
}
|
|
}
|
|
|
|
#if __BANK
|
|
if(audNorthAudioEngine::IsForcingSlowMoVideoEditor() && slowMoSoundRef != g_NullSoundRef)
|
|
{
|
|
soundRef = slowMoSoundRef;
|
|
}
|
|
#endif
|
|
|
|
pHitPed->GetPedAudioEntity()->UpBodyfallForShooting();
|
|
PF_INCREMENT(audCollisionAudioEntity_Bullets);
|
|
TriggerDeferredBulletImpact(soundRef, pResult->GetHitPosition(), impactCloserAttenuation, 1.f, playerShot, false, pHitEntity, pResult->GetHitMaterialId(), ammoType, isHeadshot REPLAY_ONLY(,slowMoSoundRef));
|
|
|
|
if(audNorthAudioEngine::ShouldTriggerPulseHeadset() && pHitPed->IsLocalPlayer())
|
|
{
|
|
Vec3V gunToBulletNrm = Normalize(RCC_VEC3V(vBulletPath));
|
|
const Mat34V &listener = g_AudioEngine.GetEnvironment().GetPanningListenerMatrix(0);
|
|
float bulletListenerDotProd = Dot(gunToBulletNrm, listener.GetCol1()).Getf();
|
|
const float angle = AcosfSafe(bulletListenerDotProd);
|
|
const float degrees = RtoD*angle;
|
|
float actualDegrees;
|
|
if(IsLessThanOrEqual(Dot(gunToBulletNrm, listener.GetCol0()), ScalarV(V_ZERO)).Getb())
|
|
{
|
|
actualDegrees = 360.0f - degrees;
|
|
}
|
|
else
|
|
{
|
|
actualDegrees = degrees;
|
|
}
|
|
|
|
audSoundInitParams initParams;
|
|
initParams.Pan = (s16)actualDegrees;
|
|
audDisplayf("Triggering pulse bullet impact: %f", actualDegrees);
|
|
g_FrontendAudioEntity.CreateAndPlaySound(g_FrontendAudioEntity.GetPulseHeadsetSounds().Find(ATSTRINGHASH("BulletImpact", 0xDD9382DE)), &initParams);
|
|
}
|
|
|
|
#if GTA_REPLAY
|
|
s32 mati = 0;
|
|
if(naVerifyf(PGTAMATERIALMGR->UnpackMtlId(pResult->GetHitMaterialId())<(phMaterialMgr::Id)g_audCollisionMaterials.GetCount(), "Unpacked mtl id too large"))
|
|
{
|
|
if(naVerifyf(PGTAMATERIALMGR->UnpackMtlId(pResult->GetHitMaterialId())<(phMaterialMgr::Id)0xffff, "Unpacked mtl id too big for s32 cast"))
|
|
{
|
|
mati = static_cast<s32>(PGTAMATERIALMGR->UnpackMtlId(pResult->GetHitMaterialId()));
|
|
}
|
|
}
|
|
|
|
materialSettings = g_audCollisionMaterials[mati];
|
|
|
|
if(materialSettings && pHitEntity)
|
|
{
|
|
u32 slowMoPresuckSound = materialSettings->SlowMoBulletImpactPreSuck;
|
|
u32 slowMoPresuckTime = materialSettings->SlowMoBulletImpactPreSuckTime;
|
|
|
|
|
|
if(slowMoPresuckSound != g_NullSoundHash)
|
|
{
|
|
CReplayMgr::ReplayPresuckSound(slowMoPresuckSound, slowMoPresuckTime, 0u, pHitEntity);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
HandleEntitySwinging(pHitEntity);
|
|
|
|
//Find the material settings from base material/prop overrides
|
|
s32 mati = 0;
|
|
if(naVerifyf(PGTAMATERIALMGR->UnpackMtlId(pResult->GetHitMaterialId())<(phMaterialMgr::Id)g_audCollisionMaterials.GetCount(), "Unpacked mtl id too large"))
|
|
{
|
|
if(naVerifyf(PGTAMATERIALMGR->UnpackMtlId(pResult->GetHitMaterialId())<(phMaterialMgr::Id)0xffff, "Unpacked mtl id too big for s32 cast"))
|
|
{
|
|
mati = static_cast<s32>(PGTAMATERIALMGR->UnpackMtlId(pResult->GetHitMaterialId()));
|
|
}
|
|
}
|
|
if(pHitEntity && pHitEntity->GetType() == ENTITY_TYPE_VEHICLE)
|
|
{
|
|
if(PGTAMATERIALMGR->GetIsGlass(PGTAMATERIALMGR->GetMtlVfxGroup(pResult->GetHitMaterialId())) && ammoType != CAmmoInfo::FMJ)
|
|
{
|
|
return;
|
|
}
|
|
if(mati == m_MaterialIdCarBody)
|
|
{
|
|
materialSettings = ((CVehicle *)pHitEntity)->GetVehicleAudioEntity()->GetCollisionAudio().GetVehicleCollisionMaterialSettings();
|
|
}
|
|
}
|
|
if (!materialSettings)
|
|
{
|
|
|
|
materialSettings = g_audCollisionMaterials[mati];
|
|
materialSettings = GetMaterialOverride(pHitEntity, materialSettings, pResult->GetHitComponent());
|
|
}
|
|
#if __BANK
|
|
if(g_PrintBulletImpactMaterial && materialSettings)
|
|
{
|
|
naDisplayf("map material: %s, component: %u", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(materialSettings->NameTableOffset), pResult->GetHitComponent());
|
|
}
|
|
else if(g_PrintBulletImpactMaterial)
|
|
{
|
|
naDisplayf("Bullet impact material missing audio material settings: %s", PGTAMATERIALMGR->GetMaterial(pResult->GetHitMaterialId()).GetName());
|
|
}
|
|
#endif
|
|
|
|
if(materialSettings)
|
|
{
|
|
#if __BANK
|
|
if( audPedFootStepAudio::sm_bMouseFootstepBullet)
|
|
{
|
|
audPedFootStepAudio::PlayDelayDebugFootstepEvent(pResult->GetHitPosition(),pResult->GetHitMaterialId(),pResult->GetHitComponent(),pHitEntity,materialSettings);
|
|
}
|
|
else if( audPedFootStepAudio::sm_bMouseFootstep)
|
|
{
|
|
audPedFootStepAudio::PlayDebugFootstepEvent(pResult->GetHitPosition(),pResult->GetHitMaterialId(),pResult->GetHitComponent(),pHitEntity,materialSettings);
|
|
return;
|
|
}
|
|
#endif
|
|
Vector3 bulletPath = vBulletPath;
|
|
bulletPath.Normalize();
|
|
float fDotP = DotProduct(bulletPath, pResult->GetHitNormal());
|
|
|
|
#if __BANK //Allows us to turn off bullet impact sounds for easy collision testing
|
|
if(!g_MeleeOnlyBulletImpacts && !doesMeleeImpacts)
|
|
#endif
|
|
{
|
|
|
|
if(fDotP < 0.0f)
|
|
{
|
|
// head on shots have a .35 chance of doing a ricochet, fully glancing ones 0.9
|
|
f32 ricochetProbability = (1.f + fDotP) * sm_TimeBetweenShotsToRicoProb.CalculateValue(timeBetweenShots);
|
|
if((AUD_GET_TRISTATE_VALUE(materialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_SHOULDPLAYRICOCHET)==AUD_TRISTATE_TRUE) &&
|
|
(audEngineUtil::ResolveProbability(ricochetProbability) ||
|
|
fwTimer::GetTimeWarp()<1.f))
|
|
{
|
|
if(!SUPERCONDUCTOR.GetGunFightConductor().GetFakeScenesConductor().IsFakingRicochets() BANK_ONLY(&&!g_DontPlayRicochets))
|
|
{
|
|
// trigger a ricochets_in at the bullet impact location
|
|
Vector3 pos = pResult->GetHitPosition();
|
|
PF_INCREMENT(audCollisionAudioEntity_Bullets);
|
|
|
|
if (audWeaponAudioEntity::IsRubberBulletGun(weaponAudioHash) || audWeaponAudioEntity::IsRiotShotgun(weaponAudioHash))
|
|
{
|
|
audSoundSetFieldRef customSoundRef;
|
|
customSoundRef.Set(audWeaponAudioEntity::IsRubberBulletGun(weaponAudioHash) ? ATSTRINGHASH("Rubber_Bullet_Sounds", 0x50AB02CD) : ATSTRINGHASH("Riot_Shotgun_Sounds", 0x992CA88C), ATSTRINGHASH("Rico", 0x8398E83F));
|
|
TriggerDeferredBulletImpact(customSoundRef, pos, impactCloserAttenuation, 1.f, playerShot, true, pHitEntity, pResult->GetHitMaterialId(), CAmmoInfo::SpecialType::None);
|
|
}
|
|
else if (audWeaponAudioEntity::IsRayGun(weaponAudioHash))
|
|
{
|
|
TriggerDeferredBulletImpact(ATSTRINGHASH("Laser_Bullets_Rico_Master", 0x57DC02A9), pos, impactCloserAttenuation, 1.f, playerShot, true, pHitEntity, pResult->GetHitMaterialId(), CAmmoInfo::SpecialType::None);
|
|
}
|
|
else
|
|
{
|
|
TriggerDeferredBulletImpact(ATSTRINGHASH("RICOCHETS_IN", 0x649832C8), pos, impactCloserAttenuation, 1.f, playerShot, true, pHitEntity, pResult->GetHitMaterialId(), CAmmoInfo::SpecialType::None);
|
|
|
|
Vector3 pos2 = bulletPath;
|
|
pos2.ReflectAbout(pResult->GetHitNormal());
|
|
pos2.Scale(g_BulletRicochetPanDistance);
|
|
pos2.Add(pResult->GetHitPosition());
|
|
PF_INCREMENT(audCollisionAudioEntity_Bullets);
|
|
TriggerDeferredBulletImpact(ATSTRINGHASH("RICOCHETS_OUT", 0x431F9671), pos2, impactCloserAttenuation, 1.f, playerShot, true, pHitEntity, pResult->GetHitMaterialId(), CAmmoInfo::SpecialType::None);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pHitEntity && pHitEntity->GetIsTypeVehicle() && ((CVehicle*)pHitEntity)->InheritsFromHeli() && cameraDistanceFromImpact > 30.f)
|
|
{
|
|
materialSettings = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_CAR_METAL", 0xA0231769));
|
|
}
|
|
|
|
PF_INCREMENT(audCollisionAudioEntity_Bullets);
|
|
|
|
bool useAutomatic = isAutomatic && !playerShot;
|
|
#if __BANK
|
|
if(sm_AutomaticImpactOnlyOnVeh)
|
|
{
|
|
useAutomatic = useAutomatic && (pHitEntity && pHitEntity->GetIsTypeVehicle());
|
|
}
|
|
#endif
|
|
useAutomatic = useAutomatic && (cameraDistanceFromImpact <= sm_DistanceToTriggerAutomaticBI);
|
|
useAutomatic = useAutomatic && (sm_TimeSinceLastAutomaticBulletHit >= sm_DtToTriggerAutomaticBI);
|
|
|
|
bool useShotgun = isShotgun;
|
|
|
|
#if __BANK
|
|
if(sm_ShotgunDisabledForPlayer)
|
|
{
|
|
useShotgun = useShotgun && !playerShot;
|
|
}
|
|
if(sm_ShotgunImpactOnlyOnVeh)
|
|
{
|
|
useShotgun = useShotgun && (pHitEntity && pHitEntity->GetIsTypeVehicle());
|
|
}
|
|
#endif
|
|
|
|
useShotgun = useShotgun && (cameraDistanceFromImpact <= sm_DistanceToTriggerShotgunBI);
|
|
useShotgun = useShotgun && (sm_TimeSinceLastShotgunBulletHit >= sm_DtToTriggerShotgunBI);
|
|
|
|
u32 bulletImpactSound = useAutomatic ? materialSettings->AutomaticBulletImpactSound : materialSettings->BulletImpactSound;
|
|
bulletImpactSound = useShotgun ? materialSettings->ShotgunBulletImpactSound : bulletImpactSound;
|
|
|
|
u32 slowMoBulletImpactSound = useAutomatic ? materialSettings->SlowMoAutomaticBulletImpactSound : materialSettings->SlowMoBulletImpactSound;
|
|
slowMoBulletImpactSound = useShotgun ? materialSettings->SlowMoShotgunBulletImpactSound : slowMoBulletImpactSound;
|
|
|
|
u32 slowMoPresuckSound = useAutomatic ? materialSettings->SlowMoAutomaticBulletImpactPreSuck: materialSettings->SlowMoBulletImpactPreSuck;
|
|
slowMoPresuckSound = useShotgun ? materialSettings->SlowMoShotgunBulletImpactPreSuck : slowMoPresuckSound;
|
|
|
|
u32 slowMoPresuckTime = useAutomatic ? materialSettings->SlowMoAutomaticBulletImpactPreSuckTime: materialSettings->SlowMoBulletImpactPreSuckTime;
|
|
slowMoPresuckTime = useShotgun ? materialSettings->SlowMoShotgunBulletImpactPreSuckTime : slowMoPresuckTime;
|
|
|
|
if (bulletImpactSound == g_NullSoundHash)
|
|
{
|
|
bulletImpactSound = materialSettings->BulletImpactSound;
|
|
slowMoBulletImpactSound = materialSettings->SlowMoBulletImpactSound;
|
|
slowMoPresuckSound = materialSettings->SlowMoBulletImpactPreSuck;
|
|
slowMoPresuckTime = materialSettings->SlowMoBulletImpactPreSuckTime;
|
|
useAutomatic = false;
|
|
useShotgun = false;
|
|
}
|
|
|
|
#if __BANK
|
|
if(audNorthAudioEngine::IsForcingSlowMoVideoEditor() && slowMoBulletImpactSound != g_NullSoundHash)
|
|
{
|
|
bulletImpactSound = slowMoBulletImpactSound;
|
|
}
|
|
#endif
|
|
|
|
if (audWeaponAudioEntity::IsRubberBulletGun(weaponAudioHash) || audWeaponAudioEntity::IsRiotShotgun(weaponAudioHash))
|
|
{
|
|
audSoundSet impactSoundSet;
|
|
|
|
if (impactSoundSet.Init(audWeaponAudioEntity::IsRubberBulletGun(weaponAudioHash) ? ATSTRINGHASH("Rubber_Bullet_Sounds", 0x50AB02CD) : ATSTRINGHASH("Riot_Shotgun_Sounds", 0x992CA88C)))
|
|
{
|
|
audSoundSetFieldRef customSoundRef;
|
|
|
|
if (pHitEntity && pHitEntity->GetIsTypeVehicle())
|
|
{
|
|
customSoundRef.Set(impactSoundSet, ATSTRINGHASH("Impact_Vehicle", 0xD6E48783));
|
|
}
|
|
else if (pHitEntity && pHitEntity->GetIsTypePed())
|
|
{
|
|
customSoundRef.Set(impactSoundSet, ATSTRINGHASH("Impact_Ped", 0x335EAE84));
|
|
}
|
|
else if (materialSettings->HardImpact != g_NullSoundHash)
|
|
{
|
|
customSoundRef.Set(impactSoundSet, ATSTRINGHASH("Impact_Hard", 0xE8CC836B));
|
|
}
|
|
else
|
|
{
|
|
customSoundRef.Set(impactSoundSet, ATSTRINGHASH("Impact_Soft", 0xB4DAE554));
|
|
}
|
|
|
|
TriggerDeferredBulletImpact(customSoundRef, pResult->GetHitPosition(), impactCloserAttenuation, 1.f, playerShot, false, pHitEntity, pResult->GetHitMaterialId(), ammoType, false REPLAY_ONLY(, slowMoBulletImpactSound));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//if we want the killshot gunfire to be different we need to play another sound here
|
|
TriggerDeferredBulletImpact(bulletImpactSound, pResult->GetHitPosition(), impactCloserAttenuation, 1.f, playerShot, false, pHitEntity, pResult->GetHitMaterialId(), ammoType, false REPLAY_ONLY(, slowMoBulletImpactSound));
|
|
|
|
if (audWeaponAudioEntity::IsRayGun(weaponAudioHash) && cameraDistanceFromImpact < sm_DistanceToTriggerRaygunBI)
|
|
{
|
|
TriggerDeferredBulletImpact(ATSTRINGHASH("Laser_Bullets_Impact_Master", 0xF438CF8F), pResult->GetHitPosition(), impactCloserAttenuation, 1.f, playerShot, false, pHitEntity, pResult->GetHitMaterialId(), ammoType, false REPLAY_ONLY(, slowMoBulletImpactSound));
|
|
}
|
|
}
|
|
|
|
#if GTA_REPLAY
|
|
if(slowMoPresuckSound != g_NullSoundHash)
|
|
{
|
|
CReplayMgr::ReplayPresuckSound(slowMoPresuckSound, slowMoPresuckTime, 0u, pHitEntity);
|
|
}
|
|
#endif
|
|
|
|
if(useAutomatic)
|
|
{
|
|
sm_TimeSinceLastAutomaticBulletHit = 0;
|
|
}
|
|
if(useShotgun)
|
|
{
|
|
sm_TimeSinceLastShotgunBulletHit = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// also trigger a melee impact
|
|
if(materialSettings->BulletCollisionScaling > 0)
|
|
{
|
|
//f32 meleeImpactScale = s_MeleeImpactScaleFactor * (materialSettings->BulletCollisionScaling*0.01f);
|
|
f32 meleeImpactScale = materialSettings->MaxImpulseMag * (materialSettings->BulletCollisionScaling*0.01f);
|
|
TriggerDeferredMeleeImpact(pResult, meleeImpactScale, impactCloserImpulseDamping, true,playerShot);
|
|
}
|
|
// In case the material plays ricochet, send a message to the conductors in order to play it.
|
|
if(!doesMeleeImpacts && playerShot && (AUD_GET_TRISTATE_VALUE(materialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_SHOULDPLAYRICOCHET)==AUD_TRISTATE_TRUE) )
|
|
{
|
|
ConductorMessageData messageData;
|
|
messageData.conductorName = GunfightConductor;
|
|
messageData.message = FakeRicochet;
|
|
messageData.entity = pHitEntity;
|
|
naAssertf( FPIsFinite(pResult->GetHitPosition().x) && FPIsFinite(pResult->GetHitPosition().y) && FPIsFinite(pResult->GetHitPosition().z),
|
|
"Bad hit position sent to the conductors.");
|
|
messageData.vExtraInfo = pResult->GetHitPosition();
|
|
SUPERCONDUCTOR.SendConductorMessage(messageData);
|
|
}
|
|
if(!doesMeleeImpacts && pHitEntity)
|
|
{
|
|
ConductorMessageData messageData;
|
|
messageData.conductorName = GunfightConductor;
|
|
messageData.entity = pHitEntity;
|
|
naAssertf( FPIsFinite(pResult->GetHitPosition().x) && FPIsFinite(pResult->GetHitPosition().y) && FPIsFinite(pResult->GetHitPosition().z),
|
|
"Bad hit position sent to the conductors.");
|
|
messageData.vExtraInfo = pResult->GetHitPosition();
|
|
CVehicle *vehicle = NULL;
|
|
CPed *player = NULL;
|
|
switch (pHitEntity->GetType())
|
|
{
|
|
case ENTITY_TYPE_BUILDING:
|
|
case ENTITY_TYPE_ANIMATED_BUILDING:
|
|
// First of all check if the material is flagged to play debris
|
|
if(AUD_GET_TRISTATE_VALUE(materialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_GENERATEPOSTSHOOTOUTDEBRIS)==AUD_TRISTATE_TRUE)
|
|
{
|
|
player = CGameWorld::FindLocalPlayer();
|
|
if(player)
|
|
{
|
|
if(cameraDistanceFromImpact <= SUPERCONDUCTOR.GetGunFightConductor().GetFakeScenesConductor().GetBuildingDistThresholdToPlayPostShootOut() || playerShot)
|
|
{
|
|
Vector3 groundNormal = player->GetGroundNormal();
|
|
f32 dot = fabs(groundNormal.Dot(pResult->GetHitNormal()));
|
|
if(dot <= 0.3f)
|
|
{
|
|
|
|
messageData.message = BuildingDebris;
|
|
SUPERCONDUCTOR.SendConductorMessage(messageData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ENTITY_TYPE_VEHICLE:
|
|
vehicle = static_cast<CVehicle*>(pHitEntity);
|
|
if(vehicle && vehicle->GetVehicleAudioEntity())
|
|
{
|
|
vehicle->GetVehicleAudioEntity()->UpdateBulletHits();
|
|
messageData.message = VehicleDebris;
|
|
SUPERCONDUCTOR.SendConductorMessage(messageData);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
naWarningf("Couldn't find audio material settings for bullet impact: %s", PGTAMATERIALMGR->GetMaterial(pResult->GetHitMaterialId()).GetName());
|
|
}
|
|
}
|
|
sm_TimeSinceLastBulletHit = 0;
|
|
}
|
|
|
|
naEnvironmentGroup* audCollisionAudioEntity::CreateBulletImpactEnvironmentGroup(const Vec3V& position, CEntity* entity, const phMaterialMgr::Id matID)
|
|
{
|
|
naEnvironmentGroup* environmentGroup = NULL;
|
|
|
|
// Try grabbing an existing group or creating one off a CObject which will use portal-occlusion, otherwise just create a probe-based group
|
|
environmentGroup = GetOcclusionGroup(position, entity, matID);
|
|
if(!environmentGroup)
|
|
{
|
|
environmentGroup = naEnvironmentGroup::Allocate("BulletImpact");
|
|
if(environmentGroup)
|
|
{
|
|
// SourceEnvironmentUpdateTime of 0 since we don't want to be doing lots of requests for all our groups
|
|
environmentGroup->Init(NULL, 20, 0);
|
|
environmentGroup->SetSource(position);
|
|
environmentGroup->SetUseCloseProximityGroupsForUpdates(true);
|
|
environmentGroup->SetUsePortalOcclusion(false);
|
|
}
|
|
}
|
|
|
|
return environmentGroup;
|
|
}
|
|
|
|
naEnvironmentGroup* audCollisionAudioEntity::GetOcclusionGroup(const audCollisionEvent *collisionEvent)
|
|
{
|
|
// First try and grab an already existing environmentGroup ( if it's a ped or vehicle )
|
|
// occlusion groups don't have size or update rates any more, so just return one if either collision entity's got one.
|
|
int index1 = 0, index2 = 1;
|
|
|
|
//First person mode hack to make sure that collisions involving the player always get put in the player mixgroup. In the future perhaps this could be for all modes?
|
|
if(collisionEvent->GetFlag(AUD_COLLISION_FLAG_PLAYER) && (!collisionEvent->entities[0] || !collisionEvent->entities[0]->GetIsTypePed() || !((CPed*)collisionEvent->entities[0].Get())->IsLocalPlayer()))
|
|
{
|
|
bool isFirstPerson = audNorthAudioEngine::IsAFirstPersonCameraActive(CGameWorld::FindLocalPlayer(), false);
|
|
if(isFirstPerson)
|
|
{
|
|
index1 = 1;
|
|
index2 = 0;
|
|
}
|
|
}
|
|
naEnvironmentGroup* environmentGroup = NULL;
|
|
environmentGroup = (naEnvironmentGroup*)(collisionEvent->entities[index1]?collisionEvent->entities[index1]->GetAudioEnvironmentGroup():NULL);
|
|
if(environmentGroup)
|
|
{
|
|
return environmentGroup;
|
|
}
|
|
environmentGroup = (naEnvironmentGroup*)(collisionEvent->entities[index2]?collisionEvent->entities[index2]->GetAudioEnvironmentGroup():NULL);
|
|
if(environmentGroup)
|
|
{
|
|
return environmentGroup;
|
|
}
|
|
|
|
// We don't already have one so create one
|
|
environmentGroup = GetOcclusionGroup(VECTOR3_TO_VEC3V(collisionEvent->positions[0]), collisionEvent->entities[0], collisionEvent->materialIds[0]);
|
|
if(environmentGroup)
|
|
{
|
|
return environmentGroup;
|
|
}
|
|
environmentGroup = GetOcclusionGroup(VECTOR3_TO_VEC3V(collisionEvent->positions[1]), collisionEvent->entities[1], collisionEvent->materialIds[1]);
|
|
if(environmentGroup)
|
|
{
|
|
return environmentGroup;
|
|
}
|
|
|
|
return environmentGroup;
|
|
}
|
|
|
|
naEnvironmentGroup* audCollisionAudioEntity::GetOcclusionGroup(const Vec3V& position, CEntity *entity, const phMaterialMgr::Id matID)
|
|
{
|
|
// Get the most appropriate occlusion group for this entity - if at all possible, don't create a new one, piggyback on the ones we've already got.
|
|
naEnvironmentGroup* environmentGroup = NULL;
|
|
|
|
if(entity)
|
|
{
|
|
switch(entity->GetType())
|
|
{
|
|
case ENTITY_TYPE_PED:
|
|
environmentGroup = (naEnvironmentGroup*)(((CPed *)entity)->GetPedAudioEntity()->GetEnvironmentGroup(true));
|
|
break;
|
|
|
|
case ENTITY_TYPE_VEHICLE:
|
|
environmentGroup = (naEnvironmentGroup*)(((CVehicle*)entity)->GetVehicleAudioEntity()->GetEnvironmentGroup());
|
|
break;
|
|
|
|
case ENTITY_TYPE_OBJECT:
|
|
{
|
|
environmentGroup = naEnvironmentGroup::Allocate("CollisionObj");
|
|
if(environmentGroup)
|
|
{
|
|
// SourceEnvironmentUpdateTime of 0 since we don't want to be doing lots of requests for all our groups, and in interiors we get that when SetInteriorInfoWithInteriorLocation()
|
|
environmentGroup->Init(NULL, 20, 0);
|
|
environmentGroup->SetSource(position);
|
|
environmentGroup->SetInteriorInfoWithEntity(entity);
|
|
environmentGroup->SetUsePortalOcclusion(true);
|
|
environmentGroup->SetMaxPathDepth(2);
|
|
environmentGroup->SetUseCloseProximityGroupsForUpdates(true);
|
|
}
|
|
break;
|
|
}
|
|
case ENTITY_TYPE_MLO:
|
|
{
|
|
const s32 roomIdx = PGTAMATERIALMGR->UnpackRoomId(matID);
|
|
if(roomIdx != 0)
|
|
{
|
|
const CInteriorProxy* intProxy = CInteriorProxy::GetInteriorProxyFromCollisionData(entity, NULL);
|
|
if(intProxy)
|
|
{
|
|
environmentGroup = naEnvironmentGroup::Allocate("CollisionMLO");
|
|
if(environmentGroup)
|
|
{
|
|
const fwInteriorLocation interiorLocation = CInteriorProxy::CreateLocation(intProxy, roomIdx);
|
|
|
|
// SourceEnvironmentUpdateTime of 0 since we don't want to be doing lots of requests for all our groups, and in interiors we get that when SetInteriorInfoWithInteriorLocation()
|
|
environmentGroup->Init(NULL, 20, 0);
|
|
environmentGroup->SetSource(position);
|
|
environmentGroup->SetInteriorInfoWithInteriorLocation(interiorLocation);
|
|
environmentGroup->SetUsePortalOcclusion(true);
|
|
environmentGroup->SetMaxPathDepth(2);
|
|
environmentGroup->SetUseCloseProximityGroupsForUpdates(true);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return environmentGroup;
|
|
}
|
|
|
|
bool audCollisionAudioEntity::ProcessImpactSounds(audCollisionEvent *collisionEvent, bool forceSoftImpact)
|
|
{
|
|
Assert(collisionEvent);
|
|
|
|
if((collisionEvent->entities[0] && collisionEvent->entities[0]->GetIsTypePed() && ((CPed*)collisionEvent->entities[0].Get())->IsLocalPlayer())
|
|
|| (collisionEvent->entities[1] && collisionEvent->entities[1]->GetIsTypePed() && ((CPed*)collisionEvent->entities[1].Get())->IsLocalPlayer()))
|
|
{
|
|
collisionEvent->SetFlag(AUD_COLLISION_FLAG_PLAYER);
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->ClearFlag(AUD_COLLISION_FLAG_PLAYER);
|
|
}
|
|
|
|
audEnvironmentGroupInterface *occlusionGroup = GetOcclusionGroup(collisionEvent);
|
|
s32 numSoundsPlayed = 0;
|
|
|
|
bool isInWater = false;
|
|
|
|
// ped -> door collisions are special cased
|
|
u32 doorIndex = ~0U;
|
|
if(collisionEvent->entities[0] && collisionEvent->entities[1])
|
|
{
|
|
if(collisionEvent->entities[0]->GetType() == ENTITY_TYPE_OBJECT && !collisionEvent->entities[0]->IsBaseFlagSet(fwEntity::IS_FIXED) &&
|
|
((CObject*)(collisionEvent->entities[0].Get()))->IsADoor() &&
|
|
(((CDoor*)(collisionEvent->entities[0].Get()))->IsCreakingDoorType()))
|
|
{
|
|
if(collisionEvent->entities[1]->GetType() == ENTITY_TYPE_PED)
|
|
{
|
|
doorIndex = 0;
|
|
}
|
|
}
|
|
else if(collisionEvent->entities[1]->GetType() == ENTITY_TYPE_OBJECT && !collisionEvent->entities[1]->IsBaseFlagSet(fwEntity::IS_FIXED) &&
|
|
((CObject*)(collisionEvent->entities[1].Get()))->IsADoor() &&
|
|
((((CDoor*)collisionEvent->entities[1].Get()))->IsCreakingDoorType()))
|
|
{
|
|
if(collisionEvent->entities[0]->GetType() == ENTITY_TYPE_PED)
|
|
{
|
|
doorIndex = 1;
|
|
}
|
|
}
|
|
|
|
if(doorIndex != ~0U)
|
|
{
|
|
// we have a ped/door collision:
|
|
// these are now handled int the door audio entity
|
|
}
|
|
}
|
|
|
|
if(collisionEvent->entities[0] && collisionEvent->entities[0]->GetIsTypeObject())
|
|
{
|
|
// ignore weapon collisions
|
|
if (collisionEvent->entities[0]->GetIsTypeObject())
|
|
{
|
|
CObject* object = static_cast<CObject*>(collisionEvent->entities[0].Get());
|
|
if (object->GetWeapon())
|
|
{
|
|
if(!object->GetAsProjectile() && !object->GetWeapon()->GetAudioComponent().GetWasDropped())
|
|
{
|
|
return false;
|
|
}
|
|
object->GetWeapon()->GetAudioComponent().SetWasDropped(false);
|
|
if(fwTimer::GetTimeInMilliseconds() - object->GetWeapon()->GetAudioComponent().GetLastImpactTime() < g_WeaponImpactFilterTime)
|
|
{
|
|
return false;
|
|
}
|
|
object->GetWeapon()->GetAudioComponent().SetLastImpactTime();
|
|
}
|
|
}
|
|
}
|
|
if(collisionEvent->entities[1] && collisionEvent->entities[1]->GetIsTypeObject())
|
|
{
|
|
// ignore weapon collisions
|
|
if (collisionEvent->entities[1]->GetIsTypeObject())
|
|
{
|
|
CObject* object = static_cast<CObject*>(collisionEvent->entities[1].Get());
|
|
if (object->GetWeapon())
|
|
{
|
|
if(!object->GetAsProjectile() && !object->GetWeapon()->GetAudioComponent().GetWasDropped())
|
|
{
|
|
return false;
|
|
}
|
|
object->GetWeapon()->GetAudioComponent().SetWasDropped(false);
|
|
if(fwTimer::GetTimeInMilliseconds() - object->GetWeapon()->GetAudioComponent().GetLastImpactTime() < g_WeaponImpactFilterTime)
|
|
{
|
|
return false;
|
|
}
|
|
object->GetWeapon()->GetAudioComponent().SetLastImpactTime();
|
|
}
|
|
if(object->GetIsInWater())
|
|
{
|
|
isInWater = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
for(s32 i=0; i<2; i++)
|
|
{
|
|
const audCategory* categoryToUse = NULL;
|
|
|
|
CollisionMaterialSettings *materialSettings = collisionEvent->materialSettings[i];
|
|
CollisionMaterialSettings *otherMaterialSettings = collisionEvent->materialSettings[(i + 1)&1];
|
|
|
|
CEntity *entity = collisionEvent->entities[i];
|
|
CEntity *otherEntity = collisionEvent->entities[(i+1)&1];
|
|
|
|
bool isPed = entity && entity->GetIsTypePed();
|
|
bool otherIsPed = otherEntity && otherEntity->GetIsTypePed();
|
|
s16 requestedPitch = 0;
|
|
|
|
CPed *ped = NULL;
|
|
|
|
if(isPed)
|
|
{
|
|
ped = (CPed *)entity;
|
|
}
|
|
|
|
if(materialSettings == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//If the materials are the same, only play one collision (TODO:maybe we want to take account of weights too)
|
|
if(numSoundsPlayed > 0 && materialSettings == otherMaterialSettings)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Ignore collisions below the impulse mag threshold
|
|
if(collisionEvent->impulseMagnitudes[i] < materialSettings->MinImpulseMag)
|
|
{
|
|
#if __BANK
|
|
if(g_AudioDebugEntity == entity && (g_DebugAudioImpacts || g_DebugAudioRolls || g_DebugAudioScrapes))
|
|
{
|
|
naDisplayf("[%u:%p] Ignoring impact due to impulse magnitude below min thresh: %f ", fwTimer::GetFrameCount(), collisionEvent, collisionEvent->impulseMagnitudes[i]);
|
|
}
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
|
|
// trigger ped impact sounds before earlying out due to silent impact since the ped stuff is free to scale impulses as it likes
|
|
if(ped)
|
|
{
|
|
if(ped->GetPedAudioEntity()->GetIsInWater())
|
|
{
|
|
ped->GetPedAudioEntity()->GetFootStepAudio().TriggerFallInWaterSplash(true);
|
|
continue;
|
|
}
|
|
|
|
if(ped->GetPedType() != PEDTYPE_ANIMAL)
|
|
{
|
|
s32 otherIndex = (i+1)&1;
|
|
//naErrorf("Ped collision, material %s, impulseMag %f, component %d, time %d", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(collisionEvent->materialSettings[otherIndex]->NameTableOffset), collisionEvent->impulseMagnitudes[i], collisionEvent->components[i], fwTimer::GetTimeInMilliseconds());
|
|
if(!ped->GetUsingRagdoll())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ped->GetPedAudioEntity()->TriggerImpactSounds(collisionEvent->positions[i],
|
|
collisionEvent->materialSettings[otherIndex],
|
|
collisionEvent->entities[otherIndex],
|
|
collisionEvent->impulseMagnitudes[i],
|
|
collisionEvent->components[i],
|
|
collisionEvent->components[otherIndex],
|
|
collisionEvent->GetFlag(AUD_COLLISION_FLAG_MELEE));
|
|
if(ped->GetPedAudioEntity()->ShouldUpBodyfallSoundsFromShooting())
|
|
{
|
|
categoryToUse = g_UppedCollisionCategory;
|
|
}
|
|
}
|
|
|
|
|
|
if(collisionEvent->GetFlag(AUD_COLLISION_FLAG_MELEE) && collisionEvent->entities[1] && collisionEvent->entities[1]->GetType() == ENTITY_TYPE_PED)
|
|
{
|
|
CPed *hittingPed = (CPed*)(collisionEvent->entities[1].Get());
|
|
CPedModelInfo *pedModelInfo = (CPedModelInfo*)hittingPed->GetBaseModelInfo();
|
|
if(pedModelInfo)
|
|
{
|
|
if(BANK_ONLY(g_PedsHitLikeGirls || )!hittingPed->IsMale())
|
|
{
|
|
requestedPitch = 600;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if( doorIndex != ~0U && !collisionEvent->GetFlag(AUD_COLLISION_FLAG_MELEE))
|
|
{
|
|
break;
|
|
}
|
|
f32 volume;
|
|
u32 startOffsetPerc;
|
|
|
|
ComputeStartOffsetAndVolumeFromImpulseMagnitude(collisionEvent, i, startOffsetPerc, volume, isPed);
|
|
|
|
if(volume <= g_SilenceVolume || startOffsetPerc >= 100)
|
|
{
|
|
#if __BANK
|
|
if(g_AudioDebugEntity == collisionEvent->entities[i] && (g_DebugAudioImpacts || g_DebugAudioRolls || g_DebugAudioScrapes))
|
|
{
|
|
naDisplayf("[%u:%p] Ignoring silent impact: vel: %f vol: %f", fwTimer::GetFrameCount(), collisionEvent, collisionEvent->impulseMagnitudes[i], volume);
|
|
}
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
if (materialSettings->CollisionCount >= materialSettings->CollisionCountThreshold)
|
|
{
|
|
if(volume > materialSettings->VolumeThreshold)
|
|
{
|
|
materialSettings->VolumeThreshold = volume;
|
|
}
|
|
else
|
|
{
|
|
//naErrorf("Culling collision for %s at volume %f, collision count %u", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(materialSettings->NameTableOffset), volume, materialSettings->CollisionCount);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//naErrorf("Impact on material %s, collision count %u", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(materialSettings->NameTableOffset), materialSettings->CollisionCount);
|
|
|
|
//If a ped is involved in the collision, let the ped code work out what wants to be played
|
|
if(( collisionEvent->entities[0] && collisionEvent->entities[0]->GetIsTypePed() ) && ( collisionEvent->entities[1] && collisionEvent->entities[1]->GetIsTypePed()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(collisionEvent->entities[0] && collisionEvent->entities[i]->GetIsTypeVehicle() && ((CVehicle*)collisionEvent->entities[i].Get())->GetVehicleAudioEntity() && !collisionEvent->GetFlag(AUD_COLLISION_FLAG_BULLET) && !collisionEvent->GetFlag(AUD_COLLISION_FLAG_MELEE))
|
|
{
|
|
CVehicle* vehicle = (CVehicle*)collisionEvent->entities[i].Get();
|
|
|
|
if(vehicle && !(vehicle->GetVehicleAudioEntity()->GetDrowningFactor() >= 1.f && !Water::IsCameraUnderwater()))
|
|
{
|
|
f32 wreckedVehicleImpactScale = g_WreckedVehicleImpactScale;
|
|
f32 wreckedVehicleRolloffScale = g_WreckedVehicleRolloffScale;
|
|
vehicle->GetVehicleAudioEntity()->GetCollisionAudio().Fakeimpact(collisionEvent->impulseMagnitudes[i]*wreckedVehicleImpactScale, wreckedVehicleRolloffScale, true);
|
|
}
|
|
}
|
|
|
|
#if __BANK
|
|
DebugAudioImpact(collisionEvent->positions[0], collisionEvent->components[0], collisionEvent->components[1], collisionEvent->impulseMagnitudes[0], (audCollisionType)collisionEvent->type);
|
|
#endif // #if __BANK
|
|
|
|
u32 hardImpact = materialSettings->HardImpact;
|
|
u32 hardImpactSlowMo = materialSettings->SlowMoHardImpact;
|
|
|
|
u32 solidImpact = materialSettings->SolidImpact;
|
|
u32 softImpact = materialSettings->SoftImpact;
|
|
|
|
u32 otherHardImpact = otherMaterialSettings? otherMaterialSettings->HardImpact: materialSettings->HardImpact;
|
|
u32 otherHardImpactSlowMo = otherMaterialSettings? otherMaterialSettings->SlowMoHardImpact: materialSettings->SlowMoHardImpact;
|
|
u32 otherSolidImpact = otherMaterialSettings? otherMaterialSettings->SolidImpact: materialSettings->SolidImpact;
|
|
|
|
//naErrorf("Playing impact sound from material %s volume %f", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(collisionEvent->materialSettings[i]->NameTableOffset), volume);
|
|
|
|
bool pedsMakeSolidCollisions = g_PedsMakeSolidCollisions || AUD_GET_TRISTATE_VALUE(materialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_PEDSARESOLID) == AUD_TRISTATE_TRUE;
|
|
|
|
if(hardImpact == g_NullSoundHash || (isPed && !collisionEvent->GetFlag(AUD_COLLISION_FLAG_MELEE)))
|
|
{
|
|
hardImpact = 0;
|
|
hardImpactSlowMo = g_NullSoundHash;
|
|
}
|
|
if(solidImpact == g_NullSoundHash)
|
|
{
|
|
solidImpact = 0;
|
|
}
|
|
if(otherHardImpact == g_NullSoundHash || (otherIsPed && !collisionEvent->GetFlag(AUD_COLLISION_FLAG_MELEE)))
|
|
{
|
|
otherHardImpact = 0;
|
|
otherHardImpactSlowMo = g_NullSoundHash;
|
|
}
|
|
if(otherSolidImpact == g_NullSoundHash && ((!otherIsPed || !pedsMakeSolidCollisions) && !collisionEvent->GetFlag(AUD_COLLISION_FLAG_MELEE)))
|
|
{
|
|
otherSolidImpact = 0;
|
|
}
|
|
|
|
if(otherMaterialSettings && AUD_GET_TRISTATE_VALUE(otherMaterialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_ISSOFTFORLIGHTPROPS) == AUD_TRISTATE_TRUE &&
|
|
GetAudioWeight(collisionEvent->entities[i]) <= AUDIO_WEIGHT_L)
|
|
{
|
|
forceSoftImpact = true;
|
|
}
|
|
|
|
if(forceSoftImpact)
|
|
{
|
|
otherHardImpact = 0;
|
|
otherHardImpactSlowMo = g_NullSoundHash;
|
|
otherSolidImpact = 0;
|
|
}
|
|
|
|
if(collisionEvent->GetFlag(AUD_COLLISION_FLAG_PUNCH) || collisionEvent->GetFlag(AUD_COLLISION_FLAG_KICK))
|
|
{
|
|
if(AUD_GET_TRISTATE_VALUE(materialSettings->Flags, FLAG_ID_COLLISIONMATERIALSETTINGS_PEDSARESOLID) != AUD_TRISTATE_TRUE)
|
|
{
|
|
otherHardImpact = 0;
|
|
otherHardImpactSlowMo = g_NullSoundHash;
|
|
otherSolidImpact = 0;
|
|
if(collisionEvent->GetFlag(AUD_COLLISION_FLAG_PUNCH))
|
|
{
|
|
softImpact = collisionEvent->materialSettings[0]->PedPunch;
|
|
}
|
|
else //kick
|
|
{
|
|
softImpact = collisionEvent->materialSettings[0]->PedKick;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(collisionEvent->GetFlag(AUD_COLLISION_FLAG_MELEE) && collisionEvent->GetFlag(AUD_COLLISION_FLAG_PLAYER) && collisionEvent->entities[0] && collisionEvent->entities[0]->GetIsTypeVehicle())
|
|
{
|
|
bool isFirstPerson = audNorthAudioEngine::IsAFirstPersonCameraActive(CGameWorld::FindLocalPlayer(), false);
|
|
if(isFirstPerson)
|
|
{
|
|
volume += g_PlayerVehicleMeleeCollisionVolumeScale;
|
|
}
|
|
}
|
|
|
|
audSoundInitParams initParams;
|
|
initParams.EnvironmentGroup = occlusionGroup;
|
|
if(categoryToUse)
|
|
{
|
|
initParams.Category = categoryToUse;
|
|
}
|
|
initParams.StartOffset = startOffsetPerc;
|
|
initParams.IsStartOffsetPercentage = true;
|
|
initParams.Volume = volume;
|
|
|
|
if(collisionEvent->GetFlag(AUD_COLLISION_FLAG_MELEE) && collisionEvent->GetFlag(AUD_COLLISION_FLAG_PLAYER) && !collisionEvent->GetFlag(AUD_COLLISION_FLAG_FOOTSTEP))
|
|
{
|
|
bool isFirstPerson = audNorthAudioEngine::IsAFirstPersonCameraActive(CGameWorld::FindLocalPlayer(), false);
|
|
if(isFirstPerson)
|
|
{
|
|
initParams.Volume += g_PlayerMeleeCollisionVolumeBoost;
|
|
initParams.Category = g_AudioEngine.GetCategoryManager().GetCategoryPtr(ATSTRINGHASH("MELEE", 0x3B8F078A));
|
|
}
|
|
}
|
|
|
|
if(collisionEvent->rollOffScale != 0.f)
|
|
{
|
|
// value of 0 means default
|
|
initParams.VolumeCurveScale = collisionEvent->rollOffScale;
|
|
}
|
|
|
|
/*if(isPed)
|
|
{
|
|
naErrorf("Playing ped impact sounds volume %f, is using upped category: %s, time %d", volume, categoryToUse ? "true" : "false", fwTimer::GetTimeInMilliseconds());
|
|
}*/
|
|
|
|
initParams.Predelay = (u32)collisionEvent->predelay;
|
|
initParams.Position = collisionEvent->positions[i];
|
|
initParams.Pitch = requestedPitch;
|
|
|
|
if(isInWater)
|
|
{
|
|
initParams.LPFCutoff = g_LFPCuttoffInWater;
|
|
initParams.Volume -= g_VolumeReductionInWater;
|
|
}
|
|
|
|
u32 impactHash = 0;
|
|
u32 slowMoImpactHash = g_NullSoundHash;
|
|
|
|
if(otherHardImpact) //Only play hard impacts if both materials are hard
|
|
{
|
|
if(hardImpact)
|
|
{
|
|
impactHash = hardImpact;
|
|
slowMoImpactHash = hardImpactSlowMo;
|
|
}
|
|
else if(solidImpact)
|
|
{
|
|
impactHash = solidImpact;
|
|
}
|
|
}
|
|
else if(solidImpact && otherSolidImpact)
|
|
{
|
|
impactHash = solidImpact;
|
|
}
|
|
|
|
if(impactHash != 0)
|
|
{
|
|
#if __BANK
|
|
if(audNorthAudioEngine::IsForcingSlowMoVideoEditor() && slowMoImpactHash != g_NullSoundHash)
|
|
{
|
|
impactHash = slowMoImpactHash;
|
|
}
|
|
#endif
|
|
|
|
CreateAndPlaySound(impactHash, &initParams);
|
|
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(impactHash, &initParams, NULL, NULL, eCollisionAudioEntity, slowMoImpactHash));
|
|
}
|
|
CreateAndPlaySound(softImpact, &initParams);
|
|
|
|
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(softImpact, &initParams, NULL, NULL, eCollisionAudioEntity));
|
|
|
|
audPlayerCollisionEvent * playerEvent = GetPlayerCollisionEventFromHistory(entity);
|
|
if(playerEvent)
|
|
{
|
|
playerEvent->lastImpactTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
collisionEvent->rollOffScale = sm_PlayerContextCollisionRolloff;
|
|
if(volume > sm_PlayerContextVolumeForBlip)
|
|
{
|
|
playerEvent->blipMap = true;
|
|
playerEvent->blipAmount = audDriverUtil::ComputeLinearVolumeFromDb(volume);
|
|
}
|
|
playerEvent->position = VECTOR3_TO_VEC3V(collisionEvent->positions[i]);
|
|
}
|
|
|
|
numSoundsPlayed++;
|
|
|
|
if(collisionEvent->materialSettings[i] && g_UseCollisionIntensityCulling)
|
|
{
|
|
if(!g_RecentlyCollidedMaterials.IsMemberOfList(collisionEvent->materialSettings[i]))
|
|
{
|
|
g_RecentlyCollidedMaterials.Add(collisionEvent->materialSettings[i]);
|
|
}
|
|
collisionEvent->materialSettings[i]->CollisionCount++;
|
|
}
|
|
|
|
#if __BANK
|
|
PF_INCREMENT(CollisonsProcessed);
|
|
#endif
|
|
|
|
}
|
|
return (numSoundsPlayed > 0);
|
|
}
|
|
|
|
bool audCollisionAudioEntity::ProcessScrapeSounds(audCollisionEvent *collisionEvent)
|
|
{
|
|
if(( collisionEvent->entities[0] && collisionEvent->entities[0]->GetIsTypePed() && (collisionEvent->components[0] == RAGDOLL_FOOT_LEFT || collisionEvent->components[0] == RAGDOLL_FOOT_RIGHT)) || ( collisionEvent->entities[1] && collisionEvent->entities[1]->GetIsTypePed() && (collisionEvent->components[1] == RAGDOLL_FOOT_LEFT || collisionEvent->components[1] == RAGDOLL_FOOT_RIGHT)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// If we're not playing any scrape sounds, kick them off
|
|
if (!collisionEvent->scrapeSounds[0] && !collisionEvent->scrapeSounds[1])
|
|
{
|
|
collisionEvent->SetFlag(AUD_COLLISION_FLAG_PLAY_SCRAPE);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool audCollisionAudioEntity::ProcessRollSounds(audCollisionEvent *collisionEvent)
|
|
{
|
|
//Peds don't roll...unless we want to do bespoke ped stuff in which case this needs to be moved lower down
|
|
if(( collisionEvent->entities[0] && collisionEvent->entities[0]->GetIsTypePed() ) || ( collisionEvent->entities[1] && collisionEvent->entities[1]->GetIsTypePed()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!collisionEvent->rollSounds[0] && !collisionEvent->rollSounds[1])
|
|
{
|
|
collisionEvent->SetFlag(AUD_COLLISION_FLAG_PLAY_ROLL);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool audCollisionAudioEntity::IsScrapeFromMagnitudes(const f32 bangMag, const f32 scrapeMag)
|
|
{
|
|
// find out whether this predominantly a bang or a scrape and re-adjust the data accordingly
|
|
const float bangScrapeBias = 0.f;
|
|
float bangRatio = bangMag / (bangMag+scrapeMag);
|
|
float scrapeRatio = scrapeMag / (bangMag+scrapeMag);
|
|
return scrapeRatio>= (bangRatio+bangScrapeBias);
|
|
}
|
|
|
|
|
|
|
|
bool audCollisionAudioEntity::IsOkToPlayImpacts(phManifold & manifold)
|
|
{
|
|
bool okToPlayImpactSounds = true;
|
|
|
|
//Filter out collisions that happen too frequently (e.g. the jiggling when objects are precariously balanced)
|
|
u32 lastCollision = GetManifoldUserDataTime(manifold);
|
|
u32 currentTime = (u16)(g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0)&0xffff);
|
|
|
|
SetManifoldUserDataTime(manifold, (u16)currentTime);
|
|
if((s32(currentTime) - s32(lastCollision)) < 0)
|
|
{
|
|
currentTime += 0xffff;
|
|
}
|
|
|
|
u32 timeDelta = currentTime-lastCollision;
|
|
if(timeDelta < g_MinTimeBetweenCollisions)
|
|
{
|
|
okToPlayImpactSounds = false;
|
|
}
|
|
|
|
return okToPlayImpactSounds;
|
|
}
|
|
|
|
void audCollisionAudioEntity::UpdateManifold(VfxCollisionInfo_s & vfxColnInfo, phManifold & manifold,bool breakableCollisionA,bool breakableCollisionB)
|
|
{
|
|
#if __BANK
|
|
if(g_DisableCollisionAudio)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
// First of all check if its a glass event and let the glassAudioEntity manage it.
|
|
if(breakableCollisionB)
|
|
{
|
|
g_GlassAudioEntity.PlayShardCollision(vfxColnInfo.pEntityA,VEC3V_TO_VECTOR3(vfxColnInfo.vRelVelocity).Mag(),manifold.GetInstanceB());
|
|
return;
|
|
}
|
|
else if(breakableCollisionA)
|
|
{
|
|
g_GlassAudioEntity.PlayShardCollision(vfxColnInfo.pEntityB,VEC3V_TO_VECTOR3(vfxColnInfo.vRelVelocity).Mag(),manifold.GetInstanceA());
|
|
return;
|
|
}
|
|
|
|
CEntity* pEntityA = vfxColnInfo.pEntityA;
|
|
CEntity* pEntityB = vfxColnInfo.pEntityB;
|
|
|
|
// return if either entity isn't valid
|
|
if (vfxColnInfo.pEntityA==NULL || vfxColnInfo.pEntityB==NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// check that entity a is a physical object (b may or may not be)
|
|
Assertf(vfxColnInfo.pEntityA->GetIsPhysical() || vfxColnInfo.pEntityB->GetIsPhysical(), "Neither entity is physical (A:%s - B:%s)", vfxColnInfo.pEntityA->GetModelName(), vfxColnInfo.pEntityB->GetModelName());
|
|
|
|
// if these entities are attached then ignore them
|
|
if (pEntityA->GetIsPhysical())
|
|
{
|
|
CPhysical* pPhysicalA = static_cast<CPhysical*>(pEntityA);
|
|
if (pPhysicalA->GetAttachParent()==pEntityB)
|
|
{
|
|
return ;
|
|
}
|
|
}
|
|
|
|
if (pEntityB->GetIsPhysical())
|
|
{
|
|
CPhysical* pPhysicalB = static_cast<CPhysical*>(pEntityB);
|
|
if (pPhysicalB->GetAttachParent()==pEntityA)
|
|
{
|
|
return ;
|
|
}
|
|
}
|
|
|
|
// return if we have two invalid colliders
|
|
if (vfxColnInfo.pColliderA==NULL && vfxColnInfo.pColliderB==NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// ignore collisions between different parts of the same ped
|
|
if (pEntityA==pEntityB && pEntityA->GetIsTypePed())
|
|
{
|
|
return ;
|
|
}
|
|
|
|
// find out whether this predominantly a bang or a scrape and re-adjust the data accordingly
|
|
float bangMag = vfxColnInfo.bangMagA;
|
|
float scrapeMag = vfxColnInfo.scrapeMagA;
|
|
|
|
if(bangMag > 1000.f) //Bad physics
|
|
{
|
|
//naErrorf("Crazy impact magnitude: %f", bangMag);
|
|
return;
|
|
}
|
|
|
|
bool isAPedCollision = false, isPedRagdollA=false, isPedRagdollB=false;
|
|
|
|
//We set up the materials early as we need access to them to make decisions about rolling
|
|
phMaterialMgr::Id materialIds[2];
|
|
materialIds[0] = vfxColnInfo.mtlIdA;
|
|
materialIds[1] = vfxColnInfo.mtlIdB;
|
|
|
|
CollisionMaterialSettings * materialSettings[2];
|
|
|
|
for(int i=0; i<2; i++)
|
|
{
|
|
CEntity * entity = (i==0) ? pEntityA:pEntityB;
|
|
u32 component = (i==0) ? vfxColnInfo.componentIdA:vfxColnInfo.componentIdB;
|
|
// extract the material data from the collision and see if we have a model specific override
|
|
phMaterialMgr::Id unpackedMtlIdA = PGTAMATERIALMGR->UnpackMtlId(materialIds[i]);
|
|
materialSettings[i] = GetMaterialOverride(entity, g_audCollisionMaterials[(s32)unpackedMtlIdA], component);
|
|
|
|
}
|
|
|
|
|
|
//Melee collisions are handled seperately
|
|
if(pEntityA->GetIsTypePed())
|
|
{
|
|
isAPedCollision = true;
|
|
CPed* ped = (CPed*)pEntityA;
|
|
if(pEntityB->GetIsTypeVehicle() && (pEntityB == ped->GetVehiclePedEntering() || pEntityB == ped->GetVehiclePedInside()))
|
|
{
|
|
return;
|
|
}
|
|
if(ped->GetPedIntelligence() && ped->GetPedIntelligence()->GetTaskMelee())
|
|
{
|
|
return;
|
|
}
|
|
if(CVfxHelper::GetGameCamWaterDepth()>0.0f)
|
|
{
|
|
return;
|
|
}
|
|
isPedRagdollA = ped->GetUsingRagdoll();
|
|
if(!isPedRagdollA && !pEntityB->GetIsTypePed() && g_PedsOverrideImpactMag)
|
|
{
|
|
bangMag = ped->GetPedAudioEntity()->GetPlayerImpactMagOverride(bangMag, pEntityB, materialSettings[1]);
|
|
}
|
|
if(!isPedRagdollA && pEntityB->GetIsTypeObject() && ((CObject*)pEntityB)->IsADoor())
|
|
{
|
|
if(((CDoor*)pEntityB)->GetDoorAudioEntity())
|
|
{
|
|
((CDoor*)pEntityB)->GetDoorAudioEntity()->TriggerDoorImpact(ped, vfxColnInfo.vWorldPosA);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if(pEntityB->GetIsTypePed())
|
|
{
|
|
isAPedCollision = true;
|
|
CPed* ped = (CPed*)pEntityB;
|
|
if(pEntityA->GetIsTypeVehicle() && (pEntityA == ped->GetVehiclePedEntering() || pEntityA == ped->GetVehiclePedInside()))
|
|
{
|
|
return;
|
|
}
|
|
if(ped->GetPedIntelligence() && ped->GetPedIntelligence()->GetTaskMelee())
|
|
{
|
|
return;
|
|
}
|
|
if(CVfxHelper::GetGameCamWaterDepth()>0.0f)
|
|
{
|
|
return;
|
|
}
|
|
isPedRagdollB = ped->GetUsingRagdoll();
|
|
if(!isPedRagdollB && ! pEntityA->GetIsTypePed() && g_PedsOverrideImpactMag)
|
|
{
|
|
bangMag = ped->GetPedAudioEntity()->GetPlayerImpactMagOverride(bangMag, pEntityB, materialSettings[0]);
|
|
}
|
|
if(!isPedRagdollB && pEntityA->GetIsTypeObject() && ((CObject*)pEntityA)->IsADoor())
|
|
{
|
|
if(((CDoor*)pEntityA)->GetDoorAudioEntity())
|
|
{
|
|
((CDoor*)pEntityA)->GetDoorAudioEntity()->TriggerDoorImpact(ped, vfxColnInfo.vWorldPosA);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// store some more useful information
|
|
phInst* pInstA = pEntityA->GetFragInst();
|
|
phInst* pInstB = pEntityB->GetFragInst();
|
|
|
|
if(pInstA == NULL)
|
|
{
|
|
pInstA = pEntityA->GetCurrentPhysicsInst();
|
|
}
|
|
if(pInstB == NULL)
|
|
{
|
|
pInstB = pEntityB->GetCurrentPhysicsInst();
|
|
}
|
|
|
|
// return early if the data is no longer valid
|
|
if (pInstA==NULL || pInstA->IsInLevel()==false ||
|
|
pInstB==NULL || pInstB->IsInLevel()==false)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
//See if we want to pass this contact to the vehicle entity to handle
|
|
bool isVehicleCollisionAndBail = false;
|
|
|
|
if(pEntityA->GetIsTypeVehicle() && !(((CVehicle*)pEntityA)->GetHealth() <= 0.f ))
|
|
{
|
|
if(!((CVehicle*)pEntityA)->GetVehicleAudioEntity()->IsDisabled())
|
|
{
|
|
audVehicleCollisionEvent vehicleCollision(vfxColnInfo, true);
|
|
|
|
((CVehicle*)pEntityA)->GetVehicleAudioEntity()->GetCollisionAudio().ProcessContact(vfxColnInfo, manifold, pEntityB, vehicleCollision);
|
|
isVehicleCollisionAndBail = true;
|
|
|
|
// this allows very heavy objects to bang inside vehicles(crates inside titan in exile1)
|
|
if(GetAudioWeight(pEntityB) == AUDIO_WEIGHT_VH && pEntityB->GetType() == ENTITY_TYPE_OBJECT)
|
|
{
|
|
isVehicleCollisionAndBail = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pEntityB->GetIsTypeVehicle() && !(((CVehicle*)pEntityB)->GetHealth() <= 0.f))
|
|
{
|
|
if(!((CVehicle*)pEntityB)->GetVehicleAudioEntity()->IsDisabled())
|
|
{
|
|
audVehicleCollisionEvent vehicleCollision(vfxColnInfo, false);
|
|
|
|
((CVehicle*)pEntityB)->GetVehicleAudioEntity()->GetCollisionAudio().ProcessContact(vfxColnInfo, manifold, pEntityA, vehicleCollision);
|
|
isVehicleCollisionAndBail = true;
|
|
|
|
// this allows very heavy objects to bang inside vehicles(crates inside titan in exile1)
|
|
if(GetAudioWeight(pEntityA) == AUDIO_WEIGHT_VH && pEntityA->GetType() == ENTITY_TYPE_OBJECT)
|
|
{
|
|
isVehicleCollisionAndBail = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(isVehicleCollisionAndBail)
|
|
{
|
|
return; //Vehicle collisions are handled in the vehicleaudioentity
|
|
}
|
|
|
|
HandleEntitySwinging(pEntityA);
|
|
HandleEntitySwinging(pEntityB);
|
|
|
|
// extract the collison normal information
|
|
const Vec3V normalA = vfxColnInfo.vWorldNormalA;
|
|
// Vector3 normalB = VEC3V_TO_VECTOR3(vfxColnInfo.vWorldNormalB);
|
|
|
|
#if __DEV
|
|
if (g_DebugAudioCollisions && g_AudioDebugEntity && (g_AudioDebugEntity==pEntityA || g_AudioDebugEntity==pEntityB))
|
|
{
|
|
naAssertf(0, "Debugging collisions");
|
|
}
|
|
#endif
|
|
|
|
//Work out the relative linear and angular speeds of the entities involved in the collision
|
|
ScalarV turnSpeed(V_ZERO);
|
|
if(pEntityA->GetIsPhysical())
|
|
{
|
|
const Vec3V turnVector = RCC_VEC3V(((CPhysical *)pEntityA)->GetAngVelocity());
|
|
const ScalarV turnDotNorm = Dot(turnVector,normalA);
|
|
turnSpeed = rage::SqrtSafe(MagSquared(turnVector) - turnDotNorm*turnDotNorm);
|
|
}
|
|
|
|
ScalarV otherTurnSpeed(V_ZERO);
|
|
if(pEntityB->GetIsPhysical())
|
|
{
|
|
const Vec3V turnVector = RCC_VEC3V(((CPhysical *)pEntityB)->GetAngVelocity());
|
|
const ScalarV turnDotNorm = Dot(turnVector,normalA);
|
|
otherTurnSpeed = rage::SqrtSafe(MagSquared(turnVector) - turnDotNorm*turnDotNorm);
|
|
}
|
|
|
|
//Get the component of the velocities involved in the direction of the collision,
|
|
//not sure if this makes complete mathematical sense with regards to the angular
|
|
//velocity but works ok so we'll go with it for now
|
|
const ScalarV relativeMoveSpeed = Abs(Dot(vfxColnInfo.vRelVelocity, vfxColnInfo.vColnDir));//Mag(vfxColnInfo.vRelVelocity).Getf(); //Abs((moveSpeed - otherMoveSpeed).Dot(normalA));
|
|
const ScalarV relativeTurnSpeed = Abs(turnSpeed+otherTurnSpeed); //Abs((turnSpeed - otherTurnSpeed).Mag()); //.Dot(normalA)); //Abs((Dot(VECTOR3_TO_VEC3V(turnSpeed), vfxColnInfo.vColnDir) - Dot(VECTOR3_TO_VEC3V(otherTurnSpeed), vfxColnInfo.vColnDir)).Getf());
|
|
const ScalarV relativeSpeed = Max(Max(relativeMoveSpeed,
|
|
relativeTurnSpeed), ScalarVFromF32(bangMag));
|
|
|
|
//Don't do collision sounds if there is not a big enough velocity involved
|
|
if(IsGreaterThanAll(relativeSpeed,ScalarVFromF32(g_MinSpeedForCollision)))
|
|
{
|
|
float fRelativeTurnSpeed = relativeTurnSpeed.Getf();
|
|
|
|
// extract the collision position
|
|
Vector3 collPosA = VEC3V_TO_VECTOR3(vfxColnInfo.vWorldPosA);
|
|
Vector3 collPosB = VEC3V_TO_VECTOR3(vfxColnInfo.vWorldPosB);
|
|
|
|
if (!IsScrapeFromMagnitudes(bangMag, scrapeMag))
|
|
{
|
|
scrapeMag = 0.0f;
|
|
}
|
|
|
|
//Create a new event and an event pointer in case we want to use one from the history instead
|
|
audCollisionEvent newEvent;
|
|
newEvent.Init();
|
|
audCollisionEvent *collisionEvent = NULL;
|
|
bool hasFoundEvent = false;
|
|
bool shouldFlipEntities = false;
|
|
bool isRolling = false;
|
|
|
|
//See if we have a rolling contact
|
|
if((manifold.IsStaticFriction() && fRelativeTurnSpeed > g_MinRollSpeed && fRelativeTurnSpeed > scrapeMag) || (scrapeMag ==0.f && fRelativeTurnSpeed > 1.f))
|
|
{
|
|
isRolling = true;
|
|
}
|
|
|
|
//We set up the materials early as we need access to them to make decisions about rolling
|
|
phMaterialMgr::Id materialIds[2];
|
|
materialIds[0] = vfxColnInfo.mtlIdA;
|
|
materialIds[1] = vfxColnInfo.mtlIdB;
|
|
|
|
CollisionMaterialSettings * materialSettings[2];
|
|
|
|
for(int i=0; i<2; i++)
|
|
{
|
|
CEntity * entity = (i==0) ? pEntityA:pEntityB;
|
|
u32 component = (i==0) ? vfxColnInfo.componentIdA:vfxColnInfo.componentIdB;
|
|
// extract the material data from the collision and see if we have a model specific override
|
|
phMaterialMgr::Id unpackedMtlIdA = PGTAMATERIALMGR->UnpackMtlId(materialIds[i]);
|
|
materialSettings[i] = GetMaterialOverride(entity, g_audCollisionMaterials[(s32)unpackedMtlIdA], component);
|
|
|
|
}
|
|
|
|
for(int i=0; i<2; i++)
|
|
{
|
|
if((materialSettings[i] && (fRelativeTurnSpeed < materialSettings[i]->MinRollSpeed)))
|
|
{
|
|
isRolling = false;
|
|
}
|
|
}
|
|
|
|
|
|
if(isRolling && isAPedCollision)
|
|
{
|
|
fRelativeTurnSpeed = 0.f;
|
|
isRolling = false;
|
|
}
|
|
|
|
//Rolling and scraping collisions use the history
|
|
if((scrapeMag > 0.f) || isRolling)
|
|
{
|
|
collisionEvent = GetCollisionEventFromHistory(manifold);
|
|
|
|
if(collisionEvent)
|
|
{
|
|
const bool areEntities00Matched = (collisionEvent->entities[0] == pEntityA); // && collisionEvent->components[0] == manifold.GetComponentA());
|
|
const bool areEntities11Matched = (collisionEvent->entities[1] == pEntityB); // && collisionEvent->components[1] == manifold.GetComponentB());
|
|
const bool areEntities01Matched = (collisionEvent->entities[0] == pEntityB); // && collisionEvent->components[0] == manifold.GetComponentB());
|
|
const bool areEntities10Matched = (collisionEvent->entities[1] == pEntityA); // && collisionEvent->components[1] == manifold.GetComponentA());
|
|
|
|
if(areEntities00Matched && areEntities11Matched)
|
|
{
|
|
hasFoundEvent = true;
|
|
}
|
|
else if(areEntities01Matched && areEntities10Matched)
|
|
{
|
|
hasFoundEvent = true;
|
|
shouldFlipEntities = true; //found the event but the entities were the other way around last time so give them a flip
|
|
}
|
|
}
|
|
|
|
if(!hasFoundEvent)
|
|
{
|
|
u16 eventIndex;
|
|
collisionEvent = GetRollCollisionEventFromHistory(pEntityA, pEntityB, &eventIndex);
|
|
if(collisionEvent)
|
|
{
|
|
SetManifoldUserDataIndex(manifold, eventIndex);
|
|
hasFoundEvent = true;
|
|
}
|
|
}
|
|
|
|
if(collisionEvent)
|
|
{
|
|
if((!pEntityA || pEntityA == collisionEvent->entities[1]) && (!pEntityB || pEntityB == collisionEvent->entities[0]))
|
|
{
|
|
shouldFlipEntities = true;
|
|
}
|
|
}
|
|
|
|
if(!hasFoundEvent)
|
|
{
|
|
//naErrorf("New scrape sound time %d on materials %s and %s, entities %p, %p isRoll: %s", audNorthAudioEngine::GetCurrentTimeInMs(), audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(materialSettings[0]->NameTableOffset), audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(materialSettings[1]->NameTableOffset), pEntityA, pEntityB, isRolling?"true":"false");
|
|
|
|
u16 eventIndex;
|
|
//We don't already have an event for collisions between these entities, so create a new one.
|
|
collisionEvent = GetFreeCollisionEventFromHistory(&eventIndex);
|
|
if(collisionEvent == NULL)
|
|
{
|
|
//naWarningf("The audio collision event history is full!");
|
|
return;
|
|
}
|
|
SetManifoldUserDataIndex(manifold, eventIndex);
|
|
|
|
collisionEvent->entities[0] = pEntityA;
|
|
collisionEvent->entities[1] = pEntityB;
|
|
collisionEvent->components[0] = (u8)vfxColnInfo.componentIdA;
|
|
collisionEvent->components[1] = (u8)vfxColnInfo.componentIdB;
|
|
}
|
|
|
|
collisionEvent->positions[0] = collPosA;
|
|
collisionEvent->positions[1] = collPosB;
|
|
|
|
if(isRolling)
|
|
{
|
|
collisionEvent->type = AUD_COLLISION_TYPE_ROLL;
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->type = AUD_COLLISION_TYPE_SCRAPE;
|
|
}
|
|
}
|
|
else //It's not a scrape or a roll so do a one shot collision event without using the history
|
|
{
|
|
if(bangMag < 0.01f)
|
|
{
|
|
return;
|
|
}
|
|
collisionEvent = &newEvent;
|
|
collisionEvent->type = AUD_COLLISION_TYPE_IMPACT;
|
|
|
|
collisionEvent->entities[0] = pEntityA;
|
|
collisionEvent->entities[1] = pEntityB;
|
|
collisionEvent->positions[0] = collPosA;
|
|
collisionEvent->positions[1] = collPosB;
|
|
collisionEvent->components[0] = (u8)vfxColnInfo.componentIdA;
|
|
collisionEvent->components[1] = (u8)vfxColnInfo.componentIdB;
|
|
}
|
|
|
|
|
|
#if __BANK
|
|
if(g_DebugFocusEntity && (pEntityA == g_pFocusEntity || pEntityB == g_pFocusEntity))
|
|
{
|
|
naErrorf("bangMag %f, scrapeMag %f, relativeTurnSpeed %f", bangMag, scrapeMag, fRelativeTurnSpeed);
|
|
}
|
|
#endif
|
|
|
|
bool okToPlayImpactsounds = (collisionEvent->type == AUD_COLLISION_TYPE_IMPACT) ? IsOkToPlayImpacts(manifold) : false;
|
|
|
|
collisionEvent->lastImpactTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
|
|
|
|
//TODO: choose between relativeMoveSpeed and bangMag
|
|
if(PopulateAudioCollisionEventFromImpact(collisionEvent, vfxColnInfo, bangMag, scrapeMag, fRelativeTurnSpeed, shouldFlipEntities))
|
|
{
|
|
audSoundInitParams initParams; //for small scrape/roll impacts
|
|
|
|
switch(collisionEvent->type)
|
|
{
|
|
case AUD_COLLISION_TYPE_IMPACT:
|
|
if(okToPlayImpactsounds)
|
|
{
|
|
ProcessImpactSounds(collisionEvent);
|
|
}
|
|
break;
|
|
|
|
case AUD_COLLISION_TYPE_SCRAPE:
|
|
|
|
if(isAPedCollision)
|
|
{
|
|
for(int i=0; i<2; i++)
|
|
{
|
|
if(collisionEvent->entities[i] && collisionEvent->entities[i]->GetIsTypePed())
|
|
{
|
|
audPedAudioEntity * pedAudio = ((CPed*)collisionEvent->entities[i].Get())->GetPedAudioEntity();
|
|
if(g_DoPedScrapeStartImpacts && (g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) - pedAudio->GetLastScrapeTime()) > g_PedScrapeStartImpactTime)
|
|
{
|
|
float volume= 0.f;
|
|
s32 pitch = 0;
|
|
ComputePitchAndVolumeFromScrapeSpeed(collisionEvent, 0, pitch, volume);
|
|
pedAudio->TriggerScrapeStartImpact(volume);
|
|
okToPlayImpactsounds = false;
|
|
}
|
|
pedAudio->SetLastScrapeTime();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if(ShouldPlayScrapeImpact(collisionEvent, bangMag) && IsOkToPlayImpacts(manifold)) //Play big collisions in addition to scraping
|
|
{
|
|
ProcessImpactSounds(collisionEvent);
|
|
}
|
|
|
|
if(!isAPedCollision || isPedRagdollA || isPedRagdollB)
|
|
{
|
|
ProcessScrapeSounds(collisionEvent);
|
|
|
|
collisionEvent->scrapeStopTimeMs = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) + g_ScrapingCollisionHoldTimeMs;
|
|
}
|
|
|
|
break;
|
|
case AUD_COLLISION_TYPE_ROLL:
|
|
collisionEvent->rollUpdatedThisFrame = 1;
|
|
if(ShouldPlayRollImpact(collisionEvent, bangMag) && IsOkToPlayImpacts(manifold)) //Play big collisions in addition to rolling
|
|
{
|
|
ProcessImpactSounds(collisionEvent);
|
|
}
|
|
|
|
ProcessRollSounds(collisionEvent);
|
|
|
|
collisionEvent->rollStopTimeMs = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) + g_RollingCollisionHoldTimeMs;
|
|
break;
|
|
default:
|
|
naAssertf(0, "Unrecognized collision type: %u", collisionEvent->type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool audCollisionAudioEntity::CanPlayRollSounds(const audCollisionEvent * colEvent)
|
|
{
|
|
return (!colEvent->materialSettings[0] || colEvent->materialSettings[0]->MinRollSpeed < colEvent->rollMagnitudes[0]) &&
|
|
(!colEvent->materialSettings[1] || colEvent->materialSettings[1]->MinRollSpeed < colEvent->rollMagnitudes[1]);
|
|
}
|
|
|
|
bool audCollisionAudioEntity::ShouldPlayRollImpact(const audCollisionEvent * colEvt, float bangMag)
|
|
{
|
|
if((colEvt->entities[0] && colEvt->entities[0]->GetIsTypePed()) || (colEvt->entities[1] && colEvt->entities[1]->GetIsTypePed()))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return (colEvt->materialSettings[0] && bangMag > colEvt->materialSettings[0]->RollImpactMag) ||
|
|
(colEvt->materialSettings[1] && bangMag > colEvt->materialSettings[1]->RollImpactMag);
|
|
}
|
|
|
|
bool audCollisionAudioEntity::ShouldPlayScrapeImpact(const audCollisionEvent * colEvt, float bangMag)
|
|
{
|
|
if((colEvt->entities[0] && colEvt->entities[0]->GetIsTypePed()) || (colEvt->entities[1] && colEvt->entities[1]->GetIsTypePed()))
|
|
{
|
|
return true;
|
|
}
|
|
return (colEvt->materialSettings[0] && bangMag > colEvt->materialSettings[0]->ScrapeImpactMag) ||
|
|
(colEvt->materialSettings[1] && bangMag > colEvt->materialSettings[1]->ScrapeImpactMag);
|
|
}
|
|
|
|
bool audCollisionAudioEntity::PopulateAudioCollisionEventFromImpact(audCollisionEvent *collisionEvent, const VfxCollisionInfo_s &collisioninfo,
|
|
float bangMag, float scrapeMag, float rollMag, bool shouldFlipEntities)
|
|
{
|
|
|
|
//Compensate for entities being the wrong way round
|
|
u8 indexOffset = 0;
|
|
if(shouldFlipEntities)
|
|
{
|
|
indexOffset = 1;
|
|
}
|
|
|
|
u8 otherIndex = (indexOffset + 1)&1;
|
|
|
|
AudioWeight weights[2];
|
|
weights[indexOffset] = GetAudioWeight(collisionEvent->entities[indexOffset]);
|
|
weights[otherIndex] = GetAudioWeight(collisionEvent->entities[otherIndex]);
|
|
|
|
collisionEvent->material0IsMap = weights[0] == AUDIO_WEIGHT_VH;
|
|
collisionEvent->material1IsMap = weights[1] == AUDIO_WEIGHT_VH;
|
|
|
|
if(collisionEvent->entities[0] && collisionEvent->entities[0]->GetCurrentPhysicsInst())
|
|
{
|
|
collisionEvent->material0IsMap = collisionEvent->entities[0]->GetCurrentPhysicsInst()->GetClassType() == PH_INST_MAPCOL;
|
|
}
|
|
if(collisionEvent->entities[1] && collisionEvent->entities[1]->GetCurrentPhysicsInst())
|
|
{
|
|
collisionEvent->material1IsMap = collisionEvent->entities[1]->GetCurrentPhysicsInst()->GetClassType() == PH_INST_MAPCOL;
|
|
}
|
|
|
|
|
|
f32 weightScaling[2];
|
|
weightScaling[indexOffset] = GetAudioWeightScaling(weights[indexOffset], weights[otherIndex]);
|
|
weightScaling[otherIndex] = GetAudioWeightScaling(weights[otherIndex], weights[indexOffset]);
|
|
|
|
//Magnitudes are calculated using the bang/scrape/roll multiplied by the other entity's weight
|
|
collisionEvent->impulseMagnitudes[indexOffset] = bangMag * weightScaling[indexOffset];
|
|
collisionEvent->impulseMagnitudes[otherIndex] = bangMag * weightScaling[otherIndex];
|
|
|
|
rollMag = rollMag * g_RollScalar;
|
|
|
|
if(collisionEvent->type == AUD_COLLISION_TYPE_ROLL && (!collisionEvent->rollUpdatedThisFrame || rollMag * weightScaling[indexOffset] > collisionEvent->rollMagnitudes[indexOffset]))
|
|
{
|
|
collisionEvent->rollMagnitudes[indexOffset] = rollMag * weightScaling[indexOffset];
|
|
collisionEvent->rollMagnitudes[otherIndex] = rollMag * weightScaling[otherIndex];
|
|
}
|
|
|
|
if(collisionEvent->type == AUD_COLLISION_TYPE_SCRAPE && (!collisionEvent->scrapeUpdatedThisFrame || (scrapeMag *weightScaling[indexOffset]) > collisionEvent->scrapeMagnitudes[indexOffset]))
|
|
{
|
|
collisionEvent->scrapeMagnitudes[indexOffset] = scrapeMag * weightScaling[indexOffset];
|
|
collisionEvent->scrapeMagnitudes[otherIndex] = scrapeMag * weightScaling[otherIndex];
|
|
}
|
|
|
|
Vector3 normals[2];
|
|
normals[indexOffset] = VEC3V_TO_VECTOR3(collisioninfo.vWorldNormalA);
|
|
normals[otherIndex] = VEC3V_TO_VECTOR3(collisioninfo.vWorldNormalB);
|
|
|
|
//Use some temporary material ids to let us override the materials for special cases
|
|
phMaterialMgr::Id materialIds[2];
|
|
materialIds[indexOffset] = collisioninfo.mtlIdA;
|
|
materialIds[otherIndex] = collisioninfo.mtlIdB;
|
|
|
|
for(u8 i=0; i<2; i++)
|
|
{
|
|
CEntity *entity = collisionEvent->entities[i];
|
|
CEntity *otherEntity = collisionEvent->entities[(i + 1)&1];
|
|
|
|
collisionEvent->materialSettings[i] = NULL;
|
|
|
|
if(entity && entity->GetIsTypePed())
|
|
{
|
|
// bullet impacts and car impacts increase general impacts for a time, as does death
|
|
// but not with vehicles
|
|
if((otherEntity == NULL || ((CPed*)entity)->GetPedAudioEntity()->ShouldUpBodyfallSounds() || ((((CPed*)entity)->IsDead()) && collisionEvent->GetFlag(AUD_COLLISION_FLAG_MELEE))))
|
|
{
|
|
collisionEvent->impulseMagnitudes[i] *= g_BodyFallBoost;
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->impulseMagnitudes[i] *= g_PedBuildingImpulseScalingFactor;
|
|
}
|
|
|
|
if(collisionEvent->type == AUD_COLLISION_TYPE_SCRAPE)
|
|
{
|
|
collisionEvent->materialSettings[i] = ((CPed*)entity)->GetPedAudioEntity()->GetScrapeMaterialSettings();
|
|
}
|
|
|
|
}
|
|
|
|
if(entity && entity->GetIsTypeObject() && entity->GetBaseModelInfo()->GetModelType() == MI_TYPE_VEHICLE)
|
|
{
|
|
CVehicleModelInfo * vehicleInfo = (CVehicleModelInfo *)(entity->GetBaseModelInfo());
|
|
VehicleCollisionSettings * vehicleCollisions = NULL;
|
|
u32 audioHash = vehicleInfo->GetAudioNameHash();
|
|
|
|
if(!audioHash)
|
|
{
|
|
audioHash = vehicleInfo->GetModelNameHash();
|
|
}
|
|
|
|
if(vehicleInfo->GetVehicleType() == VEHICLE_TYPE_BICYCLE)
|
|
{
|
|
BicycleAudioSettings * bicycleSettings = audNorthAudioEngine::GetObject<BicycleAudioSettings>(audioHash);
|
|
if(bicycleSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(bicycleSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
else if(vehicleInfo->GetIsTrain())
|
|
{
|
|
TrainAudioSettings * trainSettings = audNorthAudioEngine::GetObject<TrainAudioSettings>(audioHash);
|
|
if(trainSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(trainSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
else if(vehicleInfo->GetIsBoat())
|
|
{
|
|
BoatAudioSettings * boatSettings = audNorthAudioEngine::GetObject<BoatAudioSettings>(audioHash);
|
|
if(boatSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(boatSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
else if(vehicleInfo->GetIsPlane())
|
|
{
|
|
PlaneAudioSettings * planeSettings = audNorthAudioEngine::GetObject<PlaneAudioSettings>(audioHash);
|
|
if(Water::IsCameraUnderwater())
|
|
{
|
|
collisionEvent->impulseMagnitudes[i] = 0.f;
|
|
}
|
|
if(planeSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(planeSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
else if(vehicleInfo->GetVehicleType() == VEHICLE_TYPE_HELI || vehicleInfo->GetVehicleType()==VEHICLE_TYPE_AUTOGYRO || vehicleInfo->GetVehicleType()==VEHICLE_TYPE_BLIMP)
|
|
{
|
|
HeliAudioSettings * heliSettings = audNorthAudioEngine::GetObject<HeliAudioSettings>(audioHash);
|
|
if(heliSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(heliSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
else if(vehicleInfo->GetVehicleType() == VEHICLE_TYPE_TRAILER)
|
|
{
|
|
//Trailers don't have VehicleCollisionSettings and are set up via MACS
|
|
}
|
|
else if(vehicleInfo->GetIsAutomobile() || vehicleInfo->GetVehicleType() == VEHICLE_TYPE_BIKE)
|
|
{
|
|
CarAudioSettings * carSettings = audNorthAudioEngine::GetObject<CarAudioSettings>(audioHash);
|
|
if(carSettings)
|
|
{
|
|
vehicleCollisions = audNorthAudioEngine::GetObject<VehicleCollisionSettings>(carSettings->VehicleCollisions);
|
|
}
|
|
}
|
|
|
|
|
|
if(vehicleCollisions)
|
|
{
|
|
CObject * object = (CObject*)entity;
|
|
if(object->m_nObjectFlags.bCarWheel)
|
|
{
|
|
collisionEvent->materialSettings[i] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(vehicleCollisions->WheelFragMaterial);
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->materialSettings[i] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(vehicleCollisions->FragMaterial);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
collisionEvent->materialIds[i] = materialIds[i];
|
|
|
|
if (!collisionEvent->materialSettings[i])
|
|
{
|
|
// extract the material data from the collision and see if we have a model specific override
|
|
phMaterialMgr::Id unpackedMtlIdA = PGTAMATERIALMGR->UnpackMtlId(materialIds[i]);
|
|
CollisionMaterialSettings * materialSettings = GetMaterialOverride(collisionEvent->entities[i], g_audCollisionMaterials[(s32)unpackedMtlIdA], collisionEvent->components[i]);
|
|
collisionEvent->materialSettings[i] = materialSettings;
|
|
}
|
|
|
|
CPed *ped = NULL;
|
|
|
|
if(entity->GetIsTypePed())
|
|
{
|
|
ped = ((CPed *)(entity));
|
|
}
|
|
|
|
if(ped && ped->GetPedType() == PEDTYPE_ANIMAL)
|
|
{
|
|
collisionEvent->impulseMagnitudes[i] *= g_AnimalImpactScalar;
|
|
|
|
if(ped->GetCapsuleInfo()->IsBird())
|
|
{
|
|
collisionEvent->materialSettings[i] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_BIRD", 0xBDC5E9E2));
|
|
}
|
|
else if(ped->GetCapsuleInfo()->IsFish())
|
|
{
|
|
collisionEvent->materialSettings[i] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_FISH", 0x51392531));
|
|
}
|
|
else if(ped->GetCapsuleInfo()->IsQuadruped())
|
|
{
|
|
switch(ped->GetArchetype()->GetModelNameHash())
|
|
{
|
|
case 0x97207223: //ATSTRINGHASH("A_C_Horse", 0x97207223)
|
|
case 0xFCFA9E1E: //ATSTRINGHASH("A_C_Cow", 0xFCFA9E1E)
|
|
case 0xD86B5A95: //ATSTRINGHASH("a_c_deer", 0xD86B5A95)
|
|
collisionEvent->materialSettings[i] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_QUADPED_LARGE", 0x628B6797));
|
|
break;
|
|
case 0x14EC17EA: //ATSTRINGHASH("a_c_chop", 0x14EC17EA)
|
|
case 0xCE5FF074: //ATSTRINGHASH("a_c_boar", 0xCE5FF074)
|
|
case 0x644AC75E: //ATSTRINGHASH("a_c_coyote", 0x644AC75E)
|
|
case 0x1250D7BA: //ATSTRINGHASH("a_c_mtlion", 0x1250D7BA)
|
|
case 0xB11BAB56: //ATSTRINGHASH("a_c_pig", 0xB11BAB56)
|
|
case 0x349F33E1: //ATSTRINGHASH("a_c_retriever", 0x349F33E1)
|
|
case 0xC2D06F53: //ATSTRINGHASH("a_c_rhesus", 0xC2D06F53)
|
|
case 0x9563221D: //ATSTRINGHASH("a_c_rottweiler", 0x9563221D)
|
|
case 0xA8683715: //ATSTRINGHASH("a_c_chimp", 0xA8683715)
|
|
case 0x573201B8: //ATSTRINGHASH("a_c_cat_01", 0x573201B8)
|
|
case 0xFC4E657D: //ATSTRINGHASH("A_C_POODLE ", 0xFC4E657D)
|
|
case 0x6D362854: //ATSTRINGHASH("A_C_PUG", 0x6D362854)
|
|
case 0xAD7844BB: //ATSTRINGHASH("A_C_WESTY", 0xAD7844BB)
|
|
collisionEvent->materialSettings[i] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_QUADPED_MED", 0xEDA0B3E4));
|
|
break;
|
|
case 0xC3B52966: //ATSTRINGHASH("a_c_rat", 0xC3B52966)
|
|
case 0xDFB55C81: //ATSTRINGHASH("a_c_rabbit_01", 0xDFB55C81)
|
|
collisionEvent->materialSettings[i] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_QUADPED_SMALL", 0xB5D17E88));
|
|
break;
|
|
default:
|
|
char animalMaterial[32];
|
|
formatf(animalMaterial, "AM_ANIMAL_%08X", ped->GetArchetype()->GetModelNameHash());
|
|
CollisionMaterialSettings * animalSettings = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(animalMaterial);
|
|
if(animalSettings)
|
|
{
|
|
collisionEvent->materialSettings[0] = animalSettings;
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->materialSettings[0] = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(ATSTRINGHASH("AM_BASE_QUADPED_MED", 0xEDA0B3E4));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(collisionEvent->materialSettings[indexOffset] && collisionEvent->materialSettings[indexOffset]->RollSound == g_NullSoundHash)
|
|
{
|
|
collisionEvent->rollMagnitudes[indexOffset] = 0.f;
|
|
}
|
|
if(collisionEvent->materialSettings[otherIndex] && collisionEvent->materialSettings[otherIndex]->RollSound == g_NullSoundHash)
|
|
{
|
|
collisionEvent->rollMagnitudes[otherIndex] = 0.f;
|
|
}
|
|
}
|
|
|
|
if(collisionEvent->materialSettings[0] == collisionEvent->materialSettings[1])
|
|
{
|
|
if(collisionEvent->impulseMagnitudes[0] > collisionEvent->impulseMagnitudes[1])
|
|
{
|
|
collisionEvent->impulseMagnitudes[1] = 0.f;
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->impulseMagnitudes[0] = 0.f;
|
|
}
|
|
|
|
if(collisionEvent->scrapeMagnitudes[0] > collisionEvent->scrapeMagnitudes[1])
|
|
{
|
|
collisionEvent->scrapeMagnitudes[1] = 0.f;
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->scrapeMagnitudes[0] = 0.f;
|
|
}
|
|
|
|
if(collisionEvent->rollMagnitudes[0] > collisionEvent->rollMagnitudes[1])
|
|
{
|
|
collisionEvent->rollMagnitudes[1] = 0.f;
|
|
}
|
|
else
|
|
{
|
|
collisionEvent->rollMagnitudes[0] = 0.f;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CollisionMaterialSettings * audCollisionAudioEntity::GetVehicleCollisionSettings()
|
|
{
|
|
phMaterialMgr::Id unpackedMtlIdA = PGTAMATERIALMGR->UnpackMtlId(m_MaterialIdCarBody);
|
|
return g_audCollisionMaterials[(s32)unpackedMtlIdA];
|
|
}
|
|
|
|
CollisionMaterialSettings * audCollisionAudioEntity::GetCollisionSettings(s32 materialId)
|
|
{
|
|
phMaterialMgr::Id unpackedMtlIdA = PGTAMATERIALMGR->UnpackMtlId(materialId);
|
|
return g_audCollisionMaterials[(s32)unpackedMtlIdA];
|
|
}
|
|
|
|
|
|
|
|
CollisionMaterialSettings *audCollisionAudioEntity::GetMaterialSettingsFromMatId(const CEntity * entity, phMaterialMgr::Id matId, u32 component)
|
|
{
|
|
naAssertf(PGTAMATERIALMGR->UnpackMtlId(matId)<(phMaterialMgr::Id)0xffff, "Too big for s32 cast");
|
|
naAssertf(PGTAMATERIALMGR->UnpackMtlId(matId)<(phMaterialMgr::Id)g_audCollisionMaterials.GetCount(), "Out of bounds");
|
|
phMaterialMgr::Id unpackedMtlId = PGTAMATERIALMGR->UnpackMtlId(matId);
|
|
return GetMaterialOverride(entity, g_audCollisionMaterials[(s32)unpackedMtlId], component);
|
|
}
|
|
|
|
const ModelAudioCollisionSettings * audCollisionAudioEntity::GetFragComponentMaterialSettingsSafe(const ModelAudioCollisionSettings * settings, u32 component)
|
|
{
|
|
if(!GetNumFragComponentsFromModelSettings(settings))
|
|
{
|
|
return settings;
|
|
}
|
|
|
|
return GetFragComponentMaterialSettings(settings, component);
|
|
}
|
|
|
|
const ModelAudioCollisionSettings * audCollisionAudioEntity::GetFragComponentMaterialSettings(const ModelAudioCollisionSettings * settings, u32 component, bool dontUseParentMacs)
|
|
{
|
|
|
|
//naAssertf(component<GetNumFragComponentsFromModelSettings(settings), "Attempted to get ModelAudioSettings for fragment but component id is invalid");
|
|
if((component>=GetNumFragComponentsFromModelSettings(settings)))
|
|
{
|
|
if(dontUseParentMacs)
|
|
{
|
|
return NULL;
|
|
}
|
|
return settings;
|
|
}
|
|
|
|
//Fix up the frag component array as it sits underneath the variable MaterialOverride array
|
|
//ModelAudioCollisionSettings::tFragComponentSetting * fragArray = (ModelAudioCollisionSettings::tFragComponentSetting *) (((u8*)(settings->MaterialOverride + settings->numMaterialOverrides)) + 1);
|
|
ModelAudioCollisionSettings::tFragComponentSetting * fragArray = (ModelAudioCollisionSettings::tFragComponentSetting *) &(settings->MaterialOverride[settings->numMaterialOverrides].Override);
|
|
|
|
|
|
const ModelAudioCollisionSettings * newSettings = NULL;
|
|
|
|
if(fragArray[component].ModelSettings.IsValid())
|
|
{
|
|
newSettings = audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(fragArray[component].ModelSettings);
|
|
}
|
|
|
|
if(!newSettings && !dontUseParentMacs)
|
|
{
|
|
newSettings = settings;
|
|
}
|
|
|
|
return newSettings;
|
|
}
|
|
|
|
|
|
//material is the audStringHash of the CollisionMaterialSettings name
|
|
CollisionMaterialSettings * audCollisionAudioEntity::GetMaterialOverride(const CEntity * entity, CollisionMaterialSettings * base, u32 component)
|
|
{
|
|
if(entity && entity->IsArchetypeSet())
|
|
{
|
|
const ModelAudioCollisionSettings *settings = entity->GetBaseModelInfo()->GetAudioCollisionSettings();
|
|
// Check for overrides in our map.
|
|
if(!settings || (settings == g_MacsDefault))
|
|
{
|
|
// Hacky fix for url:bugstar:3580010
|
|
if(entity->GetBaseModelInfo()->GetModelNameHash() == ATSTRINGHASH("prop_ld_can_01b", 0x6D9C0AC))
|
|
{
|
|
settings = audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(ATSTRINGHASH("mod_prop_ld_can_01b", 0x65DDABE4));
|
|
}
|
|
|
|
auto mapOverride = sm_MacOverrideMap.Access(entity->GetBaseModelInfo()->GetModelNameHash());
|
|
if(mapOverride)
|
|
{
|
|
settings = audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>((*mapOverride));
|
|
entity->GetBaseModelInfo()->SetAudioCollisionSettings(settings,0);
|
|
}
|
|
}
|
|
|
|
if(entity->GetIsTypeObject())
|
|
{
|
|
CWeapon * weapon = ((CObject*)entity)->GetWeapon();
|
|
if(weapon && weapon->GetWeaponInfo() && weapon->GetWeaponInfo()->GetAudioCollisionHash())
|
|
{
|
|
const ModelAudioCollisionSettings * weaponOverride = audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(weapon->GetWeaponInfo()->GetAudioCollisionHash());
|
|
if(weaponOverride)
|
|
{
|
|
settings = weaponOverride;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(settings)
|
|
{
|
|
//See if the model has frag components
|
|
if(GetNumFragComponentsFromModelSettings(settings))
|
|
{
|
|
//Get individual model settings for the fragment
|
|
const ModelAudioCollisionSettings *fragSettings = GetFragComponentMaterialSettings(settings, component);
|
|
|
|
//If we only have one possible override, let's just use that
|
|
if(fragSettings->numMaterialOverrides == 1 && fragSettings->MaterialOverride[0].Override.IsValid())
|
|
{
|
|
CollisionMaterialSettings * overrideMaterial = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(fragSettings->MaterialOverride[0].Override);
|
|
return overrideMaterial ? overrideMaterial : base;
|
|
}
|
|
|
|
for(s32 i=0; fragSettings && i<fragSettings->numMaterialOverrides; i++)
|
|
{
|
|
if(base && fragSettings->MaterialOverride[i].Material.IsValid() && fragSettings->MaterialOverride[i].Override.IsValid() && base == audNorthAudioEngine::GetObject<CollisionMaterialSettings>(fragSettings->MaterialOverride[i].Material))
|
|
{
|
|
//Found an override...huzzah!
|
|
CollisionMaterialSettings * overrideMaterial = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(fragSettings->MaterialOverride[i].Override);
|
|
return overrideMaterial ? overrideMaterial : base;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(settings->numMaterialOverrides == 1 && settings->MaterialOverride[0].Override.IsValid())
|
|
{
|
|
CollisionMaterialSettings * overrideMaterial = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(settings->MaterialOverride[0].Override);
|
|
return overrideMaterial ? overrideMaterial : base;
|
|
}
|
|
|
|
//We have found model specific settings game object so see if there is an override for this material
|
|
for(s32 i=0; i<settings->numMaterialOverrides; i++)
|
|
{
|
|
if(base && settings->MaterialOverride[i].Material.IsValid() && settings->MaterialOverride[i].Override.IsValid() && base == audNorthAudioEngine::GetObject<CollisionMaterialSettings>(settings->MaterialOverride[i].Material))
|
|
{
|
|
//Found an override...huzzah!
|
|
CollisionMaterialSettings * overrideMaterial = audNorthAudioEngine::GetObject<CollisionMaterialSettings>(settings->MaterialOverride[i].Override);
|
|
return overrideMaterial ? overrideMaterial : base;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return base;
|
|
}
|
|
|
|
AudioWeight audCollisionAudioEntity::GetAudioWeight(CEntity *entity)
|
|
{
|
|
if(entity && entity->GetIsTypePed())
|
|
{
|
|
return AUDIO_WEIGHT_M;
|
|
}
|
|
|
|
if(entity && entity->GetIsTypeVehicle())
|
|
{
|
|
return AUDIO_WEIGHT_H;
|
|
}
|
|
|
|
if(entity && entity->GetIsTypeObject() && entity->IsArchetypeSet())
|
|
{
|
|
const ModelAudioCollisionSettings *settings = entity->GetBaseModelInfo()->GetAudioCollisionSettings();
|
|
if(settings)
|
|
{
|
|
//If we have a frag model, use the weight of the heaviest thing in the fragment (this could be extended to return the parent settings weight if the entity is unbroken should we need/want that functionality)
|
|
s32 numComponents = GetNumFragComponentsFromModelSettings(settings);
|
|
fragInst * frag = entity->GetFragInst();
|
|
if(numComponents && entity->GetFragInst())
|
|
{
|
|
AudioWeight weight = AUDIO_WEIGHT_VL;
|
|
if(frag)
|
|
{
|
|
//naDisplayf("Model settings: %s, numComponents", audNorthAudioEngine::GetMetadataManager().GetObjectNameFromNameTableOffset(settings->NameTableOffset), numComponents);
|
|
|
|
//naAssertf(numComponents == frag->GetTypePhysics()->GetCompositeBounds()->GetNumBounds(), "Model components (%d) and Macs frags (%d) are out of sync for %s: regenerate them, or fix them up in rave", frag->GetTypePhysics()->GetCompositeBounds()->GetNumBounds(), numComponents, entity->GetModelName());
|
|
if(numComponents != frag->GetTypePhysics()->GetCompositeBounds()->GetNumBounds())
|
|
{
|
|
return (AudioWeight)settings->Weight;
|
|
}
|
|
|
|
for(s32 i = 0; i < numComponents; i++)
|
|
{
|
|
if(!frag->GetChildBroken(i))
|
|
{
|
|
const ModelAudioCollisionSettings * fragMod = GetFragComponentMaterialSettings(settings, i);
|
|
if(fragMod)
|
|
{
|
|
weight = (AudioWeight)((fragMod->Weight > weight) ? fragMod->Weight : weight);
|
|
}
|
|
}
|
|
}
|
|
return weight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return (AudioWeight)settings->Weight;
|
|
}
|
|
}
|
|
}
|
|
|
|
return AUDIO_WEIGHT_VH;
|
|
}
|
|
|
|
|
|
f32 audCollisionAudioEntity::GetAudioWeightScaling(AudioWeight weight, AudioWeight otherWeight)
|
|
{
|
|
s32 weightDifference = Max(weight - otherWeight, 0);
|
|
|
|
return g_AudWeightDifferenceScaling[weightDifference];
|
|
}
|
|
|
|
|
|
|
|
|
|
#if __BANK
|
|
|
|
void audCollisionAudioEntity::PrintCollisionSoundDebug(const char * context, f32 impulse, f32 volume, u32 soundHash)
|
|
{
|
|
Displayf("[NorthAudio] %s, %u, %f, %f", context,
|
|
//g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(soundHash),
|
|
soundHash,
|
|
volume, impulse);
|
|
}
|
|
|
|
void audCollisionAudioEntity::DrawDebug()
|
|
{
|
|
if(g_DebugAudioImpacts)
|
|
{
|
|
for(u32 i = 0; i < g_NumDebugCollisionInfos; i++)
|
|
{
|
|
if(!g_CollisionDebugInfo[i].hidden)
|
|
{
|
|
grcDebugDraw::Sphere(RCC_VEC3V(g_CollisionDebugInfo[i].position), 0.05f, Color32(255,255,0));
|
|
char buf[256];
|
|
|
|
if(g_CollisionDebugInfo[i].impulseMag > 0.f)
|
|
{
|
|
formatf(buf, sizeof(buf), "%u: mag: %f [type: %u comp: %d] [type: %u comp: %d]", g_CollisionDebugInfo[i].time, g_CollisionDebugInfo[i].impulseMag,g_CollisionDebugInfo[i].entType1, g_CollisionDebugInfo[i].component1,g_CollisionDebugInfo[i].entType2, g_CollisionDebugInfo[i].component2);
|
|
grcDebugDraw::Text(RCC_VEC3V(g_CollisionDebugInfo[i].position), Color32(255,255,0), buf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(g_DebugAudioRolls)
|
|
{
|
|
for(u32 i = 0; i < g_NumDebugCollisionInfos; i++)
|
|
{
|
|
if(!g_CollisionDebugInfo[i].hidden && g_CollisionDebugInfo[i].type == AUD_COLLISION_TYPE_ROLL)
|
|
{
|
|
grcDebugDraw::Sphere(RCC_VEC3V(g_CollisionDebugInfo[i].position), 0.05f, Color32(100,255,0));
|
|
char buf[256];
|
|
|
|
if(g_CollisionDebugInfo[i].impulseMag > 0.f)
|
|
{
|
|
formatf(buf, sizeof(buf), "%u: mag: %f [type: %u comp: %d] [type: %u comp: %d]", g_CollisionDebugInfo[i].time, g_CollisionDebugInfo[i].impulseMag,g_CollisionDebugInfo[i].entType1, g_CollisionDebugInfo[i].component1,g_CollisionDebugInfo[i].entType2, g_CollisionDebugInfo[i].component2);
|
|
grcDebugDraw::Text(RCC_VEC3V(g_CollisionDebugInfo[i].position), Color32(100,255,0), buf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(g_DebugAudioScrapes)
|
|
{
|
|
for(u32 i = 0; i < g_NumDebugCollisionInfos; i++)
|
|
{
|
|
if(!g_CollisionDebugInfo[i].hidden && g_CollisionDebugInfo[i].type == AUD_COLLISION_TYPE_SCRAPE)
|
|
{
|
|
grcDebugDraw::Sphere(RCC_VEC3V(g_CollisionDebugInfo[i].position), 0.05f, Color32(255,100,0));
|
|
char buf[256];
|
|
|
|
if(g_CollisionDebugInfo[i].impulseMag > 0.f)
|
|
{
|
|
formatf(buf, sizeof(buf), "%u: mag: %f [type: %u comp: %d] [type: %u comp: %d]", g_CollisionDebugInfo[i].time, g_CollisionDebugInfo[i].impulseMag,g_CollisionDebugInfo[i].entType1, g_CollisionDebugInfo[i].component1,g_CollisionDebugInfo[i].entType2, g_CollisionDebugInfo[i].component2);
|
|
grcDebugDraw::Text(RCC_VEC3V(g_CollisionDebugInfo[i].position), Color32(255,100,0), buf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void audCollisionAudioEntity::AddWidgets(bkBank &bank)
|
|
{
|
|
bank.PushGroup("Collisions",false);
|
|
bank.PushGroup("Bullet impacts and ricochets",false);
|
|
bank.AddToggle("Dialogue suppress bullet impacts.", &sm_DialogueSuppressBI);
|
|
bank.AddSlider("Player's ricochets rolloff scale factor", &sm_PlayerRicochetsRollOffScaleFactor, 0.f, 10.f, 0.001f);
|
|
bank.AddSlider("Player's bullet impacts rolloff scale factor", &sm_PlayerBIRollOffScaleFactor, 0.f, 10.f, 0.001f);
|
|
bank.AddSlider("Player's headshot rolloff scale factor", &sm_PlayerHeadShotRollOffScaleFactor, 0.f, 10.f, 0.001f);
|
|
bank.AddSlider("Raygun BI Max Distance", &sm_DistanceToTriggerRaygunBI, 0.f, 1000.f, 0.001f);
|
|
bank.PopGroup();
|
|
bank.PushGroup("Automatic weapons bullet impacts",false);
|
|
bank.AddToggle("Only do it for vehicles", &sm_AutomaticImpactOnlyOnVeh);
|
|
bank.AddSlider("Distance to trigger.", &sm_DistanceToTriggerAutomaticBI, 0.f, 100000.f, 1.f);
|
|
bank.AddSlider("Time to retrigger.", &sm_DtToTriggerAutomaticBI, 0, 10000, 100);
|
|
bank.PopGroup();
|
|
bank.PushGroup("Shotgun bullet impacts",false);
|
|
bank.AddToggle("Don't do it for the player", &sm_ShotgunDisabledForPlayer);
|
|
bank.AddToggle("Only do it for vehicles", &sm_ShotgunImpactOnlyOnVeh);
|
|
bank.AddSlider("Distance to trigger.", &sm_DistanceToTriggerShotgunBI, 0.f, 100000.f, 1.f);
|
|
bank.AddSlider("Time to retrigger.", &sm_DtToTriggerShotgunBI, 0, 10000, 100);
|
|
bank.PopGroup();
|
|
bank.AddToggle("Disable", &g_DisableCollisionAudio);
|
|
bank.AddSlider("Min speed for collision", &g_MinSpeedForCollision, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Min roll speed", &g_MinRollSpeed, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Scrape impact velocity", &g_ScrapeImpactVel, 0.0f, 20.0f, 0.1f);
|
|
bank.AddSlider("Vehicle Scrape impact velocity", &g_VehicleScrapeImpactVel, 0.0f, 20.0f, 0.1f);
|
|
bank.AddSlider("Roll impact velocity", &g_RollImpactVel, 0.0f, 20.0f, 0.1f);
|
|
bank.AddSlider("Roll Scalar", &g_RollScalar, 0.1f, 10.0f, 0.1f);
|
|
bank.AddSlider("Impact Scalar", &s_ImpactMagScalar, 0.f, 10.0f, 0.1f);
|
|
bank.AddSlider("Collision time filter speed", &g_CollisionTimeFilterSpeed, 0.0f, 10.0f, 0.01f);
|
|
bank.AddSlider("Min time between collisions", &g_MinTimeBetweenCollisions, 0, 500, 1);
|
|
bank.AddSlider("Collision filter lifetime", &g_CollisionFilterLifetime, 0, 1000, 1);
|
|
bank.AddSlider("Wrecked vehicle scaling", &g_WreckedVehicleImpactScale, 0.f, 10.f, 0.1f);
|
|
bank.AddSlider("Wrecked vehicle scaling", &g_WreckedVehicleRolloffScale, 0.f, 10.f, 0.1f);
|
|
bank.AddSlider("Animal impact scaling", &g_AnimalImpactScalar, 0.f, 10.f, 0.1f);
|
|
bank.AddSlider("Body fall boost", &g_BodyFallBoost, 0.f, 10.f, 0.1f);
|
|
bank.AddSlider("Scrape against ped volume scale", &g_MaterialScrapeAgainstPedVolumeScaling, 0.f, 1.f, 0.01f);
|
|
bank.AddSlider("Player context collision rolloff", &sm_PlayerContextCollisionRolloff, 0.f, 10.f, 0.1f);
|
|
bank.AddSlider("Player context blip volume", &sm_PlayerContextVolumeForBlip, -100.f, 0.f, 1.f);
|
|
bank.AddSlider("sm_PlayerCollisionEventSpeedThreshold", &sm_PlayerCollisionEventSpeedThreshold, 0.f, 10.f, 0.1f);
|
|
//bank.AddSlider("Melee impact scale factor for bullet hits", &s_MeleeImpactScaleFactor, 0.f, 1000.f, 1.f);
|
|
|
|
bank.AddText("Collision Material Override", g_CollisionMaterialOverrideName, 64, false);
|
|
bank.AddToggle("Print bullet impact material", &g_PrintBulletImpactMaterial);
|
|
bank.AddToggle("Print melee impact material", &g_PrintMeleeImpactMaterial);
|
|
bank.AddToggle("Increase footsteps volume", &g_IncreaseFootstepsVolume);
|
|
bank.AddToggle("Don't play ricochets", &g_DontPlayRicochets);
|
|
bank.AddToggle("Do ped scrape start impacts", &g_DoPedScrapeStartImpacts);
|
|
bank.AddToggle("g_DebugAudioCollisions", &g_DebugAudioCollisions);
|
|
bank.AddToggle("g_DebugAudioImpacts", &g_DebugAudioImpacts);
|
|
bank.AddToggle("g_DebugAudioScrapes", &g_DebugAudioScrapes);
|
|
bank.AddToggle("g_DebugAudioRolls", &g_DebugAudioRolls);
|
|
bank.AddButton("Clear debug history", CFA(ClearDebugHistory));
|
|
bank.AddToggle("g_DebugPlayerVehImpacts", &g_DebugPlayerVehImpacts);
|
|
bank.AddToggle("g_MeleeOnlyBulletImpacts", &g_MeleeOnlyBulletImpacts);
|
|
bank.AddSlider("g_MaxRollSpeed", &g_MaxRollSpeed, 0.0f, 100.0f, 0.01f);
|
|
bank.AddToggle("g_MapUsesPropRollSpeedSettings", &g_MapUsesPropRollSpeedSettings);
|
|
bank.AddSlider("g_RollVolumeSmoothUp", &g_RollVolumeSmoothUp, 0.0f, 1.0f, 0.001f);
|
|
bank.AddSlider("g_RollVolumeSmoothDown", &g_RollVolumeSmoothDown, 0.0f, 1.0f, 0.001f);
|
|
bank.AddSlider("g_VelocityThesholdForPropSwing", &g_VelocityThesholdForPropSwing, 0.0f, 10.f, 0.1f);
|
|
bank.AddSlider("g_VehicleOnVehicleImpulseScalingFactor", &g_VehicleOnVehicleImpulseScalingFactor, 0.0f, 100.0f, 0.1f);
|
|
bank.AddSlider("g_BoatImpulseScalingFactor", &g_BoatImpulseScalingFactor, 0.0f, 100.0f, 0.1f);
|
|
bank.AddSlider("g_UpsideDownCarImpulseScalingFactor", &g_UpsideDownCarImpulseScalingFactor, 0.0f, 100.0f, 0.1f);
|
|
bank.AddToggle("PedsHitLikeGirls", &g_PedsHitLikeGirls);
|
|
bank.AddSlider("g_PedBuildingImpulseScalingFactor", &g_PedBuildingImpulseScalingFactor, 0.f, 100.f, 0.1f);
|
|
bank.AddSlider("ImpactRetriggerTime", &g_ImpactRetriggerTime, 0, 10000, 1);
|
|
bank.AddSlider("Frag collision start time", &g_FragCollisionStartTime, 0, 10000, 1);
|
|
bank.AddToggle("Use collision intensity culling", &g_UseCollisionIntensityCulling);
|
|
bank.AddSlider("Collison count threshold", &g_CollisionMaterialCountThreshold, 0, 100, 1);
|
|
bank.AddSlider("Collison count decrease rate", &g_CollisionCountDecreaseRate, 0, 100, 1);
|
|
bank.AddToggle("Debug focus entity", &g_DebugFocusEntity);
|
|
bank.AddToggle("DisplaysPedScrapeMaterial", &g_DisplayPedScrapeMaterial);
|
|
bank.AddSlider("g_PedWetSurfaceScrapeAtten", &g_PedWetSurfaceScrapeAtten, 0.f, 1.f, 1.f);
|
|
|
|
bank.AddSlider("g_MaxBulletImpactDampingDistance", &g_MaxBulletImpactDampingDistance, 0.0f, 1000.0f, 1.0f);
|
|
bank.AddSlider("g_MinBulletImpactDampingDistance", &g_MinBulletImpactDampingDistance, 0.0f, 1000.0f, 1.0f);
|
|
bank.AddSlider("g_MaxBulletImpactCloserImpulseDamping", &g_MaxBulletImpactCloserImpulseDamping, 0.0f, 1.0f, 0.01f);
|
|
bank.AddSlider("g_MaxBulletImpactCloserAttenuation", &g_MaxBulletImpactCloserAttenuation, -100.0f, 100.0f, 0.1f);
|
|
bank.AddSlider("g_MaxImpulseMagnitudeForMeleeDamageDoneGeneral", &g_MaxImpulseMagnitudeForMeleeDamageDoneGeneral, 0.f, 100.f, 0.1f);
|
|
bank.AddToggle("Debug swinging props", &g_DebugSwingingProps);
|
|
bank.AddSlider("Swing Vol Smooth Rate", &g_SwingVolSmoothRate, 0.f, 10.f, 0.1f);
|
|
bank.AddSlider("Weapon impact filter time", &g_WeaponImpactFilterTime, 0, 10000, 100);
|
|
bank.AddSlider("Ped scrape start impact time", &g_WeaponImpactFilterTime, 0, 10000, 100);
|
|
bank.AddToggle("g_PedsMakeSolidCollisions", &g_PedsMakeSolidCollisions);
|
|
bank.AddToggle("g_PedsOverrideImpactMag", &g_PedsOverrideImpactMag);
|
|
bank.PushGroup("Audio Weights Difference scaling");
|
|
bank.AddSlider("Difference of 1", &g_AudWeightDifferenceScaling[1], 0.f, 1.f, 0.01f);
|
|
bank.AddSlider("Difference of 2", &g_AudWeightDifferenceScaling[2], 0.f, 1.f, 0.01f);
|
|
bank.AddSlider("Difference of 3", &g_AudWeightDifferenceScaling[3], 0.f, 1.f, 0.01f);
|
|
bank.AddSlider("Solid Roll Attenuation", &g_SolidRollAttenuation, -100.f, 0.f, 0.5f);
|
|
bank.AddSlider("Soft Roll Attenuation", &g_SoftRollAttenuation, -100.f, 0.f, 0.5f);
|
|
bank.PopGroup();
|
|
bank.AddToggle("HardImpactsExpludeSolid", &g_HardImpactsExcludeSolid);
|
|
bank.PushGroup("Model editor", true);
|
|
bank.AddToggle("Enable model editor", &sm_EnableModelEditor);
|
|
bank.AddToggle("Preserve Existing Settings", &g_PreserveMACSSettingsOnExport);
|
|
bank.AddButton("Create MACS for current model", CFA(CreateModelAudioSettings));
|
|
bank.AddButton("Create Frag MACS for current model", CFA(CreateFragModelAudioSettings));
|
|
bank.AddButton("Create MACS for current frag component", CFA(CreateIndividualFragModelAudioSettings));
|
|
bank.AddSlider("Batch create start index", &batchModelCreateStartIndex, 0, 2000, 1);
|
|
bank.AddButton("Batch create models", CFA(BatchCreateMacs));
|
|
bank.AddSlider("Batch create stop index", &batchModelCreateStopIndex, 0, 2000, 1);
|
|
bank.AddText("Current Model", s_ModelSettingsCreator.audioSettingsName, sizeof(s_ModelSettingsCreator.audioSettingsName), false);
|
|
bank.AddText("Current Material", s_PointAndClickMaterial, sizeof(s_PointAndClickMaterial), false);
|
|
bank.AddText("Current Material Override", s_PointAndClickMaterialOverride, sizeof(s_PointAndClickMaterial), false);
|
|
bank.AddText("Current Frag Component`", s_PointAndClickComponent, sizeof(s_PointAndClickComponent), false);
|
|
bank.AddButton("View current model settings", CFA(OpenCurrentModelInRave));
|
|
bank.AddButton("Synchronize Macs and models", CFA(SynchronizeMacsAndModels));
|
|
bank.AddButton("Find new models", CFA(FindNewModels));
|
|
bank.AddButton("Find default material props", CFA(FindLocalDefaultMaterialModels));
|
|
bank.AddButton("Spawn new model object", CFA(SpawnNewModelObject));
|
|
bank.AddButton("Spawn sync model object", CFA(SpawnSyncModelObject));
|
|
bank.AddSlider("Set new model spawn object", &newModelSpawnIndex, 0, 2000, 1);
|
|
bank.AddSlider("Set sync model spawn object", &syncModelSpawnIndex, 0, 2000, 1);
|
|
bank.AddSeparator();
|
|
bank.AddText("Rave Selected Settings", s_RaveSelectedMaterialSettingsName, 256, false);
|
|
bank.AddToggle("Auto Spawn", &s_AutoSpawnRaveObjects);
|
|
bank.AddButton("Spawn Rave Settings", CFA(audCollisionAudioEntity::SpawnRaveSettings));
|
|
bank.AddText("Prop spawn Name", s_PropSpawnName, 256, false);
|
|
bank.AddButton("Spawn Prop from Name", CFA(SpawnPropFromPropName));
|
|
bank.AddSeparator();
|
|
bank.AddToggle("Generate Collision Lists", &sm_ProcessSectors);
|
|
bank.PushGroup("Simulate collision");
|
|
bank.AddButton("Do Collision", CFA(SimulateCollision));
|
|
bank.AddSlider("Velocity", &s_SimulateCollisionVel, 0.f, 20.f, 1.f);
|
|
bank.AddToggle("Soft impacts", &s_SimulateCollisionSoftImpact);
|
|
//bank.AddCombo("Impact Weight", &s_SimulateCollisionWeight, NUM_AUDIOWEIGHT, s_AudioWeightNames, NullCB, "Select an impact weight");
|
|
bank.AddSlider("Impact Weight", &s_SimulateCollisionWeight1, 0, NUM_AUDIOWEIGHT-1, 1);
|
|
bank.AddSlider("Other Impact Weight", &s_SimulateCollisionWeight1, 0, NUM_AUDIOWEIGHT-1, 1);
|
|
bank.PopGroup();
|
|
bank.PopGroup();
|
|
bank.AddSlider("Player melee collision boost", &g_PlayerMeleeCollisionVolumeBoost, -100.f, 100.f, 1.f);
|
|
bank.AddSlider("Player melee-vehicle collision scale", &g_PlayerVehicleMeleeCollisionVolumeScale, -100.f, 100.f, 1.f);
|
|
bank.PopGroup();
|
|
}
|
|
#endif
|