Quake 4 integrated with DoomRTX

This commit is contained in:
Justin Marshall
2026-05-09 22:10:40 -07:00
parent 8c4a087aa9
commit d37f87e493
436 changed files with 277587 additions and 10254 deletions
+696
View File
@@ -0,0 +1,696 @@
//----------------------------------------------------------------
// ClientAFEntity.cpp
//
// Copyright 2002-2006 Raven Software
//----------------------------------------------------------------
#include "precompiled.h"
#pragma hdrstop
/*
===============================================================================
rvClientAFEntity
===============================================================================
*/
CLASS_DECLARATION( rvAnimatedClientEntity, rvClientAFEntity )
END_CLASS
static const float BOUNCE_SOUND_MIN_VELOCITY = 80.0f;
static const float BOUNCE_SOUND_MAX_VELOCITY = 200.0f;
/*
================
rvClientAFEntity::rvClientAFEntity
================
*/
rvClientAFEntity::rvClientAFEntity( void ) {
combatModel = NULL;
combatModelContents = 0;
nextSoundTime = 0;
spawnOrigin.Zero();
spawnAxis.Identity();
}
/*
================
idAFEntity_Base::~idAFEntity_Base
================
*/
rvClientAFEntity::~rvClientAFEntity( void ) {
delete combatModel;
combatModel = NULL;
}
/*
================
rvClientAFEntity::Spawn
================
*/
void rvClientAFEntity::Spawn( void ) {
spawnOrigin = worldOrigin;
spawnAxis = worldAxis;
nextSoundTime = 0;
//if ( !LoadAF() ) {
// gameLocal.Error( "Couldn't load af file on entity %d", entityNumber );
//}
SetCombatModel();
//af.GetPhysics()->PutToRest();
//if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
// af.GetPhysics()->Activate();
//}
}
/*
================
rvClientAFEntity::LoadAF
================
*/
bool rvClientAFEntity::LoadAF( const char* keyname ) {
idStr fileName;
if ( !keyname || !*keyname ) {
keyname = "articulatedFigure";
}
if ( !spawnArgs.GetString( keyname, "*unknown*", fileName ) ) {
return false;
}
af.SetAnimator( GetAnimator() );
idDict args = gameLocal.entities[ ENTITYNUM_CLIENT ]->spawnArgs;
gameLocal.entities[ ENTITYNUM_CLIENT ]->spawnArgs = spawnArgs;
if ( !af.Load( gameLocal.entities[ ENTITYNUM_CLIENT ], fileName ) ) {
gameLocal.Error( "rvClientAFEntity::LoadAF: Couldn't load af file '%s' on client entity %d", fileName.c_str(), entityNumber );
}
gameLocal.entities[ ENTITYNUM_CLIENT ]->spawnArgs = args;
af.Start();
af.GetPhysics()->Rotate( spawnAxis.ToRotation() );
af.GetPhysics()->Translate( spawnOrigin );
LoadState( spawnArgs );
af.UpdateAnimation();
animator.CreateFrame( gameLocal.time, true );
UpdateVisuals();
return true;
}
/*
================
rvClientAFEntity::Think
================
*/
void rvClientAFEntity::Think( void ) {
RunPhysics();
UpdateAnimation();
Present();
LinkCombat();
}
/*
================
rvClientAFEntity::BodyForClipModelId
================
*/
int rvClientAFEntity::BodyForClipModelId( int id ) const {
return af.BodyForClipModelId( id );
}
/*
================
rvClientAFEntity::SaveState
================
*/
void rvClientAFEntity::SaveState( idDict &args ) const {
const idKeyValue *kv;
// save the ragdoll pose
af.SaveState( args );
// save all the bind constraints
kv = spawnArgs.MatchPrefix( "bindConstraint ", NULL );
while ( kv ) {
args.Set( kv->GetKey(), kv->GetValue() );
kv = spawnArgs.MatchPrefix( "bindConstraint ", kv );
}
// save the bind if it exists
kv = spawnArgs.FindKey( "bind" );
if ( kv ) {
args.Set( kv->GetKey(), kv->GetValue() );
}
kv = spawnArgs.FindKey( "bindToJoint" );
if ( kv ) {
args.Set( kv->GetKey(), kv->GetValue() );
}
kv = spawnArgs.FindKey( "bindToBody" );
if ( kv ) {
args.Set( kv->GetKey(), kv->GetValue() );
}
}
/*
================
rvClientAFEntity::LoadState
================
*/
void rvClientAFEntity::LoadState( const idDict &args ) {
af.LoadState( args );
}
/*
================
rvClientAFEntity::AddBindConstraints
================
*/
void rvClientAFEntity::AddBindConstraints( void ) {
af.AddBindConstraints();
}
/*
================
rvClientAFEntity::RemoveBindConstraints
================
*/
void rvClientAFEntity::RemoveBindConstraints( void ) {
af.RemoveBindConstraints();
}
/*
================
rvClientAFEntity::GetImpactInfo
================
*/
void rvClientAFEntity::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
if ( af.IsActive() ) {
af.GetImpactInfo( ent, id, point, info );
} else {
rvClientEntity::GetImpactInfo( ent, id, point, info );
}
}
/*
================
rvClientAFEntity::ApplyImpulse
================
*/
void rvClientAFEntity::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse, bool splash ) {
if ( af.IsLoaded() ) {
af.ApplyImpulse( ent, id, point, impulse );
}
if ( !af.IsActive() ) {
rvClientEntity::ApplyImpulse( ent, id, point, impulse, splash );
}
}
/*
================
rvClientAFEntity::AddForce
================
*/
void rvClientAFEntity::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
if ( af.IsLoaded() ) {
af.AddForce( ent, id, point, force );
}
if ( !af.IsActive() ) {
rvClientEntity::AddForce( ent, id, point, force );
}
}
bool rvClientAFEntity::CanPlayImpactEffect ( idEntity* attacker, idEntity* target ) {
// stubbed out
return true;
}
/*
================
rvClientAFEntity::Collide
================
*/
bool rvClientAFEntity::Collide( const trace_t &collision, const idVec3 &velocity ) {
float v, f;
if ( af.IsActive() ) {
v = -( velocity * collision.c.normal );
if ( v > BOUNCE_SOUND_MIN_VELOCITY && gameLocal.time > nextSoundTime ) {
// RAVEN BEGIN
// jscott: fixed negative sqrt call
if( v > BOUNCE_SOUND_MAX_VELOCITY ) {
f = 1.0f;
} else if( v <= BOUNCE_SOUND_MIN_VELOCITY ) {
f = 0.0f;
} else {
f = ( v - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / ( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) );
}
// RAVEN END
if ( StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, false, NULL ) ) {
// don't set the volume unless there is a bounce sound as it overrides the entire channel
// which causes footsteps on ai's to not honor their shader parms
SetSoundVolume( f );
}
nextSoundTime = gameLocal.time + 500;
}
}
return false;
}
/*
================
rvClientAFEntity::GetPhysicsToVisualTransform
================
*/
bool rvClientAFEntity::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
if ( af.IsActive() ) {
af.GetPhysicsToVisualTransform( origin, axis );
return true;
}
return rvClientModel::GetPhysicsToVisualTransform( origin, axis );
}
/*
================
rvClientAFEntity::UpdateAnimationControllers
================
*/
bool rvClientAFEntity::UpdateAnimationControllers( void ) {
if ( af.IsActive() ) {
if ( af.UpdateAnimation() ) {
return true;
}
}
return false;
}
/*
================
rvClientAFEntity::SetCombatModel
================
*/
void rvClientAFEntity::SetCombatModel( void ) {
if ( combatModel ) {
combatModel->Unlink();
combatModel->LoadModel( entityDefHandle );
} else {
RV_PUSH_HEAP_MEM(this);
combatModel = new idClipModel( entityDefHandle );
RV_POP_HEAP();
}
}
/*
================
rvClientAFEntity::GetCombatModel
================
*/
idClipModel *rvClientAFEntity::GetCombatModel( void ) const {
return combatModel;
}
/*
================
rvClientAFEntity::SetCombatContents
================
*/
void rvClientAFEntity::SetCombatContents( bool enable ) {
assert( combatModel );
if ( enable && combatModelContents ) {
assert( !combatModel->GetContents() );
combatModel->SetContents( combatModelContents );
combatModelContents = 0;
} else if ( !enable && combatModel->GetContents() ) {
assert( !combatModelContents );
combatModelContents = combatModel->GetContents();
combatModel->SetContents( 0 );
}
}
/*
================
rvClientAFEntity::LinkCombat
================
*/
void rvClientAFEntity::LinkCombat( void ) {
if ( combatModel ) {
combatModel->Link( gameLocal.entities[ ENTITYNUM_CLIENT ], 0, renderEntity.origin, renderEntity.axis, entityDefHandle );
}
}
/*
================
rvClientAFEntity::UnlinkCombat
================
*/
void rvClientAFEntity::UnlinkCombat( void ) {
if ( combatModel ) {
combatModel->Unlink();
}
}
/*
================
rvClientAFEntity::FreeEntityDef
================
*/
void rvClientAFEntity::FreeEntityDef( void ) {
UnlinkCombat();
rvClientEntity::FreeEntityDef();
}
/*
================
rvClientAFEntity::GetNoPlayerImpactFX
================
*/
bool rvClientAFEntity::GetNoPlayerImpactFX( void ) {
// stubbed out
return false;
}
/*
===============================================================================
idAFAttachment
===============================================================================
*/
CLASS_DECLARATION( rvClientAFEntity, rvClientAFAttachment )
END_CLASS
/*
=====================
rvClientAFAttachment::rvClientAFAttachment
=====================
*/
rvClientAFAttachment::rvClientAFAttachment( void ) {
body = NULL;
combatModel = NULL;
idleAnim = 0;
damageJoint = INVALID_JOINT;
}
/*
=====================
rvClientAFAttachment::~rvClientAFAttachment
=====================
*/
rvClientAFAttachment::~rvClientAFAttachment( void ) {
delete combatModel;
combatModel = NULL;
}
/*
=====================
rvClientAFAttachment::Spawn
=====================
*/
void rvClientAFAttachment::Spawn( void ) {
idleAnim = animator.GetAnim( "idle" );
}
/*
=====================
rvClientAFAttachment::InitCopyJoints
=====================
*/
void rvClientAFAttachment::InitCopyJoints ( void ) {
copyJoints_t copyJoint;
const idKeyValue* kv;
const char* jointName;
idAnimator* bodyAnimator;
if ( !body ) {
return;
}
bodyAnimator = body->GetAnimator ( );
// set up the list of joints to copy to the head
for( kv = spawnArgs.MatchPrefix( "copy_joint", NULL ); kv != NULL; kv = spawnArgs.MatchPrefix( "copy_joint", kv ) ) {
if ( kv->GetValue() == "" ) {
// probably clearing out inherited key, so skip it
continue;
}
if ( !body->spawnArgs.GetString ( va("copy_joint_world %s", kv->GetValue().c_str() ), kv->GetValue().c_str(), &jointName ) ) {
copyJoint.mod = JOINTMOD_LOCAL_OVERRIDE;
body->spawnArgs.GetString ( va("copy_joint %s", kv->GetValue().c_str() ), kv->GetValue().c_str(), &jointName );
} else {
copyJoint.mod = JOINTMOD_WORLD_OVERRIDE;
}
copyJoint.from = bodyAnimator->GetJointHandle ( jointName );
if ( copyJoint.from == INVALID_JOINT ) {
gameLocal.Warning( "Unknown copy_joint '%s' on client entity %d", jointName, entityNumber );
continue;
}
copyJoint.to = animator.GetJointHandle( kv->GetValue() );
if ( copyJoint.to == INVALID_JOINT ) {
gameLocal.Warning( "Unknown copy_joint '%s' on head of entity %d", kv->GetValue().c_str(), entityNumber );
continue;
}
copyJoints.Append( copyJoint );
}
}
/*
=====================
rvClientAFAttachment::CopyJointsFromBody
=====================
*/
void rvClientAFAttachment::CopyJointsFromBody ( void ) {
MEM_SCOPED_TAG(tag,MA_ANIM);
idAnimator* bodyAnimator;
int i;
idMat3 mat;
idMat3 axis;
idVec3 pos;
if ( !body ) {
return;
}
bodyAnimator = body->GetAnimator();
// copy the animation from the body to the head
for( i = 0; i < copyJoints.Num(); i++ ) {
if ( copyJoints[ i ].mod == JOINTMOD_WORLD_OVERRIDE ) {
mat = GetPhysics()->GetAxis().Transpose();
body->GetJointWorldTransform( copyJoints[ i ].from, gameLocal.time, pos, axis );
pos -= GetPhysics()->GetOrigin();
animator.SetJointPos( copyJoints[ i ].to, copyJoints[ i ].mod, pos * mat );
animator.SetJointAxis( copyJoints[ i ].to, copyJoints[ i ].mod, axis * mat );
} else {
bodyAnimator->GetJointLocalTransform( copyJoints[ i ].from, gameLocal.time, pos, axis );
animator.SetJointPos( copyJoints[ i ].to, copyJoints[ i ].mod, pos );
animator.SetJointAxis( copyJoints[ i ].to, copyJoints[ i ].mod, axis );
}
}
}
/*
=====================
rvClientAFAttachment::SetBody
=====================
*/
void rvClientAFAttachment::SetBody( idAnimatedEntity *bodyEnt, const char *model, jointHandle_t _damageJoint ) {
body = bodyEnt;
damageJoint = _damageJoint;
SetModel( model );
spawnArgs.SetBool( "bleed", body->spawnArgs.GetBool( "bleed" ) );
}
/*
=====================
rvClientAFAttachment::ClearBody
=====================
*/
void rvClientAFAttachment::ClearBody( void ) {
body = NULL;
damageJoint = INVALID_JOINT;
Hide();
}
/*
=====================
rvClientAFAttachment::GetBody
=====================
*/
idEntity *rvClientAFAttachment::GetBody( void ) const {
return body;
}
/*
================
idAFAttachment::Hide
================
*/
void rvClientAFAttachment::Hide( void ) {
UnlinkCombat();
}
/*
================
idAFAttachment::Show
================
*/
void rvClientAFAttachment::Show( void ) {
LinkCombat();
}
/*
================
idAFAttachment::AddDamageEffect
================
*/
void rvClientAFAttachment::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName, idEntity* inflictor ) {
if ( body ) {
trace_t c = collision;
c.c.id = JOINT_HANDLE_TO_CLIPMODEL_ID( damageJoint );
body->AddDamageEffect( c, velocity, damageDefName, inflictor );
}
}
/*
================
rvClientAFAttachment::GetImpactInfo
================
*/
void rvClientAFAttachment::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
if ( body ) {
body->GetImpactInfo( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( damageJoint ), point, info );
} else {
rvClientAFAttachment::GetImpactInfo( ent, id, point, info );
}
}
/*
================
rvClientAFAttachment::CanPlayImpactEffect
================
*/
bool rvClientAFAttachment::CanPlayImpactEffect ( idEntity* attacker, idEntity* target ) {
return rvClientAFEntity::CanPlayImpactEffect( attacker, target );
}
/*
================
rvClientAFAttachment::ApplyImpulse
================
*/
void rvClientAFAttachment::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse, bool splash ) {
if ( body ) {
body->ApplyImpulse( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( damageJoint ), point, impulse );
} else {
rvClientAFEntity::ApplyImpulse( ent, id, point, impulse );
}
}
/*
================
rvClientAFAttachment::AddForce
================
*/
void rvClientAFAttachment::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
if ( body ) {
body->AddForce( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( damageJoint ), point, force );
} else {
rvClientAFEntity::AddForce( ent, id, point, force );
}
}
/*
================
rvClientAFAttachment::PlayIdleAnim
================
*/
void rvClientAFAttachment::PlayIdleAnim( int channel, int blendTime ) {
if ( idleAnim && ( idleAnim != animator.CurrentAnim( channel )->AnimNum() ) ) {
animator.CycleAnim( channel, idleAnim, gameLocal.time, blendTime );
}
}
/*
================
rvClientAFAttachment::Think
================
*/
void rvClientAFAttachment::Think( void ) {
rvClientAFEntity::Think();
}
/*
================
rvClientAFAttachment::UpdateAnimationControllers
================
*/
bool rvClientAFAttachment::UpdateAnimationControllers( void ) {
CopyJointsFromBody( );
return rvClientAFEntity::UpdateAnimationControllers( );
}
/*
================
rvClientAFAttachment::SetCombatModel
================
*/
void rvClientAFAttachment::SetCombatModel( void ) {
if ( combatModel ) {
combatModel->Unlink();
combatModel->LoadModel( entityDefHandle );
} else {
RV_PUSH_HEAP_MEM(this);
combatModel = new idClipModel( entityDefHandle );
RV_POP_HEAP();
}
combatModel->SetOwner( body );
}
/*
================
rvClientAFAttachment::GetCombatModel
================
*/
idClipModel *rvClientAFAttachment::GetCombatModel( void ) const {
return combatModel;
}
/*
================
rvClientAFAttachment::LinkCombat
================
*/
void rvClientAFAttachment::LinkCombat( void ) {
if ( combatModel ) {
combatModel->Link( gameLocal.entities[ ENTITYNUM_CLIENT ], 0, renderEntity.origin, renderEntity.axis, entityDefHandle );
}
}
/*
================
rvClientAFAttachment::UnlinkCombat
================
*/
void rvClientAFAttachment::UnlinkCombat( void ) {
if ( combatModel ) {
combatModel->Unlink();
}
}
+126
View File
@@ -0,0 +1,126 @@
//----------------------------------------------------------------
// ClientAFEntity.h
//
// Copyright 2002-2006 Raven Software
//----------------------------------------------------------------
#ifndef __GAME_CLIENT_AFENTITY_H__
#define __GAME_CLIENT_AFENTITY_H__
/*
===============================================================================
rvClientAFEntity - a regular idAFEntity_Base spawned client-side
===============================================================================
*/
class rvClientAFEntity : public rvAnimatedClientEntity {
public:
CLASS_PROTOTYPE( rvClientAFEntity );
rvClientAFEntity( void );
virtual ~rvClientAFEntity( void );
void Spawn( void );
virtual void Think( void );
virtual void GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info );
virtual void ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse, bool splash = false );
virtual void AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force );
virtual bool CanPlayImpactEffect ( idEntity* attacker, idEntity* target );
virtual bool Collide( const trace_t &collision, const idVec3 &velocity );
virtual bool GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis );
virtual bool UpdateAnimationControllers( void );
virtual void FreeEntityDef( void );
virtual bool LoadAF( const char* keyname = NULL );
bool IsActiveAF( void ) const { return af.IsActive(); }
const char * GetAFName( void ) const { return af.GetName(); }
idPhysics_AF * GetAFPhysics( void ) { return af.GetPhysics(); }
void SetCombatModel( void );
idClipModel * GetCombatModel( void ) const;
// contents of combatModel can be set to 0 or re-enabled (mp)
void SetCombatContents( bool enable );
virtual void LinkCombat( void );
virtual void UnlinkCombat( void );
int BodyForClipModelId( int id ) const;
void SaveState( idDict &args ) const;
void LoadState( const idDict &args );
void AddBindConstraints( void );
void RemoveBindConstraints( void );
bool GetNoPlayerImpactFX( void );
protected:
idAF af; // articulated figure
idClipModel * combatModel; // render model for hit detection
int combatModelContents;
idVec3 spawnOrigin; // spawn origin
idMat3 spawnAxis; // rotation axis used when spawned
int nextSoundTime; // next time this can make a sound
};
/*
===============================================================================
rvClientAFAttachment - a regular idAFAttachment spawned client-side - links to an
idAnimatedEntity rather than rvClientAnimatedEntity
===============================================================================
*/
class rvClientAFAttachment : public rvClientAFEntity {
public:
CLASS_PROTOTYPE( rvClientAFAttachment );
rvClientAFAttachment( void );
virtual ~rvClientAFAttachment( void );
void Spawn( void );
void SetBody ( idAnimatedEntity* body, const char *headModel, jointHandle_t damageJoint );
void SetDamageJoint ( jointHandle_t damageJoint );
void ClearBody ( void );
idEntity * GetBody ( void ) const;
virtual void Think ( void );
virtual void Hide ( void );
virtual void Show ( void );
virtual bool UpdateAnimationControllers ( void );
void PlayIdleAnim( int channel, int blendTime );
virtual void GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info );
virtual void ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse, bool splash = false );
virtual void AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force );
virtual bool CanPlayImpactEffect ( idEntity* attacker, idEntity* target );
virtual void AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName, idEntity* inflictor );
void SetCombatModel( void );
idClipModel * GetCombatModel( void ) const;
virtual void LinkCombat( void );
virtual void UnlinkCombat( void );
// Lipsync
void InitCopyJoints ( void );
void CopyJointsFromBody ( void );
protected:
idEntityPtr<idAnimatedEntity> body;
idClipModel * combatModel; // render model for hit detection of head
int idleAnim;
jointHandle_t damageJoint;
idList<copyJoints_t> copyJoints; // copied from the body animation to the head model
};
#endif
+497
View File
@@ -0,0 +1,497 @@
//----------------------------------------------------------------
// ClientEffect.cpp
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
#include "ClientEffect.h"
/*
===============================================================================
rvClientEffect
===============================================================================
*/
CLASS_DECLARATION( rvClientEntity, rvClientEffect )
END_CLASS
/*
================
rvClientEffect::rvClientEffect
================
*/
rvClientEffect::rvClientEffect ( void ) {
Init( NULL );
Spawn();
}
rvClientEffect::rvClientEffect ( const idDecl *effect ) {
Init( effect );
Spawn();
}
void rvClientEffect::Init ( const idDecl *effect ) {
memset( &renderEffect, 0, sizeof( renderEffect ) );
renderEffect.declEffect = effect;
renderEffect.startTime = -1.0f;
renderEffect.referenceSoundHandle = -1;
effectDefHandle = -1;
endOriginJoint = INVALID_JOINT;
}
/*
================
rvClientEffect::~rvClientEffect
================
*/
rvClientEffect::~rvClientEffect( void ) {
FreeEffectDef( );
// Prevent a double free of a SoundEmitter resulting in broken in-game sounds, when
// the second free releases a emitter that was reallocated to another sound. rvBSE caches
// this referenceSoundHandle and rvBSE::Destroy also frees the sound. rvBSE::Destroy
// is triggered by FreeEffectDef. Disable this free and let rvBSE do the releasing
// Actually, the freeing should be done here and not in BSE. The client effect allocates and
// maintains the handle. Handling this here also allows emitters to be recycled for sparse
// looping effects.
soundSystem->FreeSoundEmitter( SOUNDWORLD_GAME, renderEffect.referenceSoundHandle, true );
renderEffect.referenceSoundHandle = -1;
}
/*
================
rvClientEffect::GetEffectIndex
================
*/
int rvClientEffect::GetEffectIndex( void )
{
if( renderEffect.declEffect ) {
return( renderEffect.declEffect->Index() );
}
return( -1 );
}
/*
================
rvClientEffect::GetEffectName
================
*/
const char *rvClientEffect::GetEffectName( void )
{
if( renderEffect.declEffect ) {
return( renderEffect.declEffect->GetName() );
}
return( "unknown" );
}
/*
================
rvClientEffect::FreeEffectDef
================
*/
void rvClientEffect::FreeEffectDef ( void ) {
if ( effectDefHandle != -1 && gameRenderWorld ) {
gameRenderWorld->FreeEffectDef( effectDefHandle );
}
effectDefHandle = -1;
}
/*
================
rvClientEffect::UpdateBind
================
*/
void rvClientEffect::UpdateBind ( void ) {
rvClientEntity::UpdateBind ( );
renderEffect.origin = worldOrigin;
if ( endOriginJoint != INVALID_JOINT && bindMaster ) {
idMat3 axis;
idVec3 endOrigin;
idVec3 dir;
static_cast<idAnimatedEntity*>(bindMaster.GetEntity())->GetJointWorldTransform ( endOriginJoint, gameLocal.time, endOrigin, axis );
SetEndOrigin ( endOrigin );
dir = (endOrigin - worldOrigin);
dir.Normalize ();
renderEffect.axis = dir.ToMat3 ( );
} else {
renderEffect.axis = worldAxis;
}
if ( bindMaster ) {
renderEffect.groupID = bindMaster->entityNumber + 1;
} else {
renderEffect.groupID = 0;
}
}
/*
================
rvClientEffect::Think
================
*/
void rvClientEffect::Think ( void ) {
// If we are bound to an entity that is now hidden we can just not render if looping, otherwise stop the effect
if( bindMaster && (bindMaster->GetRenderEntity()->hModel && bindMaster->GetModelDefHandle() == -1) ) {
if ( renderEffect.loop ) {
return;
}
Stop ( );
}
// RAVEN BEGIN
// jnewquist: Tag scope and callees to track allocations using "new".
MEM_SCOPED_TAG(tag,MA_EFFECT);
// RAVEN END
// If there is a valid effect handle and we havent started playing
// and effect yet then see if its time
if( effectDefHandle < 0 && renderEffect.declEffect ) {
if( renderEffect.startTime >= 0.0f ) {
// Make sure our origins are all straight before starting the effect
UpdateBind();
renderEffect.attenuation = 1.0f;
// if the rendereffect needs sound give it an emitter.
if( renderEffect.referenceSoundHandle <= 0 ) {
if( gameRenderWorld->EffectDefHasSound( &renderEffect) )
{
renderEffect.referenceSoundHandle = soundSystem->AllocSoundEmitter( SOUNDWORLD_GAME );
} else {
renderEffect.referenceSoundHandle = -1;
}
}
// Add the render effect
effectDefHandle = gameRenderWorld->AddEffectDef( &renderEffect, gameLocal.time );
if ( effectDefHandle < 0 ) {
PostEventMS( &EV_Remove, 0 );
}
}
return;
}
// If we lost our effect def handle then just remove ourself
if( effectDefHandle < 0 ) {
PostEventMS ( &EV_Remove, 0 );
return;
}
// Dont do anything else if its not a new client frame
if( !gameLocal.isNewFrame ) {
return;
}
// Check to see if the player can possibly see the effect or not
renderEffect.inConnectedArea = true;
if( bindMaster ) {
renderEffect.inConnectedArea = gameLocal.InPlayerConnectedArea( bindMaster );
}
// Update the bind
UpdateBind();
// Update the actual render effect now
if( gameRenderWorld->UpdateEffectDef( effectDefHandle, &renderEffect, gameLocal.time ) ) {
FreeEffectDef ( );
PostEventMS( &EV_Remove, 0 );
return;
}
}
/*
================
rvClientEffect::Play
================
*/
bool rvClientEffect::Play ( int _startTime, bool _loop, const idVec3& endOrigin ) {
if ( !renderEffect.declEffect ) {
return false;
}
// Initialize the render entity
if ( bindMaster ) {
renderEntity_t* renderEnt = bindMaster->GetRenderEntity ( );
assert( renderEnt );
// Copy suppress values from parent entity
renderEffect.allowSurfaceInViewID = renderEnt->allowSurfaceInViewID;
renderEffect.suppressSurfaceInViewID = renderEnt->suppressSurfaceInViewID;
renderEffect.weaponDepthHackInViewID = renderEnt->weaponDepthHackInViewID;
}
renderEffect.shaderParms[SHADERPARM_RED] = 1.0f;
renderEffect.shaderParms[SHADERPARM_GREEN] = 1.0f;
renderEffect.shaderParms[SHADERPARM_BLUE] = 1.0f;
renderEffect.shaderParms[SHADERPARM_ALPHA] = 1.0f;
renderEffect.shaderParms[SHADERPARM_BRIGHTNESS] = 1.0f;
renderEffect.shaderParms[SHADERPARM_TIMEOFFSET] = MS2SEC( gameLocal.time );
renderEffect.hasEndOrigin = ( endOrigin != vec3_origin );
renderEffect.endOrigin = endOrigin;
renderEffect.loop = _loop;
assert( effectDefHandle < 0 );
renderEffect.startTime = MS2SEC( _startTime );
return true;
}
/*
================
rvClientEffect::Stop
================
*/
void rvClientEffect::Stop ( bool destroyParticles ) {
if( effectDefHandle < 0 ) {
renderEffect.startTime = -1.0f;
renderEffect.declEffect = NULL;
return;
}
if ( destroyParticles ) {
// Clear the effect index to make sure the effect isnt started again. This is
// an indirect way of making the effect not think
renderEffect.declEffect = NULL;
FreeEffectDef ( );
PostEventMS( &EV_Remove, 0 );
} else {
gameRenderWorld->StopEffectDef( effectDefHandle );
// this will ensure the effect doesn't re-up when loaded from a save.
renderEffect.startTime = -1.0f;
Unbind ( );
}
}
/*
================
rvClientEffect::Restart
================
*/
void rvClientEffect::Restart ( void ) {
FreeEffectDef ( );
if ( renderEffect.loop ) {
Play ( gameLocal.time, true, renderEffect.endOrigin );
}
}
/*
================
rvClientEffect::Attenuate
================
*/
void rvClientEffect::Attenuate ( float attenuation ) {
renderEffect.attenuation = attenuation;
}
/*
================
rvClientEffect::GetDuration
================
*/
float rvClientEffect::GetDuration( void ) const {
if( effectDefHandle < 0 ) {
return 0.0f;
}
return bse->EffectDuration( gameRenderWorld->GetEffectDef( effectDefHandle ) );
}
/*
================
rvClientEffect::DrawDebugInfo
================
*/
void rvClientEffect::DrawDebugInfo ( void ) const {
rvClientEntity::DrawDebugInfo ( );
if ( !gameLocal.GetLocalPlayer() ) {
return;
}
if( !renderEffect.declEffect ) {
return;
}
idMat3 axis = gameLocal.GetLocalPlayer()->viewAngles.ToMat3();
idVec3 up = axis[ 2 ] * 5.0f;
gameRenderWorld->DrawText ( renderEffect.declEffect->GetName(), worldOrigin + up, 0.1f, colorWhite, axis, 1 );
}
/*
================
rvClientEffect::Save
================
*/
void rvClientEffect::Save( idSaveGame *savefile ) const {
savefile->WriteRenderEffect( renderEffect );
savefile->WriteJoint( endOriginJoint );
}
/*
================
rvClientEffect::Restore
================
*/
void rvClientEffect::Restore( idRestoreGame *savefile ) {
savefile->ReadRenderEffect( renderEffect );
effectDefHandle = -1;
savefile->ReadJoint( endOriginJoint );
}
/*
================
rvClientEffect::FreeEntityDef
================
*/
void rvClientEffect::FreeEntityDef( void ) {
FreeEffectDef();
}
/*
===============================================================================
rvClientCrawlEffect
===============================================================================
*/
CLASS_DECLARATION( rvClientEffect, rvClientCrawlEffect )
END_CLASS
/*
================
rvClientCrawlEffect::rvClientCrawlEffect
================
*/
rvClientCrawlEffect::rvClientCrawlEffect ( void ) {
}
rvClientCrawlEffect::rvClientCrawlEffect ( const idDecl *effect, idEntity* ent, int _crawlTime, idList<jointHandle_t>* joints ) : rvClientEffect ( effect ) {
int i;
// Crawl effects require an animated entity
crawlEnt = dynamic_cast<idAnimatedEntity*>(ent);
if ( !crawlEnt) {
return;
}
// Specific joint list provided?
if ( joints && joints->Num () ) {
crawlJoints.Clear ( );
for ( i = 0; i < joints->Num(); i ++ ) {
crawlJoints.Append ( (*joints)[i] );
}
} else {
// Use only parent joints and skip joint zero which is presumed to be the origin
for ( i = ent->GetAnimator()->NumJoints() - 1; i > 0; i -- ) {
if ( i != ent->GetAnimator()->GetFirstChild ( (jointHandle_t)i ) ) {
crawlJoints.Append ( (jointHandle_t)i );
}
}
}
//no joints? abort!
if ( !crawlJoints.Num() ) {
return;
}
jointStart = gameLocal.random.RandomInt ( crawlJoints.Num() );
crawlDir = gameLocal.random.RandomInt ( 2 ) > 0 ? 1 : -1;
jointEnd = (jointStart + crawlDir + crawlJoints.Num() ) % crawlJoints.Num();
crawlTime = _crawlTime;
nextCrawl = gameLocal.time + crawlTime;
}
/*
================
rvClientCrawlEffect::Think
================
*/
void rvClientCrawlEffect::Think ( void ) {
// If there is no crawl entity or no crawl joints then just free ourself
if ( !crawlEnt || !crawlJoints.Num() ) {
PostEventMS ( &EV_Remove, 0 );
return;
}
// Move to the next joint if its time
if ( gameLocal.time > nextCrawl ) {
jointStart = (jointStart + crawlDir + crawlJoints.Num() ) % crawlJoints.Num();
jointEnd = (jointStart + crawlDir + crawlJoints.Num() ) % crawlJoints.Num();
nextCrawl = gameLocal.time + crawlTime;
}
idVec3 offsetStart;
idVec3 offsetEnd;
idVec3 dir;
idMat3 axis;
// Get the start origin
crawlEnt->GetJointWorldTransform ( crawlJoints[jointStart], gameLocal.time, offsetStart, axis );
SetOrigin ( offsetStart );
// Get the end origin
crawlEnt->GetJointWorldTransform ( crawlJoints[jointEnd], gameLocal.time, offsetEnd, axis );
SetEndOrigin ( offsetEnd );
// Update the axis to point at the bone
dir = offsetEnd - offsetStart;
dir.Normalize();
SetAxis ( dir.ToMat3( ) );
rvClientEffect::Think ( );
}
/*
================
rvClientCrawlEffect::Save
================
*/
void rvClientCrawlEffect::Save( idSaveGame *savefile ) const {
savefile->WriteInt( crawlJoints.Num() );
for( int ix = 0; ix < crawlJoints.Num(); ++ix ) {
savefile->WriteJoint( crawlJoints[ix] );
}
savefile->WriteInt( crawlTime );
savefile->WriteInt( nextCrawl );
savefile->WriteInt( jointStart );
savefile->WriteInt( jointEnd );
savefile->WriteInt( crawlDir );
crawlEnt.Save( savefile );
}
/*
================
rvClientCrawlEffect::Restore
================
*/
void rvClientCrawlEffect::Restore( idRestoreGame *savefile ) {
int numJoints = 0;
savefile->ReadInt( numJoints );
crawlJoints.SetNum( numJoints );
for( int ix = 0; ix < numJoints; ++ix ) {
savefile->ReadJoint( crawlJoints[ix] );
}
savefile->ReadInt( crawlTime );
savefile->ReadInt( nextCrawl );
savefile->ReadInt( jointStart );
savefile->ReadInt( jointEnd );
savefile->ReadInt( crawlDir );
crawlEnt.Restore( savefile );
}
+102
View File
@@ -0,0 +1,102 @@
//----------------------------------------------------------------
// ClientEffect.h
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------
#ifndef __GAME_CLIENT_EFFECT_H__
#define __GAME_CLIENT_EFFECT_H__
class rvClientEffect : public rvClientEntity {
public:
CLASS_PROTOTYPE( rvClientEffect );
rvClientEffect( void );
rvClientEffect( const idDecl *effect );
virtual ~rvClientEffect( void );
virtual void Think ( void );
virtual void DrawDebugInfo ( void ) const;
virtual void FreeEntityDef ( void );
virtual void UpdateBind ( void );
bool Play ( int startTime, bool loop = false, const idVec3& origin = vec3_origin );
void Stop ( bool destroyParticles = false );
void Restart ( void );
int GetEffectIndex ( void );
const char * GetEffectName ( void );
void Attenuate ( float attenuation );
float GetDuration ( void ) const;
renderEffect_t* GetRenderEffect ( void ) { return &renderEffect; }
void SetEndOrigin ( const idVec3& endOrigin );
void SetEndOrigin ( jointHandle_t joint ) { endOriginJoint = joint; }
void SetGravity ( const idVec3& gravity ) { renderEffect.gravity = gravity; }
void SetColor ( const idVec4& color );
void SetBrightness ( float brightness ) { renderEffect.shaderParms[SHADERPARM_BRIGHTNESS] = brightness; }
void SetAmbient ( bool in ) { renderEffect.ambient = in; }
void Save ( idSaveGame *savefile ) const;
void Restore ( idRestoreGame *savefile );
protected:
void Init ( const idDecl *effect );
void FreeEffectDef ( void );
renderEffect_t renderEffect;
int effectDefHandle;
jointHandle_t endOriginJoint;
};
ID_INLINE void rvClientEffect::SetEndOrigin ( const idVec3& endOrigin ) {
renderEffect.endOrigin = endOrigin;
renderEffect.hasEndOrigin = !(endOrigin == vec3_origin);
}
ID_INLINE void rvClientEffect::SetColor ( const idVec4& color ) {
renderEffect.shaderParms[SHADERPARM_RED] = color[0];
renderEffect.shaderParms[SHADERPARM_GREEN] = color[1];
renderEffect.shaderParms[SHADERPARM_BLUE] = color[2];
renderEffect.shaderParms[SHADERPARM_ALPHA] = color[3];
}
//----------------------------------------------------------------
// rvClientCrawlEffect
//----------------------------------------------------------------
class idAnimatedEntity;
class rvClientCrawlEffect : public rvClientEffect {
public:
CLASS_PROTOTYPE( rvClientCrawlEffect );
rvClientCrawlEffect ( void );
rvClientCrawlEffect ( const idDecl *effect, idEntity* ent, int crawlTime, idList<jointHandle_t>* joints = NULL );
~rvClientCrawlEffect ( void ) {}
virtual void Think ( void );
void Save ( idSaveGame *savefile ) const;
void Restore ( idRestoreGame *savefile );
protected:
idList<jointHandle_t> crawlJoints;
int crawlTime;
int nextCrawl;
int jointStart;
int jointEnd;
int crawlDir;
idEntityPtr<idAnimatedEntity> crawlEnt;
};
typedef rvClientEntityPtr<rvClientEffect> rvClientEffectPtr;
#endif // __GAME_CLIENT_EFFECT_H__
+651
View File
@@ -0,0 +1,651 @@
//----------------------------------------------------------------
// ClientEntity.cpp
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
ABSTRACT_DECLARATION( idClass, rvClientEntity )
END_CLASS
/*
================
rvClientEntity::rvClientEntity
================
*/
rvClientEntity::rvClientEntity( void ) {
bindMaster = NULL;
entityNumber = -1;
bindOrigin.Zero();
bindAxis.Identity();
bindJoint = INVALID_JOINT;
bindOrientated = false;
memset( &refSound, 0, sizeof(refSound) );
refSound.referenceSoundHandle = -1;
}
/*
================
rvClientEntity::~rvClientEntity
================
*/
rvClientEntity::~rvClientEntity( void ) {
Unbind();
gameLocal.UnregisterClientEntity( this );
// Free sound emitter
soundSystem->FreeSoundEmitter( SOUNDWORLD_GAME, refSound.referenceSoundHandle, true );
refSound.referenceSoundHandle = -1;
}
/*
================
rvClientEntity::Spawn
================
*/
void rvClientEntity::Spawn( void ) {
idVec3 origin;
idMat3 axis;
gameLocal.RegisterClientEntity( this );
spawnNode.SetOwner( this );
bindNode.SetOwner( this );
origin = spawnArgs.GetVector( "origin", "0 0 0" );
axis = spawnArgs.GetMatrix( "axis", "1 0 0 0 1 0 0 0 1" );
InitDefaultPhysics( origin, axis );
SetOrigin( origin );
SetAxis( axis );
}
/*
================
rvClientEntity::Present
================
*/
void rvClientEntity::Present ( void ) {
}
/*
================
rvClientEntity::FreeEntityDef
================
*/
void rvClientEntity::FreeEntityDef ( void ) {
}
/*
================
rvClientEntity::Think
================
*/
void rvClientEntity::Think ( void ) {
UpdateBind();
UpdateSound();
Present();
}
/*
================
rvClientEntity::Bind
================
*/
void rvClientEntity::Bind ( idEntity* master, jointHandle_t joint, bool isOrientated ) {
Unbind();
if ( joint != INVALID_JOINT && !dynamic_cast<idAnimatedEntity*>(master) ) {
gameLocal.Warning( "rvClientEntity::Bind: entity '%s' cannot support skeletal models.", master->GetName() );
joint = INVALID_JOINT;
}
bindMaster = master;
bindJoint = joint;
bindOrigin = worldOrigin;
bindAxis = worldAxis;
bindNode.AddToEnd ( bindMaster->clientEntities );
bindOrientated = isOrientated;
if( physics ) {
physics->SetMaster( bindMaster, bindOrientated );
}
UpdateBind();
}
/*
================
rvClientEntity::Bind
================
*/
void rvClientEntity::Unbind ( void ) {
if ( !bindMaster ) {
return;
}
bindMaster = NULL;
bindNode.Remove ( );
}
/*
================
rvClientEntity::SetOrigin
================
*/
void rvClientEntity::SetOrigin( const idVec3& origin ) {
if ( bindMaster ) {
bindOrigin = origin;
} else {
worldOrigin = origin;
}
}
/*
================
rvClientEntity::SetAxis
================
*/
void rvClientEntity::SetAxis( const idMat3& axis ) {
if ( bindMaster ) {
bindAxis = axis * bindMaster->GetRenderEntity()->axis.Transpose();
} else {
worldAxis = axis;
}
}
/*
================
rvClientEntity::UpdateBind
================
*/
void rvClientEntity::UpdateBind ( void ) {
if ( !bindMaster ) {
return;
}
if ( bindJoint != INVALID_JOINT ) {
static_cast<idAnimatedEntity*>(bindMaster.GetEntity())->GetJointWorldTransform ( bindJoint, gameLocal.time, worldOrigin, worldAxis );
} else {
bindMaster->GetPosition( worldOrigin, worldAxis );
//if ( !bindMaster->GetPhysicsToVisualTransform( worldOrigin, worldAxis ) ) {
// bindMaster->GetPosition( worldOrigin, worldAxis );
//}
}
worldOrigin += (bindOrigin * worldAxis);
worldAxis = bindAxis * worldAxis;
}
/*
================
rvClientEntity::IsClient
================
*/
bool rvClientEntity::IsClient ( void ) const {
return true;
}
/*
================
rvClientEntity::DrawDebugInfo
================
*/
void rvClientEntity::DrawDebugInfo ( void ) const {
idBounds bounds ( idVec3(-8,-8,-8), idVec3(8,8,8) );
gameRenderWorld->DebugBounds ( colorGreen, bounds, worldOrigin );
if ( gameLocal.GetLocalPlayer() ) {
gameRenderWorld->DrawText ( GetClassname ( ), worldOrigin, 0.1f, colorWhite, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 );
}
}
/*
================
rvClientEntity::UpdateSound
================
*/
void rvClientEntity::UpdateSound( void ) {
idSoundEmitter *emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
if ( emitter ) {
refSound.origin = worldOrigin;
refSound.velocity = vec3_origin;
emitter->UpdateEmitter( refSound.origin, refSound.velocity, refSound.listenerId, &refSound.parms );
}
}
/*
================
rvClientEntity::SetSoundVolume
================
*/
void rvClientEntity::SetSoundVolume( float volume ) {
refSound.parms.volume = volume;
}
/*
================
rvClientEntity::StartSound
================
*/
bool rvClientEntity::StartSound( const char *soundName, const s_channelType channel, int soundShaderFlags, bool broadcast, int *length ) {
const idSoundShader *shader;
const char *sound;
if ( length ) {
*length = 0;
}
idStr soundNameStr = soundName;
if( soundNameStr.CmpPrefix( "snd_" ) && soundNameStr.CmpPrefix( "lipsync_" ) ) {
common->Warning( "Non precached sound \'%s\'", soundName );
}
if ( !spawnArgs.GetString( soundName, "", &sound ) ) {
return false;
}
if ( *sound == '\0' ) {
return false;
}
if ( !gameLocal.isNewFrame ) {
// don't play the sound, but don't report an error
return true;
}
shader = declManager->FindSound( sound );
return StartSoundShader( shader, channel, soundShaderFlags );
}
/*
================
rvClientEntity::StartSoundShader
================
*/
int rvClientEntity::StartSoundShader ( const idSoundShader* shader, const s_channelType channel, int soundShaderFlags ) {
if ( !shader ) {
return 0;
}
idSoundEmitter *emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
if ( !emitter ) {
refSound.referenceSoundHandle = soundSystem->AllocSoundEmitter( SOUNDWORLD_GAME );
}
UpdateSound();
emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
if( !emitter ) {
return( 0 );
}
emitter->UpdateEmitter( refSound.origin, refSound.velocity, refSound.listenerId, &refSound.parms );
return emitter->StartSound( shader, channel, gameLocal.random.RandomFloat(), soundShaderFlags );
}
/*
================
rvClientEntity::Size
Returns Returns memory size of an rvClientEntity.
================
*/
size_t rvClientEntity::Size ( void ) const {
return sizeof( rvClientEntity );
}
/*
================
rvClientEntity::Save
================
*/
void rvClientEntity::Save( idSaveGame *savefile ) const {
savefile->WriteInt( entityNumber );
// idLinkList<rvClientEntity> spawnNode; - reconstructed in the master entity load
// idLinkList<rvClientEntity> bindNode; - reconstructed in the master entity load
savefile->WriteVec3( worldOrigin );
savefile->WriteVec3( worldVelocity );
savefile->WriteMat3( worldAxis );
bindMaster.Save( savefile );
savefile->WriteVec3( bindOrigin );
savefile->WriteMat3( bindAxis );
savefile->WriteJoint( bindJoint );
savefile->WriteRefSound( refSound );
}
/*
================
rvClientEntity::Restore
================
*/
void rvClientEntity::Restore( idRestoreGame *savefile ) {
savefile->ReadInt( entityNumber );
// idLinkList<rvClientEntity> spawnNode; - reconstructed in the master entity load
// idLinkList<rvClientEntity> bindNode; - reconstructed in the master entity load
savefile->ReadVec3( worldOrigin );
savefile->ReadVec3( worldVelocity );
savefile->ReadMat3( worldAxis );
bindMaster.Restore( savefile );
savefile->ReadVec3( bindOrigin );
savefile->ReadMat3( bindAxis );
savefile->ReadJoint( bindJoint );
savefile->ReadRefSound( refSound );
}
/*
================
rvClientEntity::RunPhysics
================
*/
void rvClientEntity::RunPhysics ( void ) {
idPhysics* physics = GetPhysics( );
if( !physics ) {
return;
}
rvClientPhysics* clientPhysics = (rvClientPhysics*)gameLocal.entities[ENTITYNUM_CLIENT];
static_cast<rvClientPhysics*>( clientPhysics )->currentEntityNumber = entityNumber;
// order important: 1) set client physics bind master to client ent's bind master
// 2) set physics to client ent's physics, which sets physics
// master to client ent's master
// 3) set client physics origin to client ent origin, depends on
// proper bind master from 1
clientPhysics->PushBindInfo( bindMaster, bindJoint, bindOrientated );
clientPhysics->SetPhysics( physics );
clientPhysics->PushOriginInfo( bindMaster ? bindOrigin : worldOrigin, bindMaster ? bindAxis : worldAxis );
physics->Evaluate ( gameLocal.time - gameLocal.previousTime, gameLocal.time );
worldOrigin = physics->GetOrigin();
worldVelocity = physics->GetLinearVelocity();
worldAxis = physics->GetAxis();
// order important: 1) restore previous bind master
// 2) reset physics with previous bind master
// 3) reset origin with previous bind master
clientPhysics->PopBindInfo();
clientPhysics->SetPhysics( NULL );
clientPhysics->PopOriginInfo();
UpdateAnimationControllers();
}
/*
================
rvClientEntity::GetPhysics
================
*/
idPhysics* rvClientEntity::GetPhysics ( void ) const {
return physics;
}
/*
================
rvClientEntity::Collide
================
*/
bool rvClientEntity::Collide ( const trace_t &collision, const idVec3 &velocity ) {
return false;
}
/*
================
rvClientEntity::GetImpactInfo
================
*/
void rvClientEntity::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
GetPhysics()->GetImpactInfo( id, point, info );
}
/*
================
rvClientEntity::ApplyImpulse
================
*/
void rvClientEntity::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse, bool splash ) {
GetPhysics()->ApplyImpulse( id, point, impulse );
}
/*
================
rvClientEntity::AddForce
================
*/
void rvClientEntity::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
GetPhysics()->AddForce( id, point, force );
}
/*
================
rvClientEntity::UpdateAnimationControllers
================
*/
bool rvClientEntity::UpdateAnimationControllers( void ) {
return false;
}
/*
================
rvClientEntity::InitDefaultPhysics
================
*/
void rvClientEntity::InitDefaultPhysics( const idVec3 &origin, const idMat3 &axis ) {
const char *temp;
idClipModel *clipModel = NULL;
// check if a clipmodel key/value pair is set
if ( spawnArgs.GetString( "clipmodel", "", &temp ) ) {
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// RAVEN END
clipModel = new idClipModel( temp );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
}
if ( !spawnArgs.GetBool( "noclipmodel", "0" ) ) {
// check if mins/maxs or size key/value pairs are set
if ( !clipModel ) {
idVec3 size;
idBounds bounds;
bool setClipModel = false;
if ( spawnArgs.GetVector( "mins", NULL, bounds[0] ) &&
spawnArgs.GetVector( "maxs", NULL, bounds[1] ) ) {
setClipModel = true;
if ( bounds[0][0] > bounds[1][0] || bounds[0][1] > bounds[1][1] || bounds[0][2] > bounds[1][2] ) {
gameLocal.Error( "Invalid bounds '%s'-'%s' on client entity '%d'", bounds[0].ToString(), bounds[1].ToString(), entityNumber );
}
} else if ( spawnArgs.GetVector( "size", NULL, size ) ) {
if ( ( size.x < 0.0f ) || ( size.y < 0.0f ) || ( size.z < 0.0f ) ) {
gameLocal.Error( "Invalid size '%s' on client entity '%d'", size.ToString(), entityNumber );
}
bounds[0].Set( size.x * -0.5f, size.y * -0.5f, 0.0f );
bounds[1].Set( size.x * 0.5f, size.y * 0.5f, size.z );
setClipModel = true;
}
if ( setClipModel ) {
int numSides;
idTraceModel trm;
if ( spawnArgs.GetInt( "cylinder", "0", numSides ) && numSides > 0 ) {
trm.SetupCylinder( bounds, numSides < 3 ? 3 : numSides );
} else if ( spawnArgs.GetInt( "cone", "0", numSides ) && numSides > 0 ) {
trm.SetupCone( bounds, numSides < 3 ? 3 : numSides );
// RAVEN BEGIN
// bdube: added dodecahedron
} else if ( spawnArgs.GetInt( "dodecahedron", "0", numSides ) && numSides > 0 ) {
trm.SetupDodecahedron ( bounds );
// RAVEN END
} else {
trm.SetupBox( bounds );
}
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// RAVEN END
clipModel = new idClipModel( trm );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
}
}
// check if the visual model can be used as collision model
if ( !clipModel ) {
temp = spawnArgs.GetString( "model" );
if ( ( temp != NULL ) && ( *temp != 0 ) ) {
// RAVEN BEGIN
// jscott:slash problems
idStr canonical = temp;
canonical.BackSlashesToSlashes();
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// RAVEN END
clipModel = new idClipModel();
if ( !clipModel->LoadModel( canonical ) ) {
delete clipModel;
clipModel = NULL;
}
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
}
}
}
defaultPhysicsObj.SetSelf( gameLocal.entities[ENTITYNUM_CLIENT] );
defaultPhysicsObj.SetClipModel( clipModel, 1.0f );
defaultPhysicsObj.SetOrigin( origin );
defaultPhysicsObj.SetAxis( axis );
physics = &defaultPhysicsObj;
// by default no collision
physics->SetContents( 0 );
}
/*
===============================================================================
rvClientPhysics
===============================================================================
*/
CLASS_DECLARATION( idEntity, rvClientPhysics )
END_CLASS
/*
=====================
rvClientPhysics::Spawn
=====================
*/
void rvClientPhysics::Spawn( void ) {
pushedOrientated = false;
}
/*
=====================
rvClientPhysics::Collide
=====================
*/
bool rvClientPhysics::Collide( const trace_t &collision, const idVec3 &velocity ) {
assert ( currentEntityNumber >= 0 && currentEntityNumber < MAX_CENTITIES );
rvClientEntity* cent;
cent = gameLocal.clientEntities [ currentEntityNumber ];
if ( cent ) {
return cent->Collide ( collision, velocity );
}
return false;
}
/*
=====================
rvClientPhysics::PushBindInfo
=====================
*/
void rvClientPhysics::PushBindInfo( idEntity* master, jointHandle_t joint, bool orientated ) {
pushedBindJoint = joint;
pushedBindMaster = master;
pushedOrientated = fl.bindOrientated;
bindMaster = master;
bindJoint = joint;
fl.bindOrientated = orientated;
}
/*
=====================
rvClientPhysics::PopBindInfo
=====================
*/
void rvClientPhysics::PopBindInfo( void ) {
bindMaster = pushedBindMaster;
bindJoint = pushedBindJoint;
fl.bindOrientated = pushedOrientated;
}
/*
=====================
rvClientPhysics::PushOriginInfo
=====================
*/
void rvClientPhysics::PushOriginInfo( const idVec3& origin, const idMat3& axis ) {
if( !GetPhysics() ) {
return;
}
pushedOrigin = GetPhysics()->GetOrigin();
pushedAxis = GetPhysics()->GetAxis();
GetPhysics()->SetOrigin( origin );
GetPhysics()->SetAxis( axis );
}
/*
=====================
rvClientPhysics::PopOriginInfo
=====================
*/
void rvClientPhysics::PopOriginInfo( void ) {
if( !GetPhysics() ) {
return;
}
GetPhysics()->SetOrigin( pushedOrigin );
GetPhysics()->SetAxis( pushedAxis );
}
+222
View File
@@ -0,0 +1,222 @@
//----------------------------------------------------------------
// ClientEntity.h
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------
#ifndef __GAME_CLIENT_ENTITY_H__
#define __GAME_CLIENT_ENTITY_H__
class rvClientEntity : public idClass {
public:
ABSTRACT_PROTOTYPE( rvClientEntity );
rvClientEntity ( void );
virtual ~rvClientEntity ( void );
void Spawn ( void );
virtual void Present ( void );
virtual void Think ( void );
virtual idPhysics* GetPhysics ( void ) const;
virtual bool Collide ( const trace_t &collision, const idVec3 &velocity );
void SetOrigin ( const idVec3& origin );
void SetAxis ( const idMat3& axis );
const idVec3& GetOrigin ( void );
const idMat3& GetAxis ( void );
void Bind ( idEntity* master, jointHandle_t joint = INVALID_JOINT, bool isOrientated = false );
void Unbind ( void );
virtual bool IsClient ( void ) const;
virtual void DrawDebugInfo ( void ) const;
void SetSoundVolume( float volume );
bool StartSound( const char *soundName, const s_channelType channel, int soundShaderFlags, bool broadcast, int *length );
int StartSoundShader ( const idSoundShader *shader, const s_channelType channel, int soundShaderFlags );
// I'm guessing someone may decide to derive from rvClientEntity, so this virtual.
virtual size_t Size ( void ) const;
virtual void FreeEntityDef ( void );
const idEntityPtr<idEntity>& GetBindMaster( void ) const;
void Save ( idSaveGame *savefile ) const;
void Restore ( idRestoreGame *savefile );
virtual void GetImpactInfo ( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info );
virtual void ApplyImpulse ( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse, bool splash );
virtual void AddForce ( idEntity *ent, int id, const idVec3 &point, const idVec3 &force );
virtual bool UpdateAnimationControllers( void );
void InitDefaultPhysics ( const idVec3 &origin, const idMat3 &axis );
protected:
void RunPhysics ( void );
virtual void UpdateBind ( void );
void UpdateSound ( void );
public:
int entityNumber;
idLinkList<rvClientEntity> spawnNode;
idLinkList<rvClientEntity> bindNode;
idDict spawnArgs;
protected:
idPhysics_Static defaultPhysicsObj; // default physics object
idPhysics* physics;
idVec3 worldOrigin;
idVec3 worldVelocity;
idMat3 worldAxis;
idEntityPtr<idEntity> bindMaster;
idVec3 bindOrigin;
idMat3 bindAxis;
jointHandle_t bindJoint;
bool bindOrientated;
refSound_t refSound;
};
ID_INLINE const idVec3& rvClientEntity::GetOrigin ( void ) {
return worldOrigin;
}
ID_INLINE const idMat3& rvClientEntity::GetAxis ( void ) {
return worldAxis;
}
ID_INLINE const idEntityPtr<idEntity>& rvClientEntity::GetBindMaster( void ) const {
return bindMaster;
}
//============================================================================
template< class type >
class rvClientEntityPtr {
public:
rvClientEntityPtr();
rvClientEntityPtr<type> & operator=( type* cent );
int GetSpawnId ( void ) const { return spawnId; }
bool SetSpawnId ( int id );
bool UpdateSpawnId ( void );
bool IsValid ( void ) const;
type * GetEntity ( void ) const;
int GetEntityNum ( void ) const;
type * operator-> ( void ) const { return GetEntity ( ); }
operator type* ( void ) const { return GetEntity ( ); }
void Save ( idSaveGame *savefile ) const;
void Restore ( idRestoreGame *savefile );
private:
int spawnId;
};
template< class type >
ID_INLINE rvClientEntityPtr<type>::rvClientEntityPtr() {
spawnId = 0;
}
template< class type >
ID_INLINE rvClientEntityPtr<type> &rvClientEntityPtr<type>::operator=( type *cent ) {
if ( cent == NULL ) {
spawnId = 0;
} else {
spawnId = ( gameLocal.clientSpawnIds[cent->entityNumber] << CENTITYNUM_BITS ) | cent->entityNumber;
}
return *this;
}
template< class type >
ID_INLINE bool rvClientEntityPtr<type>::SetSpawnId( int id ) {
if ( id == spawnId ) {
return false;
}
if ( ( id >> CENTITYNUM_BITS ) == gameLocal.clientSpawnIds[ id & ( ( 1 << CENTITYNUM_BITS ) - 1 ) ] ) {
spawnId = id;
return true;
}
return false;
}
template< class type >
ID_INLINE bool rvClientEntityPtr<type>::IsValid( void ) const {
return ( gameLocal.clientSpawnIds[ spawnId & ( ( 1 << CENTITYNUM_BITS ) - 1 ) ] == ( ( spawnId >> CENTITYNUM_BITS ) & ( 0xffffffff >> CENTITYNUM_BITS ) ) );
}
template< class type >
ID_INLINE type *rvClientEntityPtr<type>::GetEntity( void ) const {
int entityNum = spawnId & ( ( 1 << CENTITYNUM_BITS ) - 1 );
if ( ( gameLocal.clientSpawnIds[ entityNum ] == ( ( spawnId >> CENTITYNUM_BITS ) & ( 0xffffffff >> CENTITYNUM_BITS ) ) ) ) {
return static_cast<type *>( gameLocal.clientEntities[ entityNum ] );
}
return NULL;
}
template< class type >
ID_INLINE int rvClientEntityPtr<type>::GetEntityNum( void ) const {
return ( spawnId & ( ( 1 << CENTITYNUM_BITS ) - 1 ) );
}
template< class type >
ID_INLINE void rvClientEntityPtr<type>::Save( idSaveGame *savefile ) const {
savefile->WriteInt( spawnId );
}
template< class type >
ID_INLINE void rvClientEntityPtr<type>::Restore( idRestoreGame *savefile ) {
savefile->ReadInt( spawnId );
}
/*
===============================================================================
rvClientPhysics
===============================================================================
*/
class rvClientPhysics : public idEntity {
public:
CLASS_PROTOTYPE( rvClientPhysics );
rvClientPhysics( void ) { currentEntityNumber = -1; }
void Spawn( void );
virtual bool Collide( const trace_t &collision, const idVec3 &velocity );
virtual void PushBindInfo( idEntity* master, jointHandle_t joint, bool orientated );
virtual void PopBindInfo( void );
virtual void PushOriginInfo( const idVec3& origin, const idMat3& axis );
virtual void PopOriginInfo( void );
// client side entities. make sure the networking doesn't fuck around with this
virtual bool ClientStale( void ) { assert( false ); return false; }
virtual void ClientUnstale( void ) { assert( false ); }
int currentEntityNumber;
private:
idEntity* pushedBindMaster;
jointHandle_t pushedBindJoint;
idVec3 pushedOrigin;
idMat3 pushedAxis;
bool pushedOrientated;
};
#endif // __GAME_CLIENT_ENTITY_H__
+450
View File
@@ -0,0 +1,450 @@
//----------------------------------------------------------------
// ClientModel.cpp
//
// A non-interactive client-side model
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
#include "ClientModel.h"
/*
===============================================================================
rvClientModel
===============================================================================
*/
CLASS_DECLARATION( rvClientEntity, rvClientModel )
END_CLASS
/*
================
rvClientModel::rvClientModel
================
*/
rvClientModel::rvClientModel ( void ) {
memset ( &renderEntity, 0, sizeof(renderEntity) );
worldAxis = mat3_identity;
entityDefHandle = -1;
}
/*
================
rvClientModel::~rvClientModel
================
*/
rvClientModel::~rvClientModel ( void ) {
FreeEntityDef ( );
}
/*
================
rvClientModel::FreeEntityDef
================
*/
void rvClientModel::FreeEntityDef ( void ) {
if ( entityDefHandle >= 0 ) {
gameRenderWorld->FreeEntityDef ( entityDefHandle );
entityDefHandle = -1;
}
}
/*
================
rvClientModel::Spawn
================
*/
void rvClientModel::Spawn ( void ) {
const char* spawnarg;
spawnArgs.GetString ( "classname", "", classname );
// parse static models the same way the editor display does
gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &renderEntity );
renderEntity.entityNum = entityNumber;
spawnarg = spawnArgs.GetString( "model" );
if ( spawnarg && *spawnarg ) {
SetModel( spawnarg );
}
}
/*
================
rvClientModel::Think
================
*/
void rvClientModel::Think ( void ) {
if( bindMaster && (bindMaster->GetRenderEntity()->hModel && bindMaster->GetModelDefHandle() == -1) ) {
return;
}
UpdateBind();
Present();
}
/*
================
rvClientModel::Present
================
*/
void rvClientModel::Present(void) {
// Hide client entities bound to a hidden entity
if ( bindMaster && (bindMaster->IsHidden ( ) || (bindMaster->GetRenderEntity()->hModel && bindMaster->GetModelDefHandle() == -1) ) ) {
return;
}
renderEntity.origin = worldOrigin;
renderEntity.axis = worldAxis;
// add to refresh list
if ( entityDefHandle == -1 ) {
entityDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
} else {
gameRenderWorld->UpdateEntityDef( entityDefHandle, &renderEntity );
}
}
/*
================
rvClientModel::SetCustomShader
================
*/
bool rvClientModel::SetCustomShader ( const char* shaderName ) {
if ( shaderName == NULL ) {
return false;
}
const idMaterial* material = declManager->FindMaterial( shaderName );
if ( material == NULL ) {
return false;
}
renderEntity.customShader = material;
return true;
}
/*
================
rvClientModel::Save
================
*/
void rvClientModel::Save( idSaveGame *savefile ) const {
savefile->WriteRenderEntity( renderEntity );
savefile->WriteInt( entityDefHandle );
savefile->WriteString ( classname ); // cnicholson: Added unsaved var
}
/*
================
rvClientModel::Restore
================
*/
void rvClientModel::Restore( idRestoreGame *savefile ) {
savefile->ReadRenderEntity( renderEntity, NULL );
savefile->ReadInt( entityDefHandle );
savefile->ReadString ( classname ); // cnicholson: Added unrestored var
// restore must retrieve entityDefHandle from the renderer
if ( entityDefHandle != -1 ) {
entityDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
}
}
/*
================
rvClientModel::SetModel
================
*/
void rvClientModel::SetModel( const char* modelname ) {
FreeEntityDef();
renderEntity.hModel = renderModelManager->FindModel( modelname );
if ( renderEntity.hModel ) {
renderEntity.hModel->Reset();
}
renderEntity.callback = NULL;
renderEntity.numJoints = 0;
renderEntity.joints = NULL;
if ( renderEntity.hModel ) {
renderEntity.bounds = renderEntity.hModel->Bounds( &renderEntity );
} else {
renderEntity.bounds.Zero();
}
}
/*
==============
rvClientModel::ProjectOverlay
==============
*/
void rvClientModel::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
float s, c;
idMat3 axis, axistemp;
idVec3 localOrigin, localAxis[2];
idPlane localPlane[2];
// make sure the entity has a valid model handle
if ( entityDefHandle < 0 ) {
return;
}
// only do this on dynamic md5 models
if ( renderEntity.hModel->IsDynamicModel() != DM_CACHED ) {
return;
}
idMath::SinCos16( gameLocal.random.RandomFloat() * idMath::TWO_PI, s, c );
axis[2] = -dir;
axis[2].NormalVectors( axistemp[0], axistemp[1] );
axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s;
axis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c;
renderEntity.axis.ProjectVector( origin - renderEntity.origin, localOrigin );
renderEntity.axis.ProjectVector( axis[0], localAxis[0] );
renderEntity.axis.ProjectVector( axis[1], localAxis[1] );
size = 1.0f / size;
localAxis[0] *= size;
localAxis[1] *= size;
localPlane[0] = localAxis[0];
localPlane[0][3] = -( localOrigin * localAxis[0] ) + 0.5f;
localPlane[1] = localAxis[1];
localPlane[1][3] = -( localOrigin * localAxis[1] ) + 0.5f;
const idMaterial *mtr = declManager->FindMaterial( material );
// project an overlay onto the model
gameRenderWorld->ProjectOverlay( entityDefHandle, localPlane, mtr );
// make sure non-animating models update their overlay
UpdateVisuals();
}
/*
================
rvClientModel::UpdateRenderEntity
================
*/
bool rvClientModel::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) {
if ( gameLocal.inCinematic && gameLocal.skipCinematic ) {
return false;
}
idAnimator *animator = GetAnimator();
if ( animator ) {
return animator->CreateFrame( gameLocal.time, false );
}
return false;
}
/*
================
rvClientModel::ModelCallback
NOTE: may not change the game state whatsoever!
================
*/
bool rvClientModel::ModelCallback( renderEntity_s *renderEntity, const renderView_t *renderView ) {
rvClientEntity *cent;
cent = gameLocal.clientEntities[ renderEntity->entityNum ];
if ( !cent ) {
gameLocal.Error( "rvClientModel::ModelCallback: callback with NULL client entity '%d'", renderEntity->entityNum );
return false;
}
if( !cent->IsType( rvClientModel::GetClassType() ) ) {
gameLocal.Error( "rvClientModel::ModelCallback: callback with non-client model on client entity '%d'", renderEntity->entityNum );
return false;
}
return ((rvClientModel*)cent)->UpdateRenderEntity( renderEntity, renderView );
}
/*
================
rvClientModel::GetPhysicsToVisualTransform
================
*/
bool rvClientModel::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
return false;
}
/*
================
rvClientModel::UpdateModelTransform
================
*/
void rvClientModel::UpdateModelTransform( void ) {
idVec3 origin;
idMat3 axis;
if ( GetPhysicsToVisualTransform( origin, axis ) ) {
renderEntity.axis = axis * worldAxis;
renderEntity.origin = worldOrigin + origin * renderEntity.axis;
} else {
renderEntity.axis = worldAxis;
renderEntity.origin = worldOrigin;
}
}
/*
================
rvClientModel::UpdateModel
================
*/
void rvClientModel::UpdateModel( void ) {
UpdateModelTransform();
idAnimator *animator = GetAnimator();
if ( animator && animator->ModelHandle() ) {
// set the callback to update the joints
renderEntity.callback = rvClientModel::ModelCallback;
}
}
/*
================
rvClientModel::UpdateVisuals
================
*/
void rvClientModel::UpdateVisuals( void ) {
UpdateModel();
UpdateSound();
}
/*
================
rvClientModel::SetSkin
================
*/
void rvClientModel::SetSkin( const idDeclSkin *skin ) {
renderEntity.customSkin = skin;
UpdateVisuals();
}
/*
===============================================================================
rvAnimatedClientEntity
===============================================================================
*/
CLASS_DECLARATION( rvClientModel, rvAnimatedClientEntity )
END_CLASS
/*
================
rvAnimatedClientEntity::rvAnimatedClientEntity
================
*/
rvAnimatedClientEntity::rvAnimatedClientEntity ( void ) {
}
/*
================
rvAnimatedClientEntity::~rvAnimatedClientEntity
================
*/
rvAnimatedClientEntity::~rvAnimatedClientEntity ( void ) {
}
/*
================
rvAnimatedClientEntity::Spawn
================
*/
void rvAnimatedClientEntity::Spawn( void ) {
SetModel( spawnArgs.GetString( "model" ) );
}
/*
================
rvAnimatedClientEntity::Think
================
*/
void rvAnimatedClientEntity::Think ( void ) {
UpdateAnimation();
rvClientEntity::Think();
}
/*
================
rvAnimatedClientEntity::UpdateAnimation
================
*/
void rvAnimatedClientEntity::UpdateAnimation( void ) {
// is the model an MD5?
if ( !animator.ModelHandle() ) {
// no, so nothing to do
return;
}
// call any frame commands that have happened in the past frame
animator.ServiceAnims( gameLocal.previousTime, gameLocal.time );
// if the model is animating then we have to update it
if ( !animator.FrameHasChanged( gameLocal.time ) ) {
// still fine the way it was
return;
}
// get the latest frame bounds
animator.GetBounds( gameLocal.time, renderEntity.bounds );
if ( renderEntity.bounds.IsCleared() ) {
gameLocal.DPrintf( "rvAnimatedClientEntity %s %d: inside out bounds - %d\n", GetClassname(), entityNumber, gameLocal.time );
}
// update the renderEntity
UpdateVisuals();
Present();
// the animation is updated
animator.ClearForceUpdate();
}
/*
================
rvAnimatedClientEntity::SetModel
================
*/
void rvAnimatedClientEntity::SetModel( const char *modelname ) {
FreeEntityDef();
renderEntity.hModel = animator.SetModel( modelname );
if ( !renderEntity.hModel ) {
rvClientModel::SetModel( modelname );
return;
}
if ( !renderEntity.customSkin ) {
renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin();
}
// set the callback to update the joints
renderEntity.callback = rvClientModel::ModelCallback;
animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints );
animator.GetBounds( gameLocal.time, renderEntity.bounds );
//UpdateVisuals();
Present();
}
+92
View File
@@ -0,0 +1,92 @@
//----------------------------------------------------------------
// ClientModel.h
//
// A non-interactive client-side model
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------
#ifndef __GAME_CLIENT_MODEL_H__
#define __GAME_CLIENT_MODEL_H__
class rvClientModel : public rvClientEntity {
public:
CLASS_PROTOTYPE( rvClientModel );
rvClientModel ( void );
virtual ~rvClientModel ( void );
void Spawn ( void );
virtual void Think ( void );
virtual renderEntity_t* GetRenderEntity ( void );
const char* GetClassname ( void ) const;
virtual bool SetCustomShader ( const char* shaderName );
void Save ( idSaveGame *savefile ) const;
void Restore ( idRestoreGame *savefile );
virtual void FreeEntityDef ( void );
virtual void SetModel( const char *modelname );
virtual const idAnimator* GetAnimator( void ) const { return NULL; }
virtual idAnimator* GetAnimator( void ) { return NULL; }
bool UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView );
static bool ModelCallback( renderEntity_s *renderEntity, const renderView_t *renderView );
virtual bool GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis );
void ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material );
void UpdateVisuals( void );
void UpdateModel( void );
void UpdateModelTransform( void );
void SetSkin( const idDeclSkin* skin );
int GetModelDefHandle( void );
protected:
void Present( void );
renderEntity_t renderEntity;
int entityDefHandle;
idStr classname;
};
ID_INLINE renderEntity_t* rvClientModel::GetRenderEntity ( void ) {
return &renderEntity;
}
ID_INLINE const char* rvClientModel::GetClassname ( void ) const {
return classname.c_str();
}
ID_INLINE int rvClientModel::GetModelDefHandle( void ) {
return entityDefHandle;
}
class rvAnimatedClientEntity : public rvClientModel {
public:
CLASS_PROTOTYPE( rvAnimatedClientEntity );
rvAnimatedClientEntity ( void );
virtual ~rvAnimatedClientEntity ( void );
void Spawn( void );
virtual void Think( void );
virtual void UpdateAnimation( void );
virtual void SetModel( const char *modelname );
virtual const idAnimator* GetAnimator( void ) const { return &animator; }
virtual idAnimator* GetAnimator( void ) { return &animator; }
protected:
idAnimator animator;
};
#endif // __GAME_CLIENT_MODEL_H__
+379
View File
@@ -0,0 +1,379 @@
//----------------------------------------------------------------
// ClientMoveable.cpp
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
/*
===============================================================================
rvClientMoveable
===============================================================================
*/
const idEventDef CL_FadeOut( "<fadeOut>", "d" );
const idEventDef CL_ClearDepthHack ( "<clearDepthHack>" );
static const float BOUNCE_SOUND_MIN_VELOCITY = 100.0f;
static const float BOUNCE_SOUND_MAX_VELOCITY = 200.0f;
static const int BOUNCE_SOUND_DELAY = 200;
CLASS_DECLARATION( rvClientEntity, rvClientMoveable )
EVENT( CL_FadeOut, rvClientMoveable::Event_FadeOut )
EVENT( CL_ClearDepthHack, rvClientMoveable::Event_ClearDepthHack )
END_CLASS
/*
================
rvClientMoveable::rvClientMoveable
================
*/
rvClientMoveable::rvClientMoveable ( void ) {
memset ( &renderEntity, 0, sizeof(renderEntity) );
entityDefHandle = -1;
scale.Init( 0, 0, 1.0f, 1.0f );
}
/*
================
rvClientMoveable::~rvClientMoveable
================
*/
rvClientMoveable::~rvClientMoveable ( void ) {
FreeEntityDef ( );
// Remove any trail effect if there is one
if ( trailEffect ) {
trailEffect->Stop ( );
}
}
/*
================
rvClientMoveable::FreeEntityDef
================
*/
void rvClientMoveable::FreeEntityDef ( void ) {
if ( entityDefHandle >= 0 ) {
gameRenderWorld->FreeEntityDef ( entityDefHandle );
entityDefHandle = -1;
}
}
idVec3 simpleTri[3] =
{
idVec3( -1.0, -1.0, 0.0 ),
idVec3( 0.0, 2.0, 0.0 ),
idVec3( 2.0, 0.0, 0.0 )
};
/*
================
rvClientMoveable::Spawn
================
*/
void rvClientMoveable::Spawn ( void ) {
// parse static models the same way the editor display does
gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &renderEntity );
idTraceModel trm;
int clipShrink;
idStr clipModelName;
// check if a clip model is set
spawnArgs.GetString( "clipmodel", "", clipModelName );
if ( !clipModelName.Length () ) {
clipModelName = spawnArgs.GetString( "model" ); // use the visual model
}
if ( clipModelName == SIMPLE_TRI_NAME ) {
trm.SetupPolygon( simpleTri, 3 );
} else {
clipModelName.BackSlashesToSlashes();
if ( !collisionModelManager->TrmFromModel( gameLocal.GetMapName(), clipModelName, trm ) ) {
gameLocal.Error( "rvClientMoveable '%d': cannot load collision model %s", entityNumber, clipModelName.c_str() );
return;
}
}
// if the model should be shrunk
clipShrink = spawnArgs.GetInt( "clipshrink" );
if ( clipShrink != 0 ) {
trm.Shrink( clipShrink * CM_CLIP_EPSILON );
}
physicsObj.SetSelf ( gameLocal.entities[ENTITYNUM_CLIENT] );
physicsObj.SetClipModel ( new idClipModel( trm ), spawnArgs.GetFloat ( "density", "0.5" ), entityNumber );
physicsObj.SetOrigin( GetOrigin() );
physicsObj.SetAxis( GetAxis() );
physicsObj.SetBouncyness( spawnArgs.GetFloat( "bouncyness", "0.6" ) );
physicsObj.SetFriction( spawnArgs.GetFloat("linear_friction", "0.6"), spawnArgs.GetFloat( "angular_friction", "0.6"), spawnArgs.GetFloat("friction", "0.05") );
physicsObj.SetGravity( gameLocal.GetCurrentGravity(this) );
physicsObj.SetContents( 0 );
// abahr: changed to MASK_SHOT_RENDERMODEL because brass was getting pinched between the player and the wall in some cases
// may want to try something cheaper.
physicsObj.SetClipMask( CONTENTS_OPAQUE ); // MASK_SHOT_RENDERMODEL | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP | CONTENTS_WATER );
physicsObj.Activate ( );
trailEffect = gameLocal.PlayEffect ( spawnArgs, "fx_trail", physicsObj.GetCenterMass(), GetAxis(), true );
trailAttenuateSpeed = spawnArgs.GetFloat ( "trailAttenuateSpeed", "200" );
bounceSoundShader = declManager->FindSound ( spawnArgs.GetString ( "snd_bounce" ), false );
bounceSoundTime = 0;
mPlayBounceSoundOnce = spawnArgs.GetBool("bounce_sound_once");
mHasBounced = false;
scale.Init( gameLocal.GetTime(), SEC2MS(spawnArgs.GetFloat("scale_reset_duration", "0.2")), Max(VECTOR_EPSILON, spawnArgs.GetFloat("scale", "1.0f")), 1.0f );
}
/*
================
rvClientMoveable::SetOrigin
================
*/
void rvClientMoveable::SetOrigin( const idVec3& origin ) {
rvClientEntity::SetOrigin( origin );
physicsObj.SetOrigin( origin );
}
/*
================
rvClientMoveable::SetAxis
================
*/
void rvClientMoveable::SetAxis( const idMat3& axis ) {
rvClientEntity::SetAxis( axis );
physicsObj.SetAxis( axis );
}
/*
================
rvClientMoveable::SetOwner
================
*/
void rvClientMoveable::SetOwner( idEntity* owner ) {
physicsObj.GetClipModel()->SetOwner( owner );
physicsObj.GetClipModel()->SetEntity( owner );
}
/*
================
rvClientMoveable::Think
================
*/
void rvClientMoveable::Think ( void ) {
if( bindMaster && (bindMaster->GetRenderEntity()->hModel && bindMaster->GetModelDefHandle() == -1) ) {
return;
}
RunPhysics ( );
// Special case the sound update to use the center mass since the origin may be in an odd place
idSoundEmitter *emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
if ( emitter ) {
refSound.origin = worldOrigin;
refSound.velocity = worldVelocity;
emitter->UpdateEmitter( refSound.origin, refSound.velocity, refSound.listenerId, &refSound.parms );
}
// Keep the trail effect following
if ( trailEffect ) {
float speed;
speed = idMath::ClampFloat ( 0, trailAttenuateSpeed, worldVelocity.LengthFast ( ) );
if ( physicsObj.IsAtRest ( ) ) {
trailEffect->Stop ( );
trailEffect = NULL;
} else {
trailEffect->SetOrigin ( worldOrigin );
trailEffect->SetAxis ( worldAxis );
trailEffect->Attenuate ( speed / trailAttenuateSpeed );
}
}
renderEntity.origin = worldOrigin;
renderEntity.axis = worldAxis * scale.GetCurrentValue( gameLocal.GetTime() );
// add to refresh list
if ( entityDefHandle == -1 ) {
entityDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
} else {
gameRenderWorld->UpdateEntityDef( entityDefHandle, &renderEntity );
}
}
/*
================
rvClientMoveable::GetPhysics
================
*/
idPhysics* rvClientMoveable::GetPhysics ( void ) const {
return (idPhysics*)&physicsObj;
}
/*
================
rvClientMoveable::Collide
================
*/
bool rvClientMoveable::Collide ( const trace_t &collision, const idVec3 &velocity ) {
if (mPlayBounceSoundOnce && mHasBounced)
{
return false;
}
if ( bounceSoundShader && gameLocal.time > bounceSoundTime ) {
float speed;
speed = velocity.LengthFast ( );
if ( speed > BOUNCE_SOUND_MIN_VELOCITY ) {
StartSoundShader ( bounceSoundShader, SND_CHANNEL_BODY, 0 );
bounceSoundTime = BOUNCE_SOUND_DELAY;
mHasBounced = true;
}
}
return false;
}
/*
================
rvClientMoveable::Save
================
*/
void rvClientMoveable::Save( idSaveGame *savefile ) const {
savefile->WriteRenderEntity( renderEntity );
savefile->WriteInt( entityDefHandle );
trailEffect.Save( savefile );
savefile->WriteFloat( trailAttenuateSpeed );
savefile->WriteStaticObject( physicsObj );
savefile->WriteInt( bounceSoundTime );
savefile->WriteSoundShader( bounceSoundShader );
savefile->WriteBool(mPlayBounceSoundOnce);
savefile->WriteBool(mHasBounced);
// TOSAVE: idInterpolate<float> scale;
}
/*
================
rvClientMoveable::Restore
================
*/
void rvClientMoveable::Restore( idRestoreGame *savefile ) {
savefile->ReadRenderEntity( renderEntity, NULL );
savefile->ReadInt( entityDefHandle );
trailEffect.Restore( savefile );
savefile->ReadFloat( trailAttenuateSpeed );
savefile->ReadStaticObject( physicsObj );
savefile->ReadInt( bounceSoundTime );
savefile->ReadSoundShader( bounceSoundShader );
savefile->ReadBool(mPlayBounceSoundOnce);
savefile->ReadBool(mHasBounced);
// restore must retrieve entityDefHandle from the renderer
if ( entityDefHandle != -1 ) {
entityDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
}
// TORESTORE: idInterpolate<float> scale;
}
/*
================
rvClientMoveable::Event_FadeOut
================
*/
void rvClientMoveable::Event_FadeOut ( int duration ) {
renderEntity.noShadow = true;
renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
PostEventMS ( &EV_Remove, duration );
}
/*
================
rvClientMoveable::Event_ClearDepthHack
================
*/
void rvClientMoveable::Event_ClearDepthHack ( void ) {
renderEntity.weaponDepthHackInViewID = 0;
}
/*
================
rvClientMoveable::SpawnClientMoveables
================
*/
void rvClientMoveable::SpawnClientMoveables( idEntity* ent, const char *type, idList<rvClientMoveable *>* list ) {
const idKeyValue *kv;
idVec3 origin;
idMat3 axis;
if( list == NULL || type == NULL ) {
return;
}
// drop all items
kv = ent->spawnArgs.MatchPrefix( va( "def_%s", type ), NULL );
while ( kv ) {
origin = ent->GetPhysics()->GetOrigin();
axis = ent->GetPhysics()->GetAxis();
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if( ent->IsType( idAnimatedEntity::GetClassType() ) ) {
// RAVEN END
idAnimatedEntity* animEnt = static_cast<idAnimatedEntity*>(ent);
jointHandle_t clientMoveableJoint;
const char* clientMoveableJointName = ent->spawnArgs.GetString( va( "%s_joint", kv->GetKey().c_str() + 4 ) );
// use a joint if specified
if ( idStr::Icmp( clientMoveableJointName, "") ) {
clientMoveableJoint = animEnt->GetAnimator()->GetJointHandle( clientMoveableJointName );
if ( !animEnt->GetJointWorldTransform( clientMoveableJoint, gameLocal.time, origin, axis ) ) {
gameLocal.Warning( "%s refers to invalid joint '%s' on entity '%s'\n", va( "%s_joint", kv->GetKey().c_str() + 4 ), clientMoveableJointName, ent->name.c_str() );
origin = ent->GetPhysics()->GetOrigin();
axis = ent->GetPhysics()->GetAxis();
}
}
}
// spawn the entity
const idDict* entityDef = gameLocal.FindEntityDefDict ( kv->GetValue().c_str(), false );
if ( entityDef == NULL ) {
gameLocal.Warning( "%s refers to invalid entity def '%s' on entity '%s'\n", kv->GetKey().c_str(), kv->GetValue().c_str(), ent->name.c_str() );
break;
}
rvClientMoveable* newModel = NULL;
// force spawnclass to rvClientMoveable
gameLocal.SpawnClientEntityDef( *entityDef, (rvClientEntity**)(&newModel), false, "rvClientMoveable" );
if( !newModel ) {
gameLocal.Warning( "error spawning client moveable (invalid entity def '%s' on entity '%s')\n", kv->GetValue().c_str(), ent->name.c_str() );
break;
}
newModel->SetOrigin ( origin );
newModel->SetAxis( axis );
list->Append( newModel );
kv = ent->spawnArgs.MatchPrefix( va( "def_%s", type ), kv );
}
}
+70
View File
@@ -0,0 +1,70 @@
//----------------------------------------------------------------
// ClientMoveable.h
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------
#ifndef __GAME_CLIENT_MOVEABLE_H__
#define __GAME_CLIENT_MOVEABLE_H__
class rvClientMoveable : public rvClientEntity {
public:
CLASS_PROTOTYPE( rvClientMoveable );
rvClientMoveable ( void );
virtual ~rvClientMoveable ( void );
virtual void Spawn ( void );
virtual void Think ( void );
virtual idPhysics* GetPhysics ( void ) const;
virtual bool Collide ( const trace_t &collision, const idVec3 &velocity );
renderEntity_t* GetRenderEntity ( void );
void Save ( idSaveGame *savefile ) const;
void Restore ( idRestoreGame *savefile );
static void SpawnClientMoveables ( idEntity* ent, const char *type, idList<rvClientMoveable *>* list );
virtual void FreeEntityDef ( void );
void SetOwner ( idEntity* ent );
void SetOrigin ( const idVec3& origin );
void SetAxis ( const idMat3& axis );
protected:
renderEntity_t renderEntity;
int entityDefHandle;
rvClientEffectPtr trailEffect;
float trailAttenuateSpeed;
idPhysics_RigidBody physicsObj;
int bounceSoundTime;
const idSoundShader* bounceSoundShader;
bool mPlayBounceSoundOnce;
bool mHasBounced;
idInterpolate<float> scale;
private:
void Event_FadeOut ( int duration );
void Event_ClearDepthHack ( void );
};
ID_INLINE renderEntity_t* rvClientMoveable::GetRenderEntity ( void ) {
return &renderEntity;
}
extern const idEventDef CL_FadeOut;
extern const idEventDef CL_ClearDepthHack;
#define SIMPLE_TRI_NAME "simpletri"
extern idVec3 simpleTri[3];
#endif // __GAME_CLIENT_MOVEABLE_H__