//========= Copyright <EFBFBD> 1996-2007, Valve Corporation, All rights reserved. ============//
//
// Purpose: Data types used inside constraints for the purpose of playing sounds
// during movement.
//
//=============================================================================//
# ifndef PHYSCONSTRAINT_SOUNDS_H
# define PHYSCONSTRAINT_SOUNDS_H
# ifdef _WIN32
# pragma once
# endif
# include <mathlib/ssemath.h>
# include "soundenvelope.h"
/** \brief Class to store a sampled history of velocity for an object -- used for certain sound calculations
Although this contains only one sample for now , it exists as an interface
so as to make simpler the possibility of moving to a ring buffer
implementation in the future .
The " sample rate " variable is not nominal : it should be used to specify
the ClientThink ( ) interval .
Be sure to use the beginSampling ( ) function for the first sample , and
addSample ( ) thereafter : this will be relevant and necessary for a ring
buffer implementation ( which will have to perform certain initialization ) .
*/
class VelocitySampler
{
public :
/*
enum
{
HISTORY_DEPTH_LOG = 3 , // < log-base-2 of the sampler's array depth
HISTORY_DEPTH = ( 1 < < VELOCITY_SAMPLER_HISTORY_DEPTH_LOG ) ,
} ;
*/
/// Return the internally stored sample rate.
inline float getSampleRate ( )
{
return m_fIdealSampleRate ;
}
/// Store off the first recorded sample for the given object.
inline void BeginSampling ( const Vector & relativeVelocity ) ;
/// Record a sample. Do this LAST, after calling hasReversed() et al.
inline void AddSample ( const Vector & relativeVelocity ) ;
/// Using the sample history, determine if the object has reversed direction
/// with at least the given acceleration (in units/sec^2).
int HasReversed ( const Vector & relativeVelocity , const float thresholdAcceleration [ ] , const unsigned short numThresholds ) ;
/// Call this in spawn(). (Not a constructor because those are difficult to use in entities.)
void Initialize ( float samplerate ) ;
/// A convenience function for extracting the linear velocity of one object relative to another.
inline static Vector GetRelativeVelocity ( IPhysicsObject * pObj , IPhysicsObject * pReferenceFrame ) ;
/// A convenience function for extracting the angular velocity of one object relative to another.
inline static Vector GetRelativeAngularVelocity ( IPhysicsObject * pObj , IPhysicsObject * pReferenceFrame ) ;
protected :
Vector m_prevSample ;
float m_fPrevSampleTime ;
float m_fIdealSampleRate ;
} ;
struct SimpleConstraintSoundProfile
{
// define the indices of the sound points:
enum
{
kMIN_THRESHOLD , ///< below this no sound is played
kMIN_FULL , ///< at this velocity sound is at its loudest
kHIGHWATER , ///< high water mark for this enum
} eKeypoints ;
float m_keyPoints [ kHIGHWATER ] ;
/// Number of entries in the reversal sound array
enum { kREVERSAL_SOUND_ARRAY_SIZE = 3 } ;
/// Acceleration threshold for playing the hard-reverse sound. Divided into sections.
/// Below the 0th threshold no sound will play.
float m_reversalSoundThresholds [ kREVERSAL_SOUND_ARRAY_SIZE ] ;
/// Get volume for given velocity [0..1]
float GetVolume ( float inVel ) ;
} ;
float SimpleConstraintSoundProfile : : GetVolume ( float inVel )
{
// clamped lerp on 0-1
if ( inVel < = m_keyPoints [ kMIN_THRESHOLD ] )
{
return 0 ;
}
else if ( inVel > = m_keyPoints [ kMIN_FULL ] )
{
return 1 ;
}
else // lerp...
{
return ( inVel - m_keyPoints [ kMIN_THRESHOLD ] ) / ( m_keyPoints [ kMIN_FULL ] - m_keyPoints [ kMIN_THRESHOLD ] ) ;
}
}
class CPhysConstraint ;
/** This class encapsulates the data and behavior necessary for a constraint to play sounds.
For the moment I have no easy means of populating this from an entity ' s datadesc .
You should explicitly fill out the fields with eg
DEFINE_KEYFIELD ( m_soundInfo . m_soundProfile . m_keyPoints [ SimpleConstraintSoundProfile : : kMIN_THRESHOLD ] , FIELD_FLOAT , " minSoundThreshold " ) ,
DEFINE_KEYFIELD ( m_soundInfo . m_soundProfile . m_keyPoints [ SimpleConstraintSoundProfile : : kMIN_FULL ] , FIELD_FLOAT , " maxSoundThreshold " ) ,
DEFINE_KEYFIELD ( m_soundInfo . m_iszTravelSoundFwd , FIELD_SOUNDNAME , " slidesoundfwd " ) ,
DEFINE_KEYFIELD ( m_soundInfo . m_iszTravelSoundBack , FIELD_SOUNDNAME , " slidesoundback " ) ,
DEFINE_KEYFIELD ( m_soundInfo . m_iszReversalSound , FIELD_SOUNDNAME , " reversalsound " ) ,
DEFINE_KEYFIELD ( m_soundInfo . m_soundProfile . m_reversalSoundThreshold , FIELD_FLOAT , " reversalsoundthreshold " ) ,
*/
class ConstraintSoundInfo
{
public :
// no ctor.
// dtor
~ ConstraintSoundInfo ( ) ;
/// Call from the constraint's Activate()
void OnActivate ( CPhysConstraint * pOuter ) ;
/// Constraint should have a think function that calls this. It should pass in relative velocity
/// between child and parent. (This need not be linear velocity; it may be angular.)
void OnThink ( CPhysConstraint * pOuter , const Vector & relativeVelocity ) ;
/// This is how often the think function should be run:
inline float getThinkRate ( ) const { return 0.09f ; }
/// Call this before the first call to OnThink()
void StartThinking ( CPhysConstraint * pOuter , const Vector & relativeVelocity , const Vector & forwardVector ) ;
/// Call this if you intend to stop calling OnThink():
void StopThinking ( CPhysConstraint * pOuter ) ;
/// Call from owner's Precache().
void OnPrecache ( CPhysConstraint * pOuter ) ;
VelocitySampler m_vSampler ;
SimpleConstraintSoundProfile m_soundProfile ;
Vector m_forwardAxis ; ///< velocity in this direction is forward. The opposite direction is backward.
string_t m_iszTravelSoundFwd , m_iszTravelSoundBack ; // Path/filename of WAV file to play.
CSoundPatch * m_pTravelSound ;
bool m_bPlayTravelSound ;
string_t m_iszReversalSounds [ SimpleConstraintSoundProfile : : kREVERSAL_SOUND_ARRAY_SIZE ] ; // Path/filename of WAV files to play -- one per entry in threshold.
// CSoundPatch *m_pReversalSound;
bool m_bPlayReversalSound ;
protected :
/// Maintain consistency of internal datastructures on start
void ValidateInternals ( CPhysConstraint * pOuter ) ;
/// Stop playing any active sounds.
void DeleteAllSounds ( ) ;
} ;
/////////////// INLINE FUNCTIONS
/// compute the relative velocity between an object and its parent. Just a convenience.
Vector VelocitySampler : : GetRelativeVelocity ( IPhysicsObject * pObj , IPhysicsObject * pReferenceFrame )
{
Vector childVelocity , parentVelocity ;
pObj - > GetImplicitVelocity ( & childVelocity , NULL ) ;
pReferenceFrame - > GetImplicitVelocity ( & parentVelocity , NULL ) ;
return ( childVelocity - parentVelocity ) ;
}
Vector VelocitySampler : : GetRelativeAngularVelocity ( IPhysicsObject * pObj , IPhysicsObject * pReferenceFrame )
{
Assert ( pObj ) ;
if ( pReferenceFrame )
{
Vector childVelocityLocal , parentVelocityLocal , childVelocityWorld , parentVelocityWorld ;
pObj - > GetImplicitVelocity ( NULL , & childVelocityLocal ) ;
pObj - > LocalToWorldVector ( & childVelocityWorld , childVelocityLocal ) ;
pReferenceFrame - > GetImplicitVelocity ( NULL , & parentVelocityLocal ) ;
pObj - > LocalToWorldVector ( & parentVelocityWorld , parentVelocityLocal ) ;
return ( childVelocityWorld - parentVelocityWorld ) ;
}
else
{
Vector childVelocityLocal , childVelocityWorld ;
pObj - > GetImplicitVelocity ( NULL , & childVelocityLocal ) ;
pObj - > LocalToWorldVector ( & childVelocityWorld , childVelocityLocal ) ;
return ( childVelocityWorld ) ;
}
}
/************************************************************************/
// This function is nominal -- it's here as an interface because in the
// future there will need to be special initialization for the first entry
// in a ring buffer. (I made a test implementation of this, then reverted it
// later; this is not an arbitrary assumption.)
/************************************************************************/
/// Store off the first recorded sample for the given object.
void VelocitySampler : : BeginSampling ( const Vector & relativeVelocity )
{
return AddSample ( relativeVelocity ) ;
}
// Record a sample for the given object
void VelocitySampler : : AddSample ( const Vector & relativeVelocity )
{
m_prevSample = relativeVelocity ;
m_fPrevSampleTime = gpGlobals - > curtime ;
}
/* // abandoned -- too complicated, no way to set from keyfields
# pragma warning(push)
# pragma warning( disable:4201 ) // C4201: nonstandard extension used: nameless struct/union
/// Stores information used for playing sounds based on
/// constraint movement
class ConstraintSoundProfile
{
public :
/// Defines a point in the sound profile: volume and pitch for the sound to play.
/// Implicit crossfading between two sounds. Used to map velocity to a sound profile.
struct SoundInfoTuple
{
float minVelocity ;
union {
struct {
float volume1 , pitch1 ; //< volume and pitch of sound 1
float volume2 , pitch2 ; //< volume and pitch of sound 2
} ;
fltx4 m_as4 ;
} ;
inline SoundInfoTuple ( float _minVelocity , float _volume1 , float _pitch1 , float _volume2 , float _pitch2 ) :
minVelocity ( _minVelocity ) , volume1 ( _volume1 ) , pitch1 ( _pitch1 ) , volume2 ( _volume2 ) , pitch2 ( _pitch2 )
{ }
} ;
ConstraintSoundProfile ( const SoundInfoTuple * soundTable , unsigned int tableSize )
: m_pSoundInfos ( soundTable ) , m_numSoundInfos ( tableSize )
{ }
protected :
/// A table of sound info structs
const SoundInfoTuple * const m_pSoundInfos ;
/// Size of the table
const unsigned int m_numSoundInfos ;
} ;
static ConstraintSoundProfile : : SoundInfoTuple CSDebugProfileTable [ ] =
{
ConstraintSoundProfile : : SoundInfoTuple ( 12 , 0 , 0 , 0 , 0 ) ,
ConstraintSoundProfile : : SoundInfoTuple ( 24 , 0 , 0 , 0 , 0 ) ,
} ;
# pragma warning(pop)
*/
# endif