aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/physconstraint_sounds.h
blob: 71b9a5795aebd691e3b3fc6ea9c9cbcf8f7c49fe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
//========= Copyright 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