166 lines
5.9 KiB
C++
166 lines
5.9 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#ifndef VPHYSICS_SOUND_H
|
|
#define VPHYSICS_SOUND_H
|
|
#ifdef _WIN32
|
|
#pragma once
|
|
#endif
|
|
|
|
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
|
|
|
namespace physicssound {
|
|
struct impactsound_t {
|
|
void *pGameData;
|
|
int entityIndex;
|
|
int soundChannel;
|
|
float volume;
|
|
float impactSpeed;
|
|
unsigned short surfaceProps;
|
|
unsigned short surfacePropsHit;
|
|
Vector origin;
|
|
};
|
|
|
|
// UNDONE: Use a sorted container and sort by volume/distance?
|
|
struct soundlist_t {
|
|
CUtlVector<impactsound_t> elements;
|
|
impactsound_t &GetElement(int index) { return elements[index]; }
|
|
impactsound_t &AddElement() { return elements[elements.AddToTail()]; }
|
|
int Count() { return elements.Count(); }
|
|
void RemoveAll() { elements.RemoveAll(); }
|
|
};
|
|
|
|
void PlayImpactSounds(soundlist_t &list) {
|
|
for (int i = list.Count() - 1; i >= 0; --i) {
|
|
impactsound_t &sound = list.GetElement(i);
|
|
const surfacedata_t *psurf =
|
|
physprops->GetSurfaceData(sound.surfaceProps);
|
|
if (psurf->sounds.impactHard) {
|
|
const surfacedata_t *pHit =
|
|
physprops->GetSurfaceData(sound.surfacePropsHit);
|
|
unsigned short soundName = psurf->sounds.impactHard;
|
|
if (pHit && psurf->sounds.impactSoft) {
|
|
if (pHit->audio.hardnessFactor < psurf->audio.hardThreshold ||
|
|
(psurf->audio.hardVelocityThreshold > 0 &&
|
|
psurf->audio.hardVelocityThreshold > sound.impactSpeed)) {
|
|
soundName = psurf->sounds.impactSoft;
|
|
}
|
|
}
|
|
const char *pSound = physprops->GetString(soundName);
|
|
|
|
CSoundParameters params;
|
|
if (!CBaseEntity::GetParametersForSound(pSound, params, NULL))
|
|
break;
|
|
|
|
if (sound.volume > 1) sound.volume = 1;
|
|
CPASAttenuationFilter filter(sound.origin, params.soundlevel);
|
|
// JAY: If this entity gets deleted, the sound comes out at the
|
|
// world origin this sounds bad! Play on ent 0 for now.
|
|
EmitSound_t ep;
|
|
ep.m_nChannel = sound.soundChannel;
|
|
ep.m_pSoundName = params.soundname;
|
|
ep.m_flVolume = params.volume * sound.volume;
|
|
ep.m_SoundLevel = params.soundlevel;
|
|
ep.m_nPitch = params.pitch;
|
|
ep.m_pOrigin = &sound.origin;
|
|
|
|
CBaseEntity::EmitSound(filter, 0 /*sound.entityIndex*/, ep);
|
|
}
|
|
}
|
|
list.RemoveAll();
|
|
}
|
|
void AddImpactSound(soundlist_t &list, void *pGameData, int entityIndex,
|
|
int soundChannel, IPhysicsObject *pObject, int surfaceProps,
|
|
int surfacePropsHit, float volume, float impactSpeed) {
|
|
impactSpeed += 1e-4;
|
|
for (int i = list.Count() - 1; i >= 0; --i) {
|
|
impactsound_t &sound = list.GetElement(i);
|
|
// UNDONE: Compare entity or channel somehow?
|
|
// UNDONE: Doing one slot per entity is too noisy. So now we use one
|
|
// slot per material
|
|
|
|
// heuristic - after 4 impacts sounds in one frame, start merging
|
|
// everything
|
|
if (surfaceProps == sound.surfaceProps || list.Count() > 4) {
|
|
// UNDONE: Store instance volume separate from aggregate volume and
|
|
// compare that?
|
|
if (volume > sound.volume) {
|
|
pObject->GetPosition(&sound.origin, NULL);
|
|
sound.pGameData = pGameData;
|
|
sound.entityIndex = entityIndex;
|
|
sound.soundChannel = soundChannel;
|
|
sound.surfacePropsHit = surfacePropsHit;
|
|
}
|
|
sound.volume += volume;
|
|
sound.impactSpeed = MAX(impactSpeed, sound.impactSpeed);
|
|
return;
|
|
}
|
|
}
|
|
|
|
impactsound_t &sound = list.AddElement();
|
|
sound.pGameData = pGameData;
|
|
sound.entityIndex = entityIndex;
|
|
sound.soundChannel = soundChannel;
|
|
pObject->GetPosition(&sound.origin, NULL);
|
|
sound.surfaceProps = surfaceProps;
|
|
sound.surfacePropsHit = surfacePropsHit;
|
|
sound.volume = volume;
|
|
sound.impactSpeed = impactSpeed;
|
|
}
|
|
|
|
struct breaksound_t {
|
|
Vector origin;
|
|
int surfacePropsBreak;
|
|
};
|
|
|
|
void AddBreakSound(CUtlVector<breaksound_t> &list, const Vector &origin,
|
|
unsigned short surfaceProps) {
|
|
const surfacedata_t *psurf = physprops->GetSurfaceData(surfaceProps);
|
|
if (!psurf->sounds.breakSound) return;
|
|
|
|
for (int i = list.Count() - 1; i >= 0; --i) {
|
|
breaksound_t &sound = list.Element(i);
|
|
// Allow 3 break sounds before you start merging anything.
|
|
if (list.Count() > 2 && surfaceProps == sound.surfacePropsBreak) {
|
|
sound.origin = (sound.origin + origin) * 0.5f;
|
|
return;
|
|
}
|
|
}
|
|
breaksound_t sound;
|
|
sound.origin = origin;
|
|
sound.surfacePropsBreak = surfaceProps;
|
|
list.AddToTail(sound);
|
|
}
|
|
|
|
void PlayBreakSounds(CUtlVector<breaksound_t> &list) {
|
|
for (int i = list.Count() - 1; i >= 0; --i) {
|
|
breaksound_t &sound = list.Element(i);
|
|
|
|
const surfacedata_t *psurf =
|
|
physprops->GetSurfaceData(sound.surfacePropsBreak);
|
|
const char *pSound = physprops->GetString(psurf->sounds.breakSound);
|
|
CSoundParameters params;
|
|
if (!CBaseEntity::GetParametersForSound(pSound, params, NULL)) return;
|
|
|
|
// Play from the world, because the entity is breaking, so it'll be
|
|
// destroyed soon
|
|
CPASAttenuationFilter filter(sound.origin, params.soundlevel);
|
|
EmitSound_t ep;
|
|
ep.m_nChannel = CHAN_STATIC;
|
|
ep.m_pSoundName = params.soundname;
|
|
ep.m_flVolume = params.volume;
|
|
ep.m_SoundLevel = params.soundlevel;
|
|
ep.m_nPitch = params.pitch;
|
|
ep.m_pOrigin = &sound.origin;
|
|
CBaseEntity::EmitSound(filter, 0 /*sound.entityIndex*/, ep);
|
|
}
|
|
list.RemoveAll();
|
|
}
|
|
}; // namespace physicssound
|
|
|
|
#endif // VPHYSICS_SOUND_H
|