871 lines
22 KiB
C++
871 lines
22 KiB
C++
|
// Copyright (C) 2007 Id Software, Inc.
|
||
|
//
|
||
|
|
||
|
#include "precompiled.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
|
||
|
#define new DEBUG_NEW
|
||
|
#undef THIS_FILE
|
||
|
static char THIS_FILE[] = __FILE__;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#include "AFEntity.h"
|
||
|
#include "ContentMask.h"
|
||
|
#include "Item.h"
|
||
|
#include "Player.h"
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
idAFEntity_Base
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
CLASS_DECLARATION( idAnimatedEntity, idAFEntity_Base )
|
||
|
END_CLASS
|
||
|
|
||
|
static const float BOUNCE_SOUND_MIN_VELOCITY = 80.0f;
|
||
|
static const float BOUNCE_SOUND_MAX_VELOCITY = 200.0f;
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::idAFEntity_Base
|
||
|
================
|
||
|
*/
|
||
|
idAFEntity_Base::idAFEntity_Base( void ) {
|
||
|
combatModel = NULL;
|
||
|
combatModelContents = 0;
|
||
|
nextSoundTime = 0;
|
||
|
spawnOrigin.Zero();
|
||
|
spawnAxis.Identity();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::~idAFEntity_Base
|
||
|
================
|
||
|
*/
|
||
|
idAFEntity_Base::~idAFEntity_Base( void ) {
|
||
|
gameLocal.clip.DeleteClipModel( combatModel );
|
||
|
combatModel = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::Spawn
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::Spawn( void ) {
|
||
|
spawnOrigin = GetPhysics()->GetOrigin();
|
||
|
spawnAxis = GetPhysics()->GetAxis();
|
||
|
nextSoundTime = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::LoadAF
|
||
|
================
|
||
|
*/
|
||
|
bool idAFEntity_Base::LoadAF( void ) {
|
||
|
idStr fileName;
|
||
|
|
||
|
if ( !spawnArgs.GetString( "articulatedFigure", "*unknown*", fileName ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
af.SetAnimator( GetAnimator() );
|
||
|
if ( !af.Load( this, fileName ) ) {
|
||
|
gameLocal.Error( "idAFEntity_Base::LoadAF: Couldn't load af file '%s' on entity '%s'", fileName.c_str(), name.c_str() );
|
||
|
}
|
||
|
|
||
|
af.Start();
|
||
|
|
||
|
af.GetPhysics()->Rotate( spawnAxis.ToRotation() );
|
||
|
af.GetPhysics()->Translate( spawnOrigin );
|
||
|
|
||
|
LoadState( spawnArgs );
|
||
|
|
||
|
af.UpdateAnimation();
|
||
|
animator.CreateFrame( gameLocal.time, true );
|
||
|
UpdateVisuals();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::Think
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::Think( void ) {
|
||
|
RunPhysics();
|
||
|
UpdateAnimation();
|
||
|
if ( thinkFlags & TH_UPDATEVISUALS ) {
|
||
|
Present();
|
||
|
LinkCombat();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::BodyForClipModelId
|
||
|
================
|
||
|
*/
|
||
|
int idAFEntity_Base::BodyForClipModelId( int id ) const {
|
||
|
return af.BodyForClipModelId( id );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::SaveState
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::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() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::LoadState
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::LoadState( const idDict &args ) {
|
||
|
af.LoadState( args );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::InitBind
|
||
|
================
|
||
|
*/
|
||
|
bool idAFEntity_Base::InitBind( idEntity *master ) {
|
||
|
bool ret = idEntity::InitBind( master );
|
||
|
if ( !ret ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( master ) {
|
||
|
AddBindConstraints();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::Unbind
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::Unbind( void ) {
|
||
|
RemoveBindConstraints();
|
||
|
|
||
|
idEntity::Unbind();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::AddBindConstraints
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::AddBindConstraints( void ) {
|
||
|
af.AddBindConstraints();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::RemoveBindConstraints
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::RemoveBindConstraints( void ) {
|
||
|
af.RemoveBindConstraints();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::GetImpactInfo
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
|
||
|
if ( af.IsActive() ) {
|
||
|
af.GetImpactInfo( ent, id, point, info );
|
||
|
} else {
|
||
|
idEntity::GetImpactInfo( ent, id, point, info );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::ApplyImpulse
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
|
||
|
if ( af.IsLoaded() ) {
|
||
|
af.ApplyImpulse( ent, id, point, impulse );
|
||
|
}
|
||
|
if ( !af.IsActive() ) {
|
||
|
idEntity::ApplyImpulse( ent, id, point, impulse );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::AddForce
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
|
||
|
if ( af.IsLoaded() ) {
|
||
|
af.AddForce( ent, id, point, force );
|
||
|
}
|
||
|
if ( !af.IsActive() ) {
|
||
|
idEntity::AddForce( ent, id, point, force );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::Collide
|
||
|
================
|
||
|
*/
|
||
|
bool idAFEntity_Base::Collide( const trace_t &collision, const idVec3 &velocity, int bodyId ) {
|
||
|
float v, f;
|
||
|
|
||
|
if ( af.IsActive() ) {
|
||
|
v = -( velocity * collision.c.normal );
|
||
|
if ( v > BOUNCE_SOUND_MIN_VELOCITY && gameLocal.time > nextSoundTime ) {
|
||
|
f = v > BOUNCE_SOUND_MAX_VELOCITY ? 1.0f : idMath::Sqrt( v - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / idMath::Sqrt( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) );
|
||
|
if ( StartSound( "snd_bounce", SND_ANY, 0, NULL ) ) {
|
||
|
SetSoundVolume( f );
|
||
|
}
|
||
|
nextSoundTime = gameLocal.time + 500;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::GetPhysicsToVisualTransform
|
||
|
================
|
||
|
*/
|
||
|
bool idAFEntity_Base::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
|
||
|
if ( af.IsActive() ) {
|
||
|
af.GetPhysicsToVisualTransform( origin, axis );
|
||
|
return true;
|
||
|
}
|
||
|
return idEntity::GetPhysicsToVisualTransform( origin, axis );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::UpdateAnimationControllers
|
||
|
================
|
||
|
*/
|
||
|
bool idAFEntity_Base::UpdateAnimationControllers( void ) {
|
||
|
if ( af.IsActive() ) {
|
||
|
if ( af.UpdateAnimation() ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::SetCombatModel
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::SetCombatModel( void ) {
|
||
|
gameLocal.clip.DeleteClipModel( combatModel );
|
||
|
combatModel = new idClipModel( modelDefHandle );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::SetCombatContents
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::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 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::LinkCombat
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::LinkCombat( void ) {
|
||
|
if ( fl.hidden ) {
|
||
|
return;
|
||
|
}
|
||
|
if ( combatModel ) {
|
||
|
combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::UnLinkCombat
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::UnLinkCombat( void ) {
|
||
|
if ( combatModel != NULL ) {
|
||
|
combatModel->Unlink( gameLocal.clip );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
idAFEntity_Base::ShowEditingDialog
|
||
|
===============
|
||
|
*/
|
||
|
void idAFEntity_Base::ShowEditingDialog( void ) {
|
||
|
// FIXME:
|
||
|
// common->InitTool( EDITOR_AF, &spawnArgs );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Base::DropAFs
|
||
|
|
||
|
The entity should have the following key/value pairs set:
|
||
|
"def_drop<type>AF" "af def"
|
||
|
"drop<type>Skin" "skin name"
|
||
|
To drop multiple articulated figures the following key/value pairs can be used:
|
||
|
"def_drop<type>AF*" "af def"
|
||
|
where * is an aribtrary string.
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Base::DropAFs( idEntity *ent, const char *type, idList<idEntity *> *list ) {
|
||
|
const idKeyValue *kv;
|
||
|
const char *skinName;
|
||
|
idEntity *newEnt;
|
||
|
idAFEntity_Base *af;
|
||
|
idDict args;
|
||
|
const idDeclSkin *skin;
|
||
|
|
||
|
// drop the articulated figures
|
||
|
kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), NULL );
|
||
|
while ( kv ) {
|
||
|
|
||
|
args.Set( "classname", kv->GetValue() );
|
||
|
gameLocal.SpawnEntityDef( args, true, &newEnt );
|
||
|
|
||
|
if ( newEnt && newEnt->IsType( idAFEntity_Base::Type ) ) {
|
||
|
af = static_cast<idAFEntity_Base *>(newEnt);
|
||
|
af->GetPhysics()->SetOrigin( ent->GetPhysics()->GetOrigin() );
|
||
|
af->GetPhysics()->SetAxis( ent->GetPhysics()->GetAxis() );
|
||
|
af->af.SetupPose( ent, gameLocal.time );
|
||
|
if ( list ) {
|
||
|
list->Append( af );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), kv );
|
||
|
}
|
||
|
|
||
|
// change the skin to hide all the dropped articulated figures
|
||
|
skinName = ent->spawnArgs.GetString( va( "skin_drop%s", type ) );
|
||
|
if ( skinName[0] ) {
|
||
|
skin = declHolder.declSkinType.LocalFind( skinName );
|
||
|
ent->SetSkin( skin );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
idAFEntity_Generic
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Generic )
|
||
|
EVENT( EV_Activate, idAFEntity_Generic::Event_Activate )
|
||
|
END_CLASS
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Generic::idAFEntity_Generic
|
||
|
================
|
||
|
*/
|
||
|
idAFEntity_Generic::idAFEntity_Generic( void ) {
|
||
|
keepRunningPhysics = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Generic::~idAFEntity_Generic
|
||
|
================
|
||
|
*/
|
||
|
idAFEntity_Generic::~idAFEntity_Generic( void ) {
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Generic::Think
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Generic::Think( void ) {
|
||
|
idAFEntity_Base::Think();
|
||
|
|
||
|
if ( keepRunningPhysics ) {
|
||
|
BecomeActive( TH_PHYSICS );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Generic::Spawn
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Generic::Spawn( void ) {
|
||
|
if ( !LoadAF() ) {
|
||
|
gameLocal.Error( "Couldn't load af file on entity '%s'", name.c_str() );
|
||
|
}
|
||
|
|
||
|
SetCombatModel();
|
||
|
|
||
|
SetPhysics( af.GetPhysics() );
|
||
|
|
||
|
af.GetPhysics()->PutToRest();
|
||
|
if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
|
||
|
af.GetPhysics()->Activate();
|
||
|
}
|
||
|
|
||
|
fl.takedamage = true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idAFEntity_Generic::Event_Activate
|
||
|
================
|
||
|
*/
|
||
|
void idAFEntity_Generic::Event_Activate( idEntity *activator ) {
|
||
|
float delay;
|
||
|
idVec3 init_velocity, init_avelocity;
|
||
|
|
||
|
Show();
|
||
|
|
||
|
af.GetPhysics()->EnableImpact();
|
||
|
af.GetPhysics()->Activate();
|
||
|
|
||
|
spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
|
||
|
spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
|
||
|
|
||
|
delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
|
||
|
if ( delay == 0.0f ) {
|
||
|
af.GetPhysics()->SetLinearVelocity( init_velocity );
|
||
|
} else {
|
||
|
PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
|
||
|
}
|
||
|
|
||
|
delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
|
||
|
if ( delay == 0.0f ) {
|
||
|
af.GetPhysics()->SetAngularVelocity( init_avelocity );
|
||
|
} else {
|
||
|
PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
editor support routines
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameEdit::AF_SpawnEntity
|
||
|
================
|
||
|
*/
|
||
|
bool idGameEdit::AF_SpawnEntity( const char *fileName ) {
|
||
|
idDict args;
|
||
|
idPlayer *player;
|
||
|
idAFEntity_Generic *ent;
|
||
|
const idDeclAF *af;
|
||
|
idVec3 org;
|
||
|
float yaw;
|
||
|
|
||
|
player = gameLocal.GetLocalPlayer();
|
||
|
if ( !player || !gameLocal.CheatsOk( false ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
af = declHolder.declAFType.LocalFind( fileName, false );
|
||
|
if ( !af ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
yaw = player->viewAngles.yaw;
|
||
|
args.Set( "angle", va( "%f", yaw + 180 ) );
|
||
|
org = player->GetPhysics()->GetOrigin() + idAngles( 0, yaw, 0 ).ToForward() * 80 + idVec3( 0, 0, 1 );
|
||
|
args.Set( "origin", org.ToString() );
|
||
|
args.Set( "spawnclass", "idAFEntity_Generic" );
|
||
|
if ( af->model[0] ) {
|
||
|
args.Set( "model", af->model.c_str() );
|
||
|
} else {
|
||
|
args.Set( "model", fileName );
|
||
|
}
|
||
|
if ( af->skin[0] ) {
|
||
|
args.Set( "skin", af->skin.c_str() );
|
||
|
}
|
||
|
args.Set( "articulatedFigure", fileName );
|
||
|
args.Set( "nodrop", "1" );
|
||
|
ent = static_cast<idAFEntity_Generic *>(gameLocal.SpawnEntityType( idAFEntity_Generic::Type, true, &args));
|
||
|
|
||
|
// always update this entity
|
||
|
ent->BecomeActive( TH_THINK );
|
||
|
ent->KeepRunningPhysics();
|
||
|
|
||
|
// player->dragEntity.SetSelected( ent );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameEdit::AF_UpdateEntities
|
||
|
================
|
||
|
*/
|
||
|
void idGameEdit::AF_UpdateEntities( const char *fileName ) {
|
||
|
idEntity *ent;
|
||
|
idAFEntity_Base *af;
|
||
|
idStr name;
|
||
|
|
||
|
name = fileName;
|
||
|
name.StripFileExtension();
|
||
|
|
||
|
// reload any idAFEntity_Generic which uses the given articulated figure file
|
||
|
for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
if ( ent->IsType( idAFEntity_Base::Type ) ) {
|
||
|
af = reinterpret_cast< idAFEntity_Base* >( ent );
|
||
|
if ( name.Icmp( af->GetAFName() ) == 0 ) {
|
||
|
af->LoadAF();
|
||
|
af->GetAFPhysics()->PutToRest();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameEdit::AF_UndoChanges
|
||
|
================
|
||
|
*/
|
||
|
void idGameEdit::AF_UndoChanges( void ) {
|
||
|
int i, c;
|
||
|
idEntity *ent;
|
||
|
idAFEntity_Base *af;
|
||
|
idDeclAF *decl;
|
||
|
|
||
|
c = declHolder.declAFType.Num();
|
||
|
for ( i = 0; i < c; i++ ) {
|
||
|
decl = const_cast< idDeclAF* >( declHolder.declAFType.LocalFindByIndex( i, false ) );
|
||
|
if ( !decl->modified ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
decl->Invalidate();
|
||
|
declHolder.declAFType.LocalFind( decl->GetName() );
|
||
|
|
||
|
// reload all AF entities using the file
|
||
|
for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
if ( ent->IsType( idAFEntity_Base::Type ) ) {
|
||
|
af = static_cast< idAFEntity_Base* >( ent );
|
||
|
if ( idStr::Icmp( decl->GetName(), af->GetAFName() ) == 0 ) {
|
||
|
af->LoadAF();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
GetJointTransform
|
||
|
================
|
||
|
*/
|
||
|
typedef struct {
|
||
|
renderEntity_t *ent;
|
||
|
const idMD5Joint *joints;
|
||
|
} jointTransformData_t;
|
||
|
|
||
|
static bool GetJointTransform( void *model, const idJointMat *frame, const char *jointName, idVec3 &origin, idMat3 &axis ) {
|
||
|
int i;
|
||
|
jointTransformData_t *data = reinterpret_cast<jointTransformData_t *>(model);
|
||
|
|
||
|
for ( i = 0; i < data->ent->numJoints; i++ ) {
|
||
|
if ( data->joints[i].name.Icmp( jointName ) == 0 ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ( i >= data->ent->numJoints ) {
|
||
|
return false;
|
||
|
}
|
||
|
origin = frame[i].ToVec3();
|
||
|
axis = frame[i].ToMat3();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
GetArgString
|
||
|
================
|
||
|
*/
|
||
|
static const char *GetArgString( const idDict &args, const idDict *defArgs, const char *key ) {
|
||
|
const char *s;
|
||
|
|
||
|
s = args.GetString( key );
|
||
|
if ( !s[0] && defArgs ) {
|
||
|
s = defArgs->GetString( key );
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
idGameEdit::AF_CreateMesh
|
||
|
================
|
||
|
*/
|
||
|
idRenderModel *idGameEdit::AF_CreateMesh( const idDict &args, idVec3 &meshOrigin, idMat3 &meshAxis, bool &poseIsSet ) {
|
||
|
int i, jointNum;
|
||
|
const idDeclAF *af;
|
||
|
const idDeclAF_Body *fb;
|
||
|
renderEntity_t ent;
|
||
|
idVec3 origin, *bodyOrigin, *newBodyOrigin, *modifiedOrigin;
|
||
|
idMat3 axis, *bodyAxis, *newBodyAxis, *modifiedAxis;
|
||
|
declAFJointMod_t *jointMod;
|
||
|
idAngles angles;
|
||
|
const idDict *defArgs;
|
||
|
const idKeyValue *arg;
|
||
|
idStr name;
|
||
|
jointTransformData_t data;
|
||
|
const char *classname, *afName, *modelName;
|
||
|
idRenderModel *md5;
|
||
|
const idDeclModelDef *modelDef;
|
||
|
const idMD5Anim *MD5anim;
|
||
|
const idMD5Joint *MD5joint;
|
||
|
const idMD5Joint *MD5joints;
|
||
|
int numMD5joints;
|
||
|
idJointMat *originalJoints;
|
||
|
int parentNum;
|
||
|
|
||
|
poseIsSet = false;
|
||
|
meshOrigin.Zero();
|
||
|
meshAxis.Identity();
|
||
|
|
||
|
classname = args.GetString( "classname" );
|
||
|
defArgs = gameLocal.FindEntityDefDict( classname );
|
||
|
|
||
|
// get the articulated figure
|
||
|
afName = GetArgString( args, defArgs, "articulatedFigure" );
|
||
|
af = declHolder.declAFType.LocalFind( afName );
|
||
|
if ( !af ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// get the md5 model
|
||
|
modelName = GetArgString( args, defArgs, "model" );
|
||
|
modelDef = gameLocal.declModelDefType.LocalFind( modelName, false );
|
||
|
if ( !modelDef ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// make sure model hasn't been purged
|
||
|
if ( modelDef->ModelHandle() && !modelDef->ModelHandle()->IsLoaded() ) {
|
||
|
modelDef->ModelHandle()->LoadModel();
|
||
|
}
|
||
|
|
||
|
// get the md5
|
||
|
md5 = modelDef->ModelHandle();
|
||
|
if ( !md5 || md5->IsDefaultModel() ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// get the articulated figure pose anim
|
||
|
int animNum = modelDef->GetAnim( "af_pose" );
|
||
|
if ( !animNum ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
const idAnim *anim = modelDef->GetAnim( animNum );
|
||
|
if ( !anim ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
MD5anim = anim->MD5Anim( 0 );
|
||
|
MD5joints = md5->GetJoints();
|
||
|
numMD5joints = md5->NumJoints();
|
||
|
|
||
|
// setup a render entity
|
||
|
memset( &ent, 0, sizeof( ent ) );
|
||
|
ent.customSkin = modelDef->GetSkin();
|
||
|
ent.bounds.Clear();
|
||
|
ent.numJoints = numMD5joints;
|
||
|
ent.joints = ( idJointMat * )_alloca16( ent.numJoints * sizeof( *ent.joints ) );
|
||
|
|
||
|
// create animation from of the af_pose
|
||
|
ANIM_CreateAnimFrame( md5, MD5anim, ent.numJoints, ent.joints, 1, modelDef->GetVisualOffset(), false );
|
||
|
|
||
|
// buffers to store the initial origin and axis for each body
|
||
|
bodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
|
||
|
bodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
|
||
|
newBodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
|
||
|
newBodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
|
||
|
|
||
|
// finish the AF positions
|
||
|
data.ent = &ent;
|
||
|
data.joints = MD5joints;
|
||
|
af->Finish( GetJointTransform, ent.joints, &data );
|
||
|
|
||
|
// get the initial origin and axis for each AF body
|
||
|
for ( i = 0; i < af->bodies.Num(); i++ ) {
|
||
|
fb = af->bodies[i];
|
||
|
|
||
|
if ( fb->modelType == TRM_BONE ) {
|
||
|
// axis of bone trace model
|
||
|
axis[2] = fb->v2.ToVec3() - fb->v1.ToVec3();
|
||
|
axis[2].Normalize();
|
||
|
axis[2].NormalVectors( axis[0], axis[1] );
|
||
|
axis[1] = -axis[1];
|
||
|
} else {
|
||
|
axis = fb->angles.ToMat3();
|
||
|
}
|
||
|
|
||
|
newBodyOrigin[i] = bodyOrigin[i] = fb->origin.ToVec3();
|
||
|
newBodyAxis[i] = bodyAxis[i] = axis;
|
||
|
}
|
||
|
|
||
|
// get any new body transforms stored in the key/value pairs
|
||
|
for ( arg = args.MatchPrefix( "body ", NULL ); arg; arg = args.MatchPrefix( "body ", arg ) ) {
|
||
|
name = arg->GetKey();
|
||
|
name.Strip( "body " );
|
||
|
for ( i = 0; i < af->bodies.Num(); i++ ) {
|
||
|
fb = af->bodies[i];
|
||
|
if ( fb->name.Icmp( name ) == 0 ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ( i >= af->bodies.Num() ) {
|
||
|
continue;
|
||
|
}
|
||
|
sscanf( arg->GetValue(), "%f %f %f %f %f %f", &origin.x, &origin.y, &origin.z, &angles.pitch, &angles.yaw, &angles.roll );
|
||
|
|
||
|
if ( fb->jointName.Icmp( "origin" ) == 0 ) {
|
||
|
meshAxis = bodyAxis[i].Transpose() * angles.ToMat3();
|
||
|
meshOrigin = origin - bodyOrigin[i] * meshAxis;
|
||
|
poseIsSet = true;
|
||
|
} else {
|
||
|
newBodyOrigin[i] = origin;
|
||
|
newBodyAxis[i] = angles.ToMat3();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// save the original joints
|
||
|
originalJoints = ( idJointMat * )_alloca16( numMD5joints * sizeof( originalJoints[0] ) );
|
||
|
memcpy( originalJoints, ent.joints, numMD5joints * sizeof( originalJoints[0] ) );
|
||
|
|
||
|
// buffer to store the joint mods
|
||
|
jointMod = (declAFJointMod_t *) _alloca16( numMD5joints * sizeof( declAFJointMod_t ) );
|
||
|
memset( jointMod, -1, numMD5joints * sizeof( declAFJointMod_t ) );
|
||
|
modifiedOrigin = (idVec3 *) _alloca16( numMD5joints * sizeof( idVec3 ) );
|
||
|
memset( modifiedOrigin, 0, numMD5joints * sizeof( idVec3 ) );
|
||
|
modifiedAxis = (idMat3 *) _alloca16( numMD5joints * sizeof( idMat3 ) );
|
||
|
memset( modifiedAxis, 0, numMD5joints * sizeof( idMat3 ) );
|
||
|
|
||
|
// get all the joint modifications
|
||
|
for ( i = 0; i < af->bodies.Num(); i++ ) {
|
||
|
fb = af->bodies[i];
|
||
|
|
||
|
if ( fb->jointName.Icmp( "origin" ) == 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
for ( jointNum = 0; jointNum < numMD5joints; jointNum++ ) {
|
||
|
if ( MD5joints[jointNum].name.Icmp( fb->jointName ) == 0 ) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( jointNum >= 0 && jointNum < ent.numJoints ) {
|
||
|
jointMod[ jointNum ] = fb->jointMod;
|
||
|
modifiedAxis[ jointNum ] = ( bodyAxis[i] * originalJoints[jointNum].ToMat3().Transpose() ).Transpose() * ( newBodyAxis[i] * meshAxis.Transpose() );
|
||
|
// FIXME: calculate correct modifiedOrigin
|
||
|
modifiedOrigin[ jointNum ] = originalJoints[ jointNum ].ToVec3();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// apply joint modifications to the skeleton
|
||
|
MD5joint = MD5joints + 1;
|
||
|
for( i = 1; i < numMD5joints; i++, MD5joint++ ) {
|
||
|
|
||
|
parentNum = MD5joint->parent - MD5joints;
|
||
|
idMat3 parentAxis = originalJoints[ parentNum ].ToMat3();
|
||
|
idMat3 localm = originalJoints[i].ToMat3() * parentAxis.Transpose();
|
||
|
idVec3 localt = ( originalJoints[i].ToVec3() - originalJoints[ parentNum ].ToVec3() ) * parentAxis.Transpose();
|
||
|
|
||
|
switch( jointMod[i] ) {
|
||
|
case DECLAF_JOINTMOD_ORIGIN: {
|
||
|
ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
|
||
|
ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
|
||
|
break;
|
||
|
}
|
||
|
case DECLAF_JOINTMOD_AXIS: {
|
||
|
ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
|
||
|
ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
|
||
|
break;
|
||
|
}
|
||
|
case DECLAF_JOINTMOD_BOTH: {
|
||
|
ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
|
||
|
ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
|
||
|
break;
|
||
|
}
|
||
|
default: {
|
||
|
ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
|
||
|
ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// instantiate a mesh using the joint information from the render entity
|
||
|
return renderSystem->InstantiateDynamicModel( md5, &ent );
|
||
|
}
|