503 lines
No EOL
10 KiB
C++
503 lines
No EOL
10 KiB
C++
#include "common_headers.h"
|
|
|
|
#ifdef _IMMERSION
|
|
|
|
#include "ff_snd.h"
|
|
#include "ff.h"
|
|
|
|
extern FFSystem gFFSystem;
|
|
|
|
#define FF_GAIN_STEP 500
|
|
|
|
//
|
|
// Internal data structures
|
|
//
|
|
// This whole system should mirror snd_dma.cpp to some degree.
|
|
// Right now, not much works.
|
|
|
|
/*
|
|
template<typename T>
|
|
static T RelativeDistance( T Volume, T Min, T Max )
|
|
{
|
|
if ( Min == Max )
|
|
if ( Volume < Min )
|
|
return 0.f;
|
|
else
|
|
return 1.f;
|
|
|
|
return (Volume - Min) / (Max - Min);
|
|
};
|
|
|
|
template<typename T>
|
|
int Round( T value, int mod )
|
|
{
|
|
int intval = (int)value;
|
|
int intmod = intval % mod;
|
|
int roundup = intmod >= mod / 2;
|
|
|
|
return
|
|
( intval
|
|
? roundup
|
|
? intval + mod - intmod
|
|
: intval - intmod
|
|
: roundup
|
|
? mod
|
|
: 0
|
|
);
|
|
}
|
|
*/
|
|
|
|
class SndForce
|
|
{
|
|
public:
|
|
ffHandle_t mHandle;
|
|
int mRefs;
|
|
qboolean mPlaying;
|
|
// int mEntNum;
|
|
// vec3_t mOrigin;
|
|
// struct SDistanceLimits
|
|
// { int min;
|
|
// int max;
|
|
// } mDistance;
|
|
|
|
public:
|
|
void zero()
|
|
{
|
|
mHandle = FF_HANDLE_NULL;
|
|
mRefs = 0;
|
|
mPlaying = qfalse;
|
|
// mEntNum = 0;
|
|
// mDistance.min = 0;
|
|
// mDistance.max = 0;
|
|
// mOrigin[0] = 1.f;
|
|
// mOrigin[1] = 0.f;
|
|
// mOrigin[2] = 0.f;
|
|
}
|
|
SndForce()
|
|
{
|
|
zero();
|
|
}
|
|
SndForce( const SndForce &other )
|
|
{
|
|
memcpy( this, &other, sizeof(SndForce) );
|
|
}
|
|
SndForce( ffHandle_t handle/*, int entNum, const vec3_t origin, float maxDistance, float minDistance*/ )
|
|
: mHandle( handle )
|
|
, mRefs( 0 )
|
|
, mPlaying( qfalse )
|
|
// , mEntNum( entNum )
|
|
{
|
|
// mDistance.min = minDistance;
|
|
// mDistance.max = maxDistance;
|
|
// memcpy( mOrigin, origin, sizeof(mOrigin) );
|
|
}
|
|
void AddRef()
|
|
{
|
|
++mRefs;
|
|
}
|
|
void SubRef()
|
|
{
|
|
--mRefs;
|
|
}
|
|
qboolean Update( void ) const
|
|
{
|
|
return qboolean
|
|
( mRefs != 0
|
|
&& (ChannelCompound*)mHandle
|
|
);
|
|
}
|
|
/* int GetGain()
|
|
{
|
|
float distance = 1.f - GetRelativeDistance();
|
|
|
|
return distance == 0.f
|
|
? 10000
|
|
: Clamp<int>
|
|
( Round<int>
|
|
( distance * 10000
|
|
, FF_GAIN_STEP
|
|
)
|
|
, 0
|
|
, 10000
|
|
)
|
|
;
|
|
}
|
|
float GetRelativeDistance()
|
|
{
|
|
return !mRefs
|
|
? 1.f
|
|
: IsOrigin()
|
|
? 0.f
|
|
: RelativeDistance<float>
|
|
( sqrt
|
|
( mOrigin[0] * mOrigin[0]
|
|
+ mOrigin[1] * mOrigin[1]
|
|
+ mOrigin[2] * mOrigin[2]
|
|
)
|
|
, mDistance.min
|
|
, mDistance.max
|
|
) / mRefs
|
|
;
|
|
}
|
|
qboolean IsOrigin()
|
|
{
|
|
return qboolean
|
|
( !mOrigin[0]
|
|
&& !mOrigin[1]
|
|
&& !mOrigin[2]
|
|
);
|
|
}
|
|
void Respatialize( int entNum, const vec3_t origin )
|
|
{
|
|
extern vec3_t s_entityPosition[];
|
|
|
|
if ( mEntNum != entNum )
|
|
{
|
|
// Assumes all forces follow its entity and is centered on entity
|
|
mOrigin[0] = s_entityPosition[ entNum ][0] - origin[0];
|
|
mOrigin[1] = s_entityPosition[ entNum ][1] - origin[1];
|
|
mOrigin[2] = s_entityPosition[ entNum ][2] - origin[2];
|
|
}
|
|
else
|
|
{
|
|
memset( mOrigin, 0, sizeof(mOrigin) );
|
|
}
|
|
}*/
|
|
void operator += ( SndForce &other );
|
|
};
|
|
|
|
// Fancy comparator
|
|
struct SndForceLess : public less<SndForce>
|
|
{
|
|
bool operator() ( const SndForce &x, const SndForce &y )
|
|
{
|
|
return bool
|
|
(/* x.mEntNum < y.mEntNum
|
|
||*/x.mHandle < y.mHandle
|
|
// || x.mOrigin < y.mOrigin // uhhh... compare components
|
|
|| x.mPlaying < y.mPlaying
|
|
);
|
|
}
|
|
};
|
|
|
|
|
|
class LoopForce : public SndForce
|
|
{
|
|
public:
|
|
LoopForce(){}
|
|
LoopForce( const LoopForce &other )
|
|
{
|
|
memcpy( this, &other, sizeof(LoopForce) );
|
|
}
|
|
LoopForce( ffHandle_t handle/*int entNum, , const vec3_t origin, float maxDistance, float minDistance*/ )
|
|
: SndForce( handle/*, entNum, origin, maxDistance, minDistance*/ )
|
|
{}
|
|
|
|
void Add( ffHandle_t ff/*, int entNum, const vec3_t origin*/ );
|
|
// void Respatialize( int entNum, const vec3_t origin );
|
|
qboolean Update( void )
|
|
{
|
|
qboolean result = SndForce::Update();
|
|
mRefs = 0;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
class SndForceSet
|
|
{
|
|
public:
|
|
typedef set<SndForce, SndForceLess> PendingSet;
|
|
typedef map<ffHandle_t, SndForce> ActiveSet;
|
|
ActiveSet mActive;
|
|
PendingSet mPending;
|
|
public:
|
|
void Add( ffHandle_t handle/*, int entNum, const vec3_t origin, float maxDistance, float minDistance*/ )
|
|
{
|
|
const_cast <SndForce&> (*mPending.insert( SndForce( handle/*, entNum, origin, maxDistance, minDistance*/ ) ).first).AddRef();
|
|
}
|
|
qboolean Update( void );
|
|
/* void Respatialize( int entNum, const vec3_t origin )
|
|
{
|
|
for
|
|
( PendingSet::iterator itPending = mPending.begin()
|
|
; itPending != mPending.end()
|
|
; itPending++
|
|
){
|
|
(*itPending).Respatialize( entNum, origin );
|
|
}
|
|
}*/
|
|
};
|
|
|
|
class LoopForceSet
|
|
{
|
|
public:
|
|
typedef set<LoopForce, SndForceLess> PendingSet;
|
|
typedef map<ffHandle_t, LoopForce> ActiveSet;
|
|
ActiveSet mActive;
|
|
PendingSet mPending;
|
|
public:
|
|
void Add( ffHandle_t handle/*, int entNum, const vec3_t origin, float maxDistance, float minDistance*/ )
|
|
{
|
|
const_cast <LoopForce&>(*mPending.insert( LoopForce( handle/*, entNum, origin, maxDistance, minDistance*/ ) ).first).AddRef();
|
|
}
|
|
qboolean Update( void );
|
|
/* void Respatialize( int entNum, const vec3_t origin )
|
|
{
|
|
for
|
|
( PendingSet::iterator itPending = mPending.begin()
|
|
; itPending != mPending.end()
|
|
; itPending++
|
|
){
|
|
(*itPending).Respatialize( entNum, origin );
|
|
}
|
|
}*/
|
|
};
|
|
|
|
class MasterForceSet
|
|
{
|
|
protected:
|
|
int mEntityNum;
|
|
// vec3_t mOrigin;
|
|
SndForceSet mSnd;
|
|
LoopForceSet mLoop;
|
|
|
|
public:
|
|
void Add( ffHandle_t handle/*, int entNum, const vec3_t origin, float maxDistance, float minDistance*/ )
|
|
{
|
|
mSnd.Add( handle/*, entNum, origin, maxDistance, minDistance*/ );
|
|
}
|
|
void AddLoop( ffHandle_t handle/*, int entNum, const vec3_t origin, float maxDistance, float minDistance*/ )
|
|
{
|
|
mLoop.Add( handle/*, entNum, origin, maxDistance, minDistance*/ );
|
|
}
|
|
/* void Respatialize( int entNum, const vec3_t origin )
|
|
{
|
|
memcpy( mOrigin, origin, sizeof(mOrigin) );
|
|
mEntityNum = entNum;
|
|
mSnd.Respatialize( entNum, origin );
|
|
mLoop.Respatialize( entNum, origin );
|
|
}
|
|
*/ void Update( void );
|
|
};
|
|
|
|
//
|
|
// ===================================================================================
|
|
//
|
|
|
|
static MasterForceSet _MasterForceSet;
|
|
|
|
void FF_AddForce( ffHandle_t ff/*, int entNum, const vec3_t origin, float maxDistance, float minDistance*/ )
|
|
{
|
|
_MasterForceSet.Add( ff/*, entNum, origin, maxDistance, minDistance*/ );
|
|
}
|
|
|
|
void FF_AddLoopingForce( ffHandle_t ff/*, int entNum, const vec3_t origin, float maxDistance, float minDistance*/ )
|
|
{
|
|
_MasterForceSet.AddLoop( ff/*, entNum, origin, maxDistance, minDistance*/ );
|
|
}
|
|
/*
|
|
void FF_Respatialize( int entNum, const vec3_t origin )
|
|
{
|
|
_MasterForceSet.Respatialize( entNum, origin );
|
|
}
|
|
*/
|
|
void FF_Update( void )
|
|
{
|
|
_MasterForceSet.Update();
|
|
}
|
|
|
|
//
|
|
// ===================================================================================
|
|
//
|
|
|
|
void MasterForceSet::Update()
|
|
{
|
|
mSnd.Update();
|
|
mLoop.Update();
|
|
}
|
|
|
|
////-----------------
|
|
/// LoopForce::Update
|
|
//---------------------
|
|
// Starts/Stops/Updates looping forces.
|
|
// Call once per frame after all looping forces have been added and respatialized.
|
|
//
|
|
qboolean LoopForceSet::Update()
|
|
{
|
|
ActiveSet::iterator itActive;
|
|
PendingSet::iterator itPending;
|
|
|
|
// Sum effects
|
|
ActiveSet active;
|
|
for
|
|
( itPending = mPending.begin()
|
|
; itPending != mPending.end()
|
|
; itPending++
|
|
){
|
|
if ( (const_cast <LoopForce&> (*itPending)).Update() )
|
|
{
|
|
active[ (*itPending).mHandle ] += const_cast <LoopForce&> (*itPending) ;
|
|
}
|
|
}
|
|
|
|
// Stop and remove unreferenced effects
|
|
for
|
|
( itActive = mActive.begin()
|
|
; itActive != mActive.end()
|
|
; //itActive++
|
|
){
|
|
if ( active.find( (*itActive).first ) != active.end() )
|
|
{
|
|
itActive++;
|
|
}
|
|
else
|
|
{
|
|
SndForce &sndForce = (*itActive).second;
|
|
FF_Stop( sndForce.mHandle );
|
|
itActive = mActive.erase( itActive );
|
|
}
|
|
}
|
|
|
|
// Decide whether to start or update
|
|
for
|
|
( itActive = active.begin()
|
|
; itActive != active.end()
|
|
; itActive++
|
|
){
|
|
SndForce &sndForce = mActive[ (*itActive).first ];
|
|
sndForce.mHandle = (*itActive).first;
|
|
if ( sndForce.mPlaying )
|
|
{
|
|
// Just update it
|
|
|
|
// if ( (*itActive).second.GetGain() != sndForce.GetGain() )
|
|
// {
|
|
// gFFSystem.ChangeGain( sndForce.mHandle, sndForce.GetGain() );
|
|
// }
|
|
}
|
|
else
|
|
{
|
|
// Update and start it
|
|
|
|
// gFFSystem.ChangeGain( sndForce.mHandle, sndForce.GetGain() );
|
|
FF_Play( sndForce.mHandle );
|
|
sndForce.mPlaying = qtrue;
|
|
}
|
|
}
|
|
|
|
mPending.clear();
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
////-------------------
|
|
/// SndForceSet::Update
|
|
//-----------------------
|
|
//
|
|
//
|
|
qboolean SndForceSet::Update()
|
|
{
|
|
ActiveSet::iterator itActive;
|
|
PendingSet::iterator itPending;
|
|
/*
|
|
// Remove finished effects from active //and pending sets
|
|
for
|
|
( itActive = mActive.begin()
|
|
; itActive != mActive.end()
|
|
; //itActive++
|
|
){
|
|
if ( gFFSystem.IsPlaying( (*itActive).first ) )
|
|
{
|
|
++itActive;
|
|
}
|
|
else
|
|
{
|
|
#if( 0 )
|
|
for
|
|
( itPending = mPending.begin()
|
|
; itPending != mPending.end()
|
|
; itPending++
|
|
){
|
|
if
|
|
( (*itPending).mHandle == (*itActive).first
|
|
&& (*itPending).mPlaying
|
|
){
|
|
itPending = mPending.erase( itPending );
|
|
}
|
|
}
|
|
#endif
|
|
itActive = mActive.erase( itActive );
|
|
}
|
|
}
|
|
*/
|
|
// Sum effects
|
|
ActiveSet start;
|
|
for
|
|
( itPending = mPending.begin()
|
|
; itPending != mPending.end()
|
|
; itPending++
|
|
){
|
|
if ( (*itPending).Update() )
|
|
{
|
|
start[ (*itPending).mHandle ] += const_cast <SndForce&> (*itPending);
|
|
}
|
|
}
|
|
|
|
// Decide whether to start ( no updating one-shots )
|
|
for
|
|
( itActive = start.begin()
|
|
; itActive != start.end()
|
|
; itActive++
|
|
){
|
|
/* SndForce &sndForce = mActive[ (*itActive).first ];
|
|
sndForce.mHandle = (*itActive).first;
|
|
if ( (*itActive).second.GetGain() >= sndForce.GetGain() )
|
|
{
|
|
//gFFSystem.ChangeGain( sndForce.mHandle, sndForce.GetGain() );
|
|
FF_Start( sndForce.mHandle );
|
|
sndForce.mPlaying = qtrue;
|
|
}
|
|
*/ FF_Play( (*itActive).first );
|
|
}
|
|
|
|
mPending.clear();
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
void SndForce::operator += ( SndForce &other )
|
|
{
|
|
/*
|
|
float dist = other.GetRelativeDistance();
|
|
|
|
if ( dist < 1.f )
|
|
{
|
|
float thisdist = GetRelativeDistance();
|
|
|
|
if ( thisdist < 1.f )
|
|
{
|
|
if ( dist == 0.f || thisdist == 0.f )
|
|
{
|
|
mOrigin[0] = 0.f;
|
|
mOrigin[1] = 0.f;
|
|
mOrigin[2] = 0.f;
|
|
}
|
|
else
|
|
{
|
|
// This is so shitty
|
|
mOrigin[0] *= dist;
|
|
mOrigin[1] *= dist;
|
|
mOrigin[2] *= dist;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memcpy( mOrigin, other.mOrigin, sizeof(mOrigin) );
|
|
}
|
|
*/
|
|
|
|
mRefs += other.mRefs;
|
|
// }
|
|
}
|
|
|
|
#endif // _IMMERSION
|