2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "Game_local.h"
36 ===============================================================================
40 ===============================================================================
43 CLASS_DECLARATION( idEntity, idMultiModelAF )
51 void idMultiModelAF::Spawn( void ) {
52 physicsObj.SetSelf( this );
57 idMultiModelAF::~idMultiModelAF
60 idMultiModelAF::~idMultiModelAF( void ) {
63 for ( i = 0; i < modelDefHandles.Num(); i++ ) {
64 if ( modelDefHandles[i] != -1 ) {
65 gameRenderWorld->FreeEntityDef( modelDefHandles[i] );
66 modelDefHandles[i] = -1;
73 idMultiModelAF::SetModelForId
76 void idMultiModelAF::SetModelForId( int id, const idStr &modelName ) {
77 modelHandles.AssureSize( id+1, NULL );
78 modelDefHandles.AssureSize( id+1, -1 );
79 modelHandles[id] = renderModelManager->FindModel( modelName );
84 idMultiModelAF::Present
87 void idMultiModelAF::Present( void ) {
90 // don't present to the renderer if the entity hasn't changed
91 if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
94 BecomeInactive( TH_UPDATEVISUALS );
96 for ( i = 0; i < modelHandles.Num(); i++ ) {
98 if ( !modelHandles[i] ) {
102 renderEntity.origin = physicsObj.GetOrigin( i );
103 renderEntity.axis = physicsObj.GetAxis( i );
104 renderEntity.hModel = modelHandles[i];
105 renderEntity.bodyId = i;
107 // add to refresh list
108 if ( modelDefHandles[i] == -1 ) {
109 modelDefHandles[i] = gameRenderWorld->AddEntityDef( &renderEntity );
111 gameRenderWorld->UpdateEntityDef( modelDefHandles[i], &renderEntity );
118 idMultiModelAF::Think
121 void idMultiModelAF::Think( void ) {
128 ===============================================================================
132 ===============================================================================
135 CLASS_DECLARATION( idMultiModelAF, idChain )
142 builds a chain hanging down from the ceiling
143 the highest link is a child of the link below it etc.
144 this allows an object to be attached to multiple chains while keeping a single tree structure
147 void idChain::BuildChain( const idStr &name, const idVec3 &origin, float linkLength, float linkWidth, float density, int numLinks, bool bindToWorld ) {
149 float halfLinkLength = linkLength * 0.5f;
152 idAFBody *body, *lastBody;
153 idAFConstraint_BallAndSocketJoint *bsj;
154 idAFConstraint_UniversalJoint *uj;
157 // create a trace model
158 trm = idTraceModel( linkLength, linkWidth );
159 trm.Translate( -trm.offset );
161 org = origin - idVec3( 0, 0, halfLinkLength );
164 for ( i = 0; i < numLinks; i++ ) {
167 clip = new idClipModel( trm );
168 clip->SetContents( CONTENTS_SOLID );
169 clip->Link( gameLocal.clip, this, 0, org, mat3_identity );
170 body = new idAFBody( name + idStr(i), clip, density );
171 physicsObj.AddBody( body );
173 // visual model for body
174 SetModelForId( physicsObj.GetBodyId( body ), spawnArgs.GetString( "model" ) );
179 uj = new idAFConstraint_UniversalJoint( name + idStr(i), body, lastBody );
180 uj->SetShafts( idVec3( 0, 0, -1 ), idVec3( 0, 0, 1 ) );
181 //uj->SetConeLimit( idVec3( 0, 0, -1 ), 30.0f );
182 //uj->SetPyramidLimit( idVec3( 0, 0, -1 ), idVec3( 1, 0, 0 ), 90.0f, 30.0f );
185 uj = new idAFConstraint_UniversalJoint( name + idStr(i), lastBody, body );
186 uj->SetShafts( idVec3( 0, 0, 1 ), idVec3( 0, 0, -1 ) );
187 //uj->SetConeLimit( idVec3( 0, 0, 1 ), 30.0f );
189 uj->SetAnchor( org + idVec3( 0, 0, halfLinkLength ) );
190 uj->SetFriction( 0.9f );
191 physicsObj.AddConstraint( uj );
195 bsj = new idAFConstraint_BallAndSocketJoint( "joint" + idStr(i), lastBody, body );
196 bsj->SetAnchor( org + idVec3( 0, 0, halfLinkLength ) );
197 bsj->SetConeLimit( idVec3( 0, 0, 1 ), 60.0f, idVec3( 0, 0, 1 ) );
198 physicsObj.AddConstraint( bsj );
202 org[2] -= linkLength;
213 void idChain::Spawn( void ) {
215 float length, linkLength, linkWidth, density;
219 spawnArgs.GetBool( "drop", "0", drop );
220 spawnArgs.GetInt( "links", "3", numLinks );
221 spawnArgs.GetFloat( "length", idStr( numLinks * 32.0f ), length );
222 spawnArgs.GetFloat( "width", "8", linkWidth );
223 spawnArgs.GetFloat( "density", "0.2", density );
224 linkLength = length / numLinks;
225 origin = GetPhysics()->GetOrigin();
227 // initialize physics
228 physicsObj.SetSelf( this );
229 physicsObj.SetGravity( gameLocal.GetGravity() );
230 physicsObj.SetClipMask( MASK_SOLID | CONTENTS_BODY );
231 SetPhysics( &physicsObj );
233 BuildChain( "link", origin, linkLength, linkWidth, density, numLinks, !drop );
237 ===============================================================================
241 ===============================================================================
244 CLASS_DECLARATION( idAnimatedEntity, idAFAttachment )
248 =====================
249 idAFAttachment::idAFAttachment
250 =====================
252 idAFAttachment::idAFAttachment( void ) {
256 attachJoint = INVALID_JOINT;
260 =====================
261 idAFAttachment::~idAFAttachment
262 =====================
264 idAFAttachment::~idAFAttachment( void ) {
266 StopSound( SND_CHANNEL_ANY, false );
273 =====================
274 idAFAttachment::Spawn
275 =====================
277 void idAFAttachment::Spawn( void ) {
278 idleAnim = animator.GetAnim( "idle" );
282 =====================
283 idAFAttachment::SetBody
284 =====================
286 void idAFAttachment::SetBody( idEntity *bodyEnt, const char *model, jointHandle_t attachJoint ) {
290 this->attachJoint = attachJoint;
292 fl.takedamage = true;
294 bleed = body->spawnArgs.GetBool( "bleed" );
295 spawnArgs.SetBool( "bleed", bleed );
299 =====================
300 idAFAttachment::ClearBody
301 =====================
303 void idAFAttachment::ClearBody( void ) {
305 attachJoint = INVALID_JOINT;
310 =====================
311 idAFAttachment::GetBody
312 =====================
314 idEntity *idAFAttachment::GetBody( void ) const {
322 archive object for savegame file
325 void idAFAttachment::Save( idSaveGame *savefile ) const {
326 savefile->WriteObject( body );
327 savefile->WriteInt( idleAnim );
328 savefile->WriteJoint( attachJoint );
333 idAFAttachment::Restore
335 unarchives object from save game file
338 void idAFAttachment::Restore( idRestoreGame *savefile ) {
339 savefile->ReadObject( reinterpret_cast<idClass *&>( body ) );
340 savefile->ReadInt( idleAnim );
341 savefile->ReadJoint( attachJoint );
352 void idAFAttachment::Hide( void ) {
362 void idAFAttachment::Show( void ) {
369 idAFAttachment::Damage
371 Pass damage to body at the bindjoint
374 void idAFAttachment::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
375 const char *damageDefName, const float damageScale, const int location ) {
378 body->Damage( inflictor, attacker, dir, damageDefName, damageScale, attachJoint );
384 idAFAttachment::AddDamageEffect
387 void idAFAttachment::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
389 trace_t c = collision;
390 c.c.id = JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint );
391 body->AddDamageEffect( c, velocity, damageDefName );
397 idAFAttachment::GetImpactInfo
400 void idAFAttachment::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
402 body->GetImpactInfo( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, info );
404 idEntity::GetImpactInfo( ent, id, point, info );
410 idAFAttachment::ApplyImpulse
413 void idAFAttachment::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
415 body->ApplyImpulse( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, impulse );
417 idEntity::ApplyImpulse( ent, id, point, impulse );
423 idAFAttachment::AddForce
426 void idAFAttachment::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
428 body->AddForce( ent, JOINT_HANDLE_TO_CLIPMODEL_ID( attachJoint ), point, force );
430 idEntity::AddForce( ent, id, point, force );
436 idAFAttachment::PlayIdleAnim
439 void idAFAttachment::PlayIdleAnim( int blendTime ) {
440 if ( idleAnim && ( idleAnim != animator.CurrentAnim( ANIMCHANNEL_ALL )->AnimNum() ) ) {
441 animator.CycleAnim( ANIMCHANNEL_ALL, idleAnim, gameLocal.time, blendTime );
447 idAfAttachment::Think
450 void idAFAttachment::Think( void ) {
451 idAnimatedEntity::Think();
452 if ( thinkFlags & TH_UPDATEPARTICLES ) {
453 UpdateDamageEffects();
459 idAFAttachment::SetCombatModel
462 void idAFAttachment::SetCombatModel( void ) {
464 combatModel->Unlink();
465 combatModel->LoadModel( modelDefHandle );
467 combatModel = new idClipModel( modelDefHandle );
469 combatModel->SetOwner( body );
474 idAFAttachment::GetCombatModel
477 idClipModel *idAFAttachment::GetCombatModel( void ) const {
483 idAFAttachment::LinkCombat
486 void idAFAttachment::LinkCombat( void ) {
492 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
498 idAFAttachment::UnlinkCombat
501 void idAFAttachment::UnlinkCombat( void ) {
503 combatModel->Unlink();
509 ===============================================================================
513 ===============================================================================
516 const idEventDef EV_SetConstraintPosition( "SetConstraintPosition", "sv" );
518 CLASS_DECLARATION( idAnimatedEntity, idAFEntity_Base )
519 EVENT( EV_SetConstraintPosition, idAFEntity_Base::Event_SetConstraintPosition )
522 static const float BOUNCE_SOUND_MIN_VELOCITY = 80.0f;
523 static const float BOUNCE_SOUND_MAX_VELOCITY = 200.0f;
527 idAFEntity_Base::idAFEntity_Base
530 idAFEntity_Base::idAFEntity_Base( void ) {
532 combatModelContents = 0;
535 spawnAxis.Identity();
540 idAFEntity_Base::~idAFEntity_Base
543 idAFEntity_Base::~idAFEntity_Base( void ) {
550 idAFEntity_Base::Save
553 void idAFEntity_Base::Save( idSaveGame *savefile ) const {
554 savefile->WriteInt( combatModelContents );
555 savefile->WriteClipModel( combatModel );
556 savefile->WriteVec3( spawnOrigin );
557 savefile->WriteMat3( spawnAxis );
558 savefile->WriteInt( nextSoundTime );
564 idAFEntity_Base::Restore
567 void idAFEntity_Base::Restore( idRestoreGame *savefile ) {
568 savefile->ReadInt( combatModelContents );
569 savefile->ReadClipModel( combatModel );
570 savefile->ReadVec3( spawnOrigin );
571 savefile->ReadMat3( spawnAxis );
572 savefile->ReadInt( nextSoundTime );
575 af.Restore( savefile );
580 idAFEntity_Base::Spawn
583 void idAFEntity_Base::Spawn( void ) {
584 spawnOrigin = GetPhysics()->GetOrigin();
585 spawnAxis = GetPhysics()->GetAxis();
591 idAFEntity_Base::LoadAF
594 bool idAFEntity_Base::LoadAF( void ) {
597 if ( !spawnArgs.GetString( "articulatedFigure", "*unknown*", fileName ) ) {
601 af.SetAnimator( GetAnimator() );
602 if ( !af.Load( this, fileName ) ) {
603 gameLocal.Error( "idAFEntity_Base::LoadAF: Couldn't load af file '%s' on entity '%s'", fileName.c_str(), name.c_str() );
608 af.GetPhysics()->Rotate( spawnAxis.ToRotation() );
609 af.GetPhysics()->Translate( spawnOrigin );
611 LoadState( spawnArgs );
613 af.UpdateAnimation();
614 animator.CreateFrame( gameLocal.time, true );
622 idAFEntity_Base::Think
625 void idAFEntity_Base::Think( void ) {
628 if ( thinkFlags & TH_UPDATEVISUALS ) {
636 idAFEntity_Base::BodyForClipModelId
639 int idAFEntity_Base::BodyForClipModelId( int id ) const {
640 return af.BodyForClipModelId( id );
645 idAFEntity_Base::SaveState
648 void idAFEntity_Base::SaveState( idDict &args ) const {
649 const idKeyValue *kv;
651 // save the ragdoll pose
652 af.SaveState( args );
654 // save all the bind constraints
655 kv = spawnArgs.MatchPrefix( "bindConstraint ", NULL );
657 args.Set( kv->GetKey(), kv->GetValue() );
658 kv = spawnArgs.MatchPrefix( "bindConstraint ", kv );
661 // save the bind if it exists
662 kv = spawnArgs.FindKey( "bind" );
664 args.Set( kv->GetKey(), kv->GetValue() );
666 kv = spawnArgs.FindKey( "bindToJoint" );
668 args.Set( kv->GetKey(), kv->GetValue() );
670 kv = spawnArgs.FindKey( "bindToBody" );
672 args.Set( kv->GetKey(), kv->GetValue() );
678 idAFEntity_Base::LoadState
681 void idAFEntity_Base::LoadState( const idDict &args ) {
682 af.LoadState( args );
687 idAFEntity_Base::AddBindConstraints
690 void idAFEntity_Base::AddBindConstraints( void ) {
691 af.AddBindConstraints();
696 idAFEntity_Base::RemoveBindConstraints
699 void idAFEntity_Base::RemoveBindConstraints( void ) {
700 af.RemoveBindConstraints();
705 idAFEntity_Base::GetImpactInfo
708 void idAFEntity_Base::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
709 if ( af.IsActive() ) {
710 af.GetImpactInfo( ent, id, point, info );
712 idEntity::GetImpactInfo( ent, id, point, info );
718 idAFEntity_Base::ApplyImpulse
721 void idAFEntity_Base::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
722 if ( af.IsLoaded() ) {
723 af.ApplyImpulse( ent, id, point, impulse );
725 if ( !af.IsActive() ) {
726 idEntity::ApplyImpulse( ent, id, point, impulse );
732 idAFEntity_Base::AddForce
735 void idAFEntity_Base::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
736 if ( af.IsLoaded() ) {
737 af.AddForce( ent, id, point, force );
739 if ( !af.IsActive() ) {
740 idEntity::AddForce( ent, id, point, force );
746 idAFEntity_Base::Collide
749 bool idAFEntity_Base::Collide( const trace_t &collision, const idVec3 &velocity ) {
752 if ( af.IsActive() ) {
753 v = -( velocity * collision.c.normal );
754 if ( v > BOUNCE_SOUND_MIN_VELOCITY && gameLocal.time > nextSoundTime ) {
755 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 ) );
756 if ( StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, false, NULL ) ) {
757 // don't set the volume unless there is a bounce sound as it overrides the entire channel
758 // which causes footsteps on ai's to not honor their shader parms
761 nextSoundTime = gameLocal.time + 500;
770 idAFEntity_Base::GetPhysicsToVisualTransform
773 bool idAFEntity_Base::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
774 if ( af.IsActive() ) {
775 af.GetPhysicsToVisualTransform( origin, axis );
778 return idEntity::GetPhysicsToVisualTransform( origin, axis );
783 idAFEntity_Base::UpdateAnimationControllers
786 bool idAFEntity_Base::UpdateAnimationControllers( void ) {
787 if ( af.IsActive() ) {
788 if ( af.UpdateAnimation() ) {
797 idAFEntity_Base::SetCombatModel
800 void idAFEntity_Base::SetCombatModel( void ) {
802 combatModel->Unlink();
803 combatModel->LoadModel( modelDefHandle );
805 combatModel = new idClipModel( modelDefHandle );
811 idAFEntity_Base::GetCombatModel
814 idClipModel *idAFEntity_Base::GetCombatModel( void ) const {
820 idAFEntity_Base::SetCombatContents
823 void idAFEntity_Base::SetCombatContents( bool enable ) {
824 assert( combatModel );
825 if ( enable && combatModelContents ) {
826 assert( !combatModel->GetContents() );
827 combatModel->SetContents( combatModelContents );
828 combatModelContents = 0;
829 } else if ( !enable && combatModel->GetContents() ) {
830 assert( !combatModelContents );
831 combatModelContents = combatModel->GetContents();
832 combatModel->SetContents( 0 );
838 idAFEntity_Base::LinkCombat
841 void idAFEntity_Base::LinkCombat( void ) {
846 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
852 idAFEntity_Base::UnlinkCombat
855 void idAFEntity_Base::UnlinkCombat( void ) {
857 combatModel->Unlink();
863 idAFEntity_Base::FreeModelDef
866 void idAFEntity_Base::FreeModelDef( void ) {
868 idEntity::FreeModelDef();
873 idAFEntity_Base::ShowEditingDialog
876 void idAFEntity_Base::ShowEditingDialog( void ) {
877 common->InitTool( EDITOR_AF, &spawnArgs );
882 idAFEntity_Base::DropAFs
884 The entity should have the following key/value pairs set:
885 "def_drop<type>AF" "af def"
886 "drop<type>Skin" "skin name"
887 To drop multiple articulated figures the following key/value pairs can be used:
888 "def_drop<type>AF*" "af def"
889 where * is an aribtrary string.
892 void idAFEntity_Base::DropAFs( idEntity *ent, const char *type, idList<idEntity *> *list ) {
893 const idKeyValue *kv;
894 const char *skinName;
898 const idDeclSkin *skin;
900 // drop the articulated figures
901 kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), NULL );
904 args.Set( "classname", kv->GetValue() );
905 gameLocal.SpawnEntityDef( args, &newEnt );
907 if ( newEnt && newEnt->IsType( idAFEntity_Base::Type ) ) {
908 af = static_cast<idAFEntity_Base *>(newEnt);
909 af->GetPhysics()->SetOrigin( ent->GetPhysics()->GetOrigin() );
910 af->GetPhysics()->SetAxis( ent->GetPhysics()->GetAxis() );
911 af->af.SetupPose( ent, gameLocal.time );
917 kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sAF", type ), kv );
920 // change the skin to hide all the dropped articulated figures
921 skinName = ent->spawnArgs.GetString( va( "skin_drop%s", type ) );
923 skin = declManager->FindSkin( skinName );
924 ent->SetSkin( skin );
930 idAFEntity_Base::Event_SetConstraintPosition
933 void idAFEntity_Base::Event_SetConstraintPosition( const char *name, const idVec3 &pos ) {
934 af.SetConstraintPosition( name, pos );
938 ===============================================================================
942 ===============================================================================
945 const idEventDef EV_Gib( "gib", "s" );
946 const idEventDef EV_Gibbed( "<gibbed>" );
948 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Gibbable )
949 EVENT( EV_Gib, idAFEntity_Gibbable::Event_Gib )
950 EVENT( EV_Gibbed, idAFEntity_Base::Event_Remove )
956 idAFEntity_Gibbable::idAFEntity_Gibbable
959 idAFEntity_Gibbable::idAFEntity_Gibbable( void ) {
960 skeletonModel = NULL;
961 skeletonModelDefHandle = -1;
967 idAFEntity_Gibbable::~idAFEntity_Gibbable
970 idAFEntity_Gibbable::~idAFEntity_Gibbable() {
971 if ( skeletonModelDefHandle != -1 ) {
972 gameRenderWorld->FreeEntityDef( skeletonModelDefHandle );
973 skeletonModelDefHandle = -1;
979 idAFEntity_Gibbable::Save
982 void idAFEntity_Gibbable::Save( idSaveGame *savefile ) const {
983 savefile->WriteBool( gibbed );
984 savefile->WriteBool( combatModel != NULL );
989 idAFEntity_Gibbable::Restore
992 void idAFEntity_Gibbable::Restore( idRestoreGame *savefile ) {
995 savefile->ReadBool( gibbed );
996 savefile->ReadBool( hasCombatModel );
1000 if ( hasCombatModel ) {
1008 idAFEntity_Gibbable::Spawn
1011 void idAFEntity_Gibbable::Spawn( void ) {
1012 InitSkeletonModel();
1019 idAFEntity_Gibbable::InitSkeletonModel
1022 void idAFEntity_Gibbable::InitSkeletonModel( void ) {
1023 const char *modelName;
1024 const idDeclModelDef *modelDef;
1026 skeletonModel = NULL;
1027 skeletonModelDefHandle = -1;
1029 modelName = spawnArgs.GetString( "model_gib" );
1032 if ( modelName[0] != '\0' ) {
1033 modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) );
1035 skeletonModel = modelDef->ModelHandle();
1037 skeletonModel = renderModelManager->FindModel( modelName );
1039 if ( skeletonModel != NULL && renderEntity.hModel != NULL ) {
1040 if ( skeletonModel->NumJoints() != renderEntity.hModel->NumJoints() ) {
1041 gameLocal.Error( "gib model '%s' has different number of joints than model '%s'",
1042 skeletonModel->Name(), renderEntity.hModel->Name() );
1050 idAFEntity_Gibbable::Present
1053 void idAFEntity_Gibbable::Present( void ) {
1054 renderEntity_t skeleton;
1056 if ( !gameLocal.isNewFrame ) {
1060 // don't present to the renderer if the entity hasn't changed
1061 if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
1065 // update skeleton model
1066 if ( gibbed && !IsHidden() && skeletonModel != NULL ) {
1067 skeleton = renderEntity;
1068 skeleton.hModel = skeletonModel;
1069 // add to refresh list
1070 if ( skeletonModelDefHandle == -1 ) {
1071 skeletonModelDefHandle = gameRenderWorld->AddEntityDef( &skeleton );
1073 gameRenderWorld->UpdateEntityDef( skeletonModelDefHandle, &skeleton );
1077 idEntity::Present();
1082 idAFEntity_Gibbable::Damage
1085 void idAFEntity_Gibbable::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
1086 if ( !fl.takedamage ) {
1089 idAFEntity_Base::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
1090 if ( health < -20 && spawnArgs.GetBool( "gib" ) ) {
1091 Gib( dir, damageDefName );
1096 =====================
1097 idAFEntity_Gibbable::SpawnGibs
1098 =====================
1100 void idAFEntity_Gibbable::SpawnGibs( const idVec3 &dir, const char *damageDefName ) {
1103 idVec3 entityCenter, velocity;
1104 idList<idEntity *> list;
1106 assert( !gameLocal.isClient );
1108 const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
1110 gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
1113 // spawn gib articulated figures
1114 idAFEntity_Base::DropAFs( this, "gib", &list );
1117 idMoveableItem::DropItems( this, "gib", &list );
1119 // blow out the gibs in the given direction away from the center of the entity
1120 entityCenter = GetPhysics()->GetAbsBounds().GetCenter();
1121 gibNonSolid = damageDef->GetBool( "gibNonSolid" );
1122 for ( i = 0; i < list.Num(); i++ ) {
1123 if ( gibNonSolid ) {
1124 list[i]->GetPhysics()->SetContents( 0 );
1125 list[i]->GetPhysics()->SetClipMask( 0 );
1126 list[i]->GetPhysics()->UnlinkClip();
1127 list[i]->GetPhysics()->PutToRest();
1129 list[i]->GetPhysics()->SetContents( CONTENTS_CORPSE );
1130 list[i]->GetPhysics()->SetClipMask( CONTENTS_SOLID );
1131 velocity = list[i]->GetPhysics()->GetAbsBounds().GetCenter() - entityCenter;
1132 velocity.NormalizeFast();
1133 velocity += ( i & 1 ) ? dir : -dir;
1134 list[i]->GetPhysics()->SetLinearVelocity( velocity * 75.0f );
1136 list[i]->GetRenderEntity()->noShadow = true;
1137 list[i]->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
1138 list[i]->PostEventSec( &EV_Remove, 4.0f );
1144 idAFEntity_Gibbable::Gib
1147 void idAFEntity_Gibbable::Gib( const idVec3 &dir, const char *damageDefName ) {
1153 const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
1155 gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
1158 if ( damageDef->GetBool( "gibNonSolid" ) ) {
1159 GetAFPhysics()->SetContents( 0 );
1160 GetAFPhysics()->SetClipMask( 0 );
1161 GetAFPhysics()->UnlinkClip();
1162 GetAFPhysics()->PutToRest();
1164 GetAFPhysics()->SetContents( CONTENTS_CORPSE );
1165 GetAFPhysics()->SetClipMask( CONTENTS_SOLID );
1170 if ( g_bloodEffects.GetBool() ) {
1171 if ( gameLocal.time > gameLocal.GetGibTime() ) {
1172 gameLocal.SetGibTime( gameLocal.time + GIB_DELAY );
1173 SpawnGibs( dir, damageDefName );
1174 renderEntity.noShadow = true;
1175 renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
1176 StartSound( "snd_gibbed", SND_CHANNEL_ANY, 0, false, NULL );
1184 PostEventSec( &EV_Gibbed, 4.0f );
1189 idAFEntity_Gibbable::Event_Gib
1192 void idAFEntity_Gibbable::Event_Gib( const char *damageDefName ) {
1193 Gib( idVec3( 0, 0, 1 ), damageDefName );
1197 ===============================================================================
1201 ===============================================================================
1204 CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_Generic )
1205 EVENT( EV_Activate, idAFEntity_Generic::Event_Activate )
1210 idAFEntity_Generic::idAFEntity_Generic
1213 idAFEntity_Generic::idAFEntity_Generic( void ) {
1214 keepRunningPhysics = false;
1219 idAFEntity_Generic::~idAFEntity_Generic
1222 idAFEntity_Generic::~idAFEntity_Generic( void ) {
1227 idAFEntity_Generic::Save
1230 void idAFEntity_Generic::Save( idSaveGame *savefile ) const {
1231 savefile->WriteBool( keepRunningPhysics );
1236 idAFEntity_Generic::Restore
1239 void idAFEntity_Generic::Restore( idRestoreGame *savefile ) {
1240 savefile->ReadBool( keepRunningPhysics );
1245 idAFEntity_Generic::Think
1248 void idAFEntity_Generic::Think( void ) {
1249 idAFEntity_Base::Think();
1251 if ( keepRunningPhysics ) {
1252 BecomeActive( TH_PHYSICS );
1258 idAFEntity_Generic::Spawn
1261 void idAFEntity_Generic::Spawn( void ) {
1263 gameLocal.Error( "Couldn't load af file on entity '%s'", name.c_str() );
1268 SetPhysics( af.GetPhysics() );
1270 af.GetPhysics()->PutToRest();
1271 if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
1272 af.GetPhysics()->Activate();
1275 fl.takedamage = true;
1280 idAFEntity_Generic::Event_Activate
1283 void idAFEntity_Generic::Event_Activate( idEntity *activator ) {
1285 idVec3 init_velocity, init_avelocity;
1289 af.GetPhysics()->EnableImpact();
1290 af.GetPhysics()->Activate();
1292 spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
1293 spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
1295 delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
1296 if ( delay == 0.0f ) {
1297 af.GetPhysics()->SetLinearVelocity( init_velocity );
1299 PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
1302 delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
1303 if ( delay == 0.0f ) {
1304 af.GetPhysics()->SetAngularVelocity( init_avelocity );
1306 PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
1312 ===============================================================================
1314 idAFEntity_WithAttachedHead
1316 ===============================================================================
1319 CLASS_DECLARATION( idAFEntity_Gibbable, idAFEntity_WithAttachedHead )
1320 EVENT( EV_Gib, idAFEntity_WithAttachedHead::Event_Gib )
1321 EVENT( EV_Activate, idAFEntity_WithAttachedHead::Event_Activate )
1326 idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead
1329 idAFEntity_WithAttachedHead::idAFEntity_WithAttachedHead() {
1335 idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead
1338 idAFEntity_WithAttachedHead::~idAFEntity_WithAttachedHead() {
1339 if ( head.GetEntity() ) {
1340 head.GetEntity()->ClearBody();
1341 head.GetEntity()->PostEventMS( &EV_Remove, 0 );
1347 idAFEntity_WithAttachedHead::Spawn
1350 void idAFEntity_WithAttachedHead::Spawn( void ) {
1357 SetPhysics( af.GetPhysics() );
1359 af.GetPhysics()->PutToRest();
1360 if ( !spawnArgs.GetBool( "nodrop", "0" ) ) {
1361 af.GetPhysics()->Activate();
1364 fl.takedamage = true;
1366 if ( head.GetEntity() ) {
1367 int anim = head.GetEntity()->GetAnimator()->GetAnim( "dead" );
1370 head.GetEntity()->GetAnimator()->SetFrame( ANIMCHANNEL_ALL, anim, 0, gameLocal.time, 0 );
1377 idAFEntity_WithAttachedHead::Save
1380 void idAFEntity_WithAttachedHead::Save( idSaveGame *savefile ) const {
1381 head.Save( savefile );
1386 idAFEntity_WithAttachedHead::Restore
1389 void idAFEntity_WithAttachedHead::Restore( idRestoreGame *savefile ) {
1390 head.Restore( savefile );
1395 idAFEntity_WithAttachedHead::SetupHead
1398 void idAFEntity_WithAttachedHead::SetupHead( void ) {
1399 idAFAttachment *headEnt;
1401 const char *headModel;
1402 jointHandle_t joint;
1406 headModel = spawnArgs.GetString( "def_head", "" );
1407 if ( headModel[ 0 ] ) {
1408 jointName = spawnArgs.GetString( "head_joint" );
1409 joint = animator.GetJointHandle( jointName );
1410 if ( joint == INVALID_JOINT ) {
1411 gameLocal.Error( "Joint '%s' not found for 'head_joint' on '%s'", jointName.c_str(), name.c_str() );
1414 headEnt = static_cast<idAFAttachment *>( gameLocal.SpawnEntityType( idAFAttachment::Type, NULL ) );
1415 headEnt->SetName( va( "%s_head", name.c_str() ) );
1416 headEnt->SetBody( this, headModel, joint );
1417 headEnt->SetCombatModel();
1420 animator.GetJointTransform( joint, gameLocal.time, origin, axis );
1421 origin = renderEntity.origin + origin * renderEntity.axis;
1422 headEnt->SetOrigin( origin );
1423 headEnt->SetAxis( renderEntity.axis );
1424 headEnt->BindToJoint( this, joint, true );
1430 idAFEntity_WithAttachedHead::Think
1433 void idAFEntity_WithAttachedHead::Think( void ) {
1434 idAFEntity_Base::Think();
1439 idAFEntity_WithAttachedHead::LinkCombat
1442 void idAFEntity_WithAttachedHead::LinkCombat( void ) {
1443 idAFAttachment *headEnt;
1449 if ( combatModel ) {
1450 combatModel->Link( gameLocal.clip, this, 0, renderEntity.origin, renderEntity.axis, modelDefHandle );
1452 headEnt = head.GetEntity();
1454 headEnt->LinkCombat();
1460 idAFEntity_WithAttachedHead::UnlinkCombat
1463 void idAFEntity_WithAttachedHead::UnlinkCombat( void ) {
1464 idAFAttachment *headEnt;
1466 if ( combatModel ) {
1467 combatModel->Unlink();
1469 headEnt = head.GetEntity();
1471 headEnt->UnlinkCombat();
1477 idAFEntity_WithAttachedHead::Hide
1480 void idAFEntity_WithAttachedHead::Hide( void ) {
1481 idAFEntity_Base::Hide();
1482 if ( head.GetEntity() ) {
1483 head.GetEntity()->Hide();
1490 idAFEntity_WithAttachedHead::Show
1493 void idAFEntity_WithAttachedHead::Show( void ) {
1494 idAFEntity_Base::Show();
1495 if ( head.GetEntity() ) {
1496 head.GetEntity()->Show();
1503 idAFEntity_WithAttachedHead::ProjectOverlay
1506 void idAFEntity_WithAttachedHead::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
1508 idEntity::ProjectOverlay( origin, dir, size, material );
1510 if ( head.GetEntity() ) {
1511 head.GetEntity()->ProjectOverlay( origin, dir, size, material );
1517 idAFEntity_WithAttachedHead::Gib
1520 void idAFEntity_WithAttachedHead::Gib( const idVec3 &dir, const char *damageDefName ) {
1525 idAFEntity_Gibbable::Gib( dir, damageDefName );
1526 if ( head.GetEntity() ) {
1527 head.GetEntity()->Hide();
1533 idAFEntity_WithAttachedHead::Event_Gib
1536 void idAFEntity_WithAttachedHead::Event_Gib( const char *damageDefName ) {
1537 Gib( idVec3( 0, 0, 1 ), damageDefName );
1542 idAFEntity_WithAttachedHead::Event_Activate
1545 void idAFEntity_WithAttachedHead::Event_Activate( idEntity *activator ) {
1547 idVec3 init_velocity, init_avelocity;
1551 af.GetPhysics()->EnableImpact();
1552 af.GetPhysics()->Activate();
1554 spawnArgs.GetVector( "init_velocity", "0 0 0", init_velocity );
1555 spawnArgs.GetVector( "init_avelocity", "0 0 0", init_avelocity );
1557 delay = spawnArgs.GetFloat( "init_velocityDelay", "0" );
1558 if ( delay == 0.0f ) {
1559 af.GetPhysics()->SetLinearVelocity( init_velocity );
1561 PostEventSec( &EV_SetLinearVelocity, delay, init_velocity );
1564 delay = spawnArgs.GetFloat( "init_avelocityDelay", "0" );
1565 if ( delay == 0.0f ) {
1566 af.GetPhysics()->SetAngularVelocity( init_avelocity );
1568 PostEventSec( &EV_SetAngularVelocity, delay, init_avelocity );
1574 ===============================================================================
1578 ===============================================================================
1581 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_Vehicle )
1586 idAFEntity_Vehicle::idAFEntity_Vehicle
1589 idAFEntity_Vehicle::idAFEntity_Vehicle( void ) {
1591 eyesJoint = INVALID_JOINT;
1592 steeringWheelJoint = INVALID_JOINT;
1601 idAFEntity_Vehicle::Spawn
1604 void idAFEntity_Vehicle::Spawn( void ) {
1605 const char *eyesJointName = spawnArgs.GetString( "eyesJoint", "eyes" );
1606 const char *steeringWheelJointName = spawnArgs.GetString( "steeringWheelJoint", "steeringWheel" );
1612 SetPhysics( af.GetPhysics() );
1614 fl.takedamage = true;
1616 if ( !eyesJointName[0] ) {
1617 gameLocal.Error( "idAFEntity_Vehicle '%s' no eyes joint specified", name.c_str() );
1619 eyesJoint = animator.GetJointHandle( eyesJointName );
1620 if ( !steeringWheelJointName[0] ) {
1621 gameLocal.Error( "idAFEntity_Vehicle '%s' no steering wheel joint specified", name.c_str() );
1623 steeringWheelJoint = animator.GetJointHandle( steeringWheelJointName );
1625 spawnArgs.GetFloat( "wheelRadius", "20", wheelRadius );
1626 spawnArgs.GetFloat( "steerSpeed", "5", steerSpeed );
1631 const char *smokeName = spawnArgs.GetString( "smoke_vehicle_dust", "muzzlesmoke" );
1632 if ( *smokeName != '\0' ) {
1633 dustSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
1639 idAFEntity_Vehicle::Use
1642 void idAFEntity_Vehicle::Use( idPlayer *other ) {
1647 if ( player == other ) {
1651 af.GetPhysics()->SetComeToRest( true );
1656 animator.GetJointTransform( eyesJoint, gameLocal.time, origin, axis );
1657 origin = renderEntity.origin + origin * renderEntity.axis;
1658 player->GetPhysics()->SetOrigin( origin );
1659 player->BindToBody( this, 0, true );
1661 af.GetPhysics()->SetComeToRest( false );
1662 af.GetPhysics()->Activate();
1668 idAFEntity_Vehicle::GetSteerAngle
1671 float idAFEntity_Vehicle::GetSteerAngle( void ) {
1672 float idealSteerAngle, angleDelta;
1674 idealSteerAngle = player->usercmd.rightmove * ( 30.0f / 128.0f );
1675 angleDelta = idealSteerAngle - steerAngle;
1677 if ( angleDelta > steerSpeed ) {
1678 steerAngle += steerSpeed;
1679 } else if ( angleDelta < -steerSpeed ) {
1680 steerAngle -= steerSpeed;
1682 steerAngle = idealSteerAngle;
1690 ===============================================================================
1692 idAFEntity_VehicleSimple
1694 ===============================================================================
1697 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSimple )
1702 idAFEntity_VehicleSimple::idAFEntity_VehicleSimple
1705 idAFEntity_VehicleSimple::idAFEntity_VehicleSimple( void ) {
1707 for ( i = 0; i < 4; i++ ) {
1708 suspension[i] = NULL;
1714 idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple
1717 idAFEntity_VehicleSimple::~idAFEntity_VehicleSimple( void ) {
1724 idAFEntity_VehicleSimple::Spawn
1727 void idAFEntity_VehicleSimple::Spawn( void ) {
1728 static const char *wheelJointKeys[] = {
1729 "wheelJointFrontLeft",
1730 "wheelJointFrontRight",
1731 "wheelJointRearLeft",
1732 "wheelJointRearRight"
1734 static idVec3 wheelPoly[4] = { idVec3( 2, 2, 0 ), idVec3( 2, -2, 0 ), idVec3( -2, -2, 0 ), idVec3( -2, 2, 0 ) };
1741 trm.SetupPolygon( wheelPoly, 4 );
1742 trm.Translate( idVec3( 0, 0, -wheelRadius ) );
1743 wheelModel = new idClipModel( trm );
1745 for ( i = 0; i < 4; i++ ) {
1746 const char *wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
1747 if ( !wheelJointName[0] ) {
1748 gameLocal.Error( "idAFEntity_VehicleSimple '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
1750 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
1751 if ( wheelJoints[i] == INVALID_JOINT ) {
1752 gameLocal.Error( "idAFEntity_VehicleSimple '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
1755 GetAnimator()->GetJointTransform( wheelJoints[i], 0, origin, axis );
1756 origin = renderEntity.origin + origin * renderEntity.axis;
1758 suspension[i] = new idAFConstraint_Suspension();
1759 suspension[i]->Setup( va( "suspension%d", i ), af.GetPhysics()->GetBody( 0 ), origin, af.GetPhysics()->GetAxis( 0 ), wheelModel );
1760 suspension[i]->SetSuspension( g_vehicleSuspensionUp.GetFloat(),
1761 g_vehicleSuspensionDown.GetFloat(),
1762 g_vehicleSuspensionKCompress.GetFloat(),
1763 g_vehicleSuspensionDamping.GetFloat(),
1764 g_vehicleTireFriction.GetFloat() );
1766 af.GetPhysics()->AddConstraint( suspension[i] );
1769 memset( wheelAngles, 0, sizeof( wheelAngles ) );
1770 BecomeActive( TH_THINK );
1775 idAFEntity_VehicleSimple::Think
1778 void idAFEntity_VehicleSimple::Think( void ) {
1780 float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
1783 idRotation wheelRotation, steerRotation;
1785 if ( thinkFlags & TH_THINK ) {
1788 // capture the input from a player
1789 velocity = g_vehicleVelocity.GetFloat();
1790 if ( player->usercmd.forwardmove < 0 ) {
1791 velocity = -velocity;
1793 force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
1794 steerAngle = GetSteerAngle();
1797 // update the wheel motor force and steering
1798 for ( i = 0; i < 2; i++ ) {
1800 // front wheel drive
1801 if ( velocity != 0.0f ) {
1802 suspension[i]->EnableMotor( true );
1804 suspension[i]->EnableMotor( false );
1806 suspension[i]->SetMotorVelocity( velocity );
1807 suspension[i]->SetMotorForce( force );
1809 // update the wheel steering
1810 suspension[i]->SetSteerAngle( steerAngle );
1813 // adjust wheel velocity for better steering because there are no differentials between the wheels
1814 if ( steerAngle < 0.0f ) {
1815 suspension[0]->SetMotorVelocity( velocity * 0.5f );
1816 } else if ( steerAngle > 0.0f ) {
1817 suspension[1]->SetMotorVelocity( velocity * 0.5f );
1820 // update suspension with latest cvar settings
1821 for ( i = 0; i < 4; i++ ) {
1822 suspension[i]->SetSuspension( g_vehicleSuspensionUp.GetFloat(),
1823 g_vehicleSuspensionDown.GetFloat(),
1824 g_vehicleSuspensionKCompress.GetFloat(),
1825 g_vehicleSuspensionDamping.GetFloat(),
1826 g_vehicleTireFriction.GetFloat() );
1832 // move and rotate the wheels visually
1833 for ( i = 0; i < 4; i++ ) {
1834 idAFBody *body = af.GetPhysics()->GetBody( 0 );
1836 origin = suspension[i]->GetWheelOrigin();
1837 velocity = body->GetPointVelocity( origin ) * body->GetWorldAxis()[0];
1838 wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
1840 // additional rotation about the wheel axis
1841 wheelRotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
1842 wheelRotation.SetVec( 0, -1, 0 );
1845 // rotate the wheel for steering
1846 steerRotation.SetAngle( steerAngle );
1847 steerRotation.SetVec( 0, 0, 1 );
1848 // set wheel rotation
1849 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() * steerRotation.ToMat3() );
1851 // set wheel rotation
1852 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, wheelRotation.ToMat3() );
1855 // set wheel position for suspension
1856 origin = ( origin - renderEntity.origin ) * renderEntity.axis.Transpose();
1857 GetAnimator()->SetJointPos( wheelJoints[i], JOINTMOD_WORLD_OVERRIDE, origin );
1860 // spawn dust particle effects
1861 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
1863 idAFConstraint_Contact *contacts[2];
1864 for ( i = 0; i < 4; i++ ) {
1865 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
1866 for ( int j = 0; j < numContacts; j++ ) {
1867 gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3() );
1875 if ( thinkFlags & TH_UPDATEVISUALS ) {
1883 ===============================================================================
1885 idAFEntity_VehicleFourWheels
1887 ===============================================================================
1890 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleFourWheels )
1896 idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels
1899 idAFEntity_VehicleFourWheels::idAFEntity_VehicleFourWheels( void ) {
1902 for ( i = 0; i < 4; i++ ) {
1904 wheelJoints[i] = INVALID_JOINT;
1905 wheelAngles[i] = 0.0f;
1913 idAFEntity_VehicleFourWheels::Spawn
1916 void idAFEntity_VehicleFourWheels::Spawn( void ) {
1918 static const char *wheelBodyKeys[] = {
1919 "wheelBodyFrontLeft",
1920 "wheelBodyFrontRight",
1921 "wheelBodyRearLeft",
1922 "wheelBodyRearRight"
1924 static const char *wheelJointKeys[] = {
1925 "wheelJointFrontLeft",
1926 "wheelJointFrontRight",
1927 "wheelJointRearLeft",
1928 "wheelJointRearRight"
1930 static const char *steeringHingeKeys[] = {
1931 "steeringHingeFrontLeft",
1932 "steeringHingeFrontRight",
1935 const char *wheelBodyName, *wheelJointName, *steeringHingeName;
1937 for ( i = 0; i < 4; i++ ) {
1938 wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" );
1939 if ( !wheelBodyName[0] ) {
1940 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] );
1942 wheels[i] = af.GetPhysics()->GetBody( wheelBodyName );
1944 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName );
1946 wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
1947 if ( !wheelJointName[0] ) {
1948 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
1950 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
1951 if ( wheelJoints[i] == INVALID_JOINT ) {
1952 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
1956 for ( i = 0; i < 2; i++ ) {
1957 steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" );
1958 if ( !steeringHingeName[0] ) {
1959 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] );
1961 steering[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( steeringHingeName ));
1962 if ( !steering[i] ) {
1963 gameLocal.Error( "idAFEntity_VehicleFourWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName );
1967 memset( wheelAngles, 0, sizeof( wheelAngles ) );
1968 BecomeActive( TH_THINK );
1973 idAFEntity_VehicleFourWheels::Think
1976 void idAFEntity_VehicleFourWheels::Think( void ) {
1978 float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
1981 idRotation rotation;
1983 if ( thinkFlags & TH_THINK ) {
1986 // capture the input from a player
1987 velocity = g_vehicleVelocity.GetFloat();
1988 if ( player->usercmd.forwardmove < 0 ) {
1989 velocity = -velocity;
1991 force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
1992 steerAngle = GetSteerAngle();
1995 // update the wheel motor force
1996 for ( i = 0; i < 2; i++ ) {
1997 wheels[2+i]->SetContactMotorVelocity( velocity );
1998 wheels[2+i]->SetContactMotorForce( force );
2001 // adjust wheel velocity for better steering because there are no differentials between the wheels
2002 if ( steerAngle < 0.0f ) {
2003 wheels[2]->SetContactMotorVelocity( velocity * 0.5f );
2005 else if ( steerAngle > 0.0f ) {
2006 wheels[3]->SetContactMotorVelocity( velocity * 0.5f );
2009 // update the wheel steering
2010 steering[0]->SetSteerAngle( steerAngle );
2011 steering[1]->SetSteerAngle( steerAngle );
2012 for ( i = 0; i < 2; i++ ) {
2013 steering[i]->SetSteerSpeed( 3.0f );
2016 // update the steering wheel
2017 animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis );
2018 rotation.SetVec( axis[2] );
2019 rotation.SetAngle( -steerAngle );
2020 animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() );
2025 // rotate the wheels visually
2026 for ( i = 0; i < 4; i++ ) {
2027 if ( force == 0.0f ) {
2028 velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0];
2030 wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
2031 // give the wheel joint an additional rotation about the wheel axis
2032 rotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
2033 axis = af.GetPhysics()->GetAxis( 0 );
2034 rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] );
2035 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() );
2038 // spawn dust particle effects
2039 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
2041 idAFConstraint_Contact *contacts[2];
2042 for ( i = 0; i < 4; i++ ) {
2043 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
2044 for ( int j = 0; j < numContacts; j++ ) {
2045 gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3() );
2052 if ( thinkFlags & TH_UPDATEVISUALS ) {
2060 ===============================================================================
2062 idAFEntity_VehicleSixWheels
2064 ===============================================================================
2067 CLASS_DECLARATION( idAFEntity_Vehicle, idAFEntity_VehicleSixWheels )
2072 idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels
2075 idAFEntity_VehicleSixWheels::idAFEntity_VehicleSixWheels( void ) {
2078 for ( i = 0; i < 6; i++ ) {
2080 wheelJoints[i] = INVALID_JOINT;
2081 wheelAngles[i] = 0.0f;
2091 idAFEntity_VehicleSixWheels::Spawn
2094 void idAFEntity_VehicleSixWheels::Spawn( void ) {
2096 static const char *wheelBodyKeys[] = {
2097 "wheelBodyFrontLeft",
2098 "wheelBodyFrontRight",
2099 "wheelBodyMiddleLeft",
2100 "wheelBodyMiddleRight",
2101 "wheelBodyRearLeft",
2102 "wheelBodyRearRight"
2104 static const char *wheelJointKeys[] = {
2105 "wheelJointFrontLeft",
2106 "wheelJointFrontRight",
2107 "wheelJointMiddleLeft",
2108 "wheelJointMiddleRight",
2109 "wheelJointRearLeft",
2110 "wheelJointRearRight"
2112 static const char *steeringHingeKeys[] = {
2113 "steeringHingeFrontLeft",
2114 "steeringHingeFrontRight",
2115 "steeringHingeRearLeft",
2116 "steeringHingeRearRight"
2119 const char *wheelBodyName, *wheelJointName, *steeringHingeName;
2121 for ( i = 0; i < 6; i++ ) {
2122 wheelBodyName = spawnArgs.GetString( wheelBodyKeys[i], "" );
2123 if ( !wheelBodyName[0] ) {
2124 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelBodyKeys[i] );
2126 wheels[i] = af.GetPhysics()->GetBody( wheelBodyName );
2128 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel body '%s'", name.c_str(), wheelBodyName );
2130 wheelJointName = spawnArgs.GetString( wheelJointKeys[i], "" );
2131 if ( !wheelJointName[0] ) {
2132 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), wheelJointKeys[i] );
2134 wheelJoints[i] = animator.GetJointHandle( wheelJointName );
2135 if ( wheelJoints[i] == INVALID_JOINT ) {
2136 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' can't find wheel joint '%s'", name.c_str(), wheelJointName );
2140 for ( i = 0; i < 4; i++ ) {
2141 steeringHingeName = spawnArgs.GetString( steeringHingeKeys[i], "" );
2142 if ( !steeringHingeName[0] ) {
2143 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s' no '%s' specified", name.c_str(), steeringHingeKeys[i] );
2145 steering[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( steeringHingeName ));
2146 if ( !steering[i] ) {
2147 gameLocal.Error( "idAFEntity_VehicleSixWheels '%s': can't find steering hinge '%s'", name.c_str(), steeringHingeName );
2151 memset( wheelAngles, 0, sizeof( wheelAngles ) );
2152 BecomeActive( TH_THINK );
2157 idAFEntity_VehicleSixWheels::Think
2160 void idAFEntity_VehicleSixWheels::Think( void ) {
2162 float force = 0.0f, velocity = 0.0f, steerAngle = 0.0f;
2165 idRotation rotation;
2167 if ( thinkFlags & TH_THINK ) {
2170 // capture the input from a player
2171 velocity = g_vehicleVelocity.GetFloat();
2172 if ( player->usercmd.forwardmove < 0 ) {
2173 velocity = -velocity;
2175 force = idMath::Fabs( player->usercmd.forwardmove * g_vehicleForce.GetFloat() ) * (1.0f / 128.0f);
2176 steerAngle = GetSteerAngle();
2179 // update the wheel motor force
2180 for ( i = 0; i < 6; i++ ) {
2181 wheels[i]->SetContactMotorVelocity( velocity );
2182 wheels[i]->SetContactMotorForce( force );
2185 // adjust wheel velocity for better steering because there are no differentials between the wheels
2186 if ( steerAngle < 0.0f ) {
2187 for ( i = 0; i < 3; i++ ) {
2188 wheels[(i<<1)]->SetContactMotorVelocity( velocity * 0.5f );
2191 else if ( steerAngle > 0.0f ) {
2192 for ( i = 0; i < 3; i++ ) {
2193 wheels[1+(i<<1)]->SetContactMotorVelocity( velocity * 0.5f );
2197 // update the wheel steering
2198 steering[0]->SetSteerAngle( steerAngle );
2199 steering[1]->SetSteerAngle( steerAngle );
2200 steering[2]->SetSteerAngle( -steerAngle );
2201 steering[3]->SetSteerAngle( -steerAngle );
2202 for ( i = 0; i < 4; i++ ) {
2203 steering[i]->SetSteerSpeed( 3.0f );
2206 // update the steering wheel
2207 animator.GetJointTransform( steeringWheelJoint, gameLocal.time, origin, axis );
2208 rotation.SetVec( axis[2] );
2209 rotation.SetAngle( -steerAngle );
2210 animator.SetJointAxis( steeringWheelJoint, JOINTMOD_WORLD, rotation.ToMat3() );
2215 // rotate the wheels visually
2216 for ( i = 0; i < 6; i++ ) {
2217 if ( force == 0.0f ) {
2218 velocity = wheels[i]->GetLinearVelocity() * wheels[i]->GetWorldAxis()[0];
2220 wheelAngles[i] += velocity * MS2SEC( gameLocal.msec ) / wheelRadius;
2221 // give the wheel joint an additional rotation about the wheel axis
2222 rotation.SetAngle( RAD2DEG( wheelAngles[i] ) );
2223 axis = af.GetPhysics()->GetAxis( 0 );
2224 rotation.SetVec( (wheels[i]->GetWorldAxis() * axis.Transpose())[2] );
2225 animator.SetJointAxis( wheelJoints[i], JOINTMOD_WORLD, rotation.ToMat3() );
2228 // spawn dust particle effects
2229 if ( force != 0.0f && !( gameLocal.framenum & 7 ) ) {
2231 idAFConstraint_Contact *contacts[2];
2232 for ( i = 0; i < 6; i++ ) {
2233 numContacts = af.GetPhysics()->GetBodyContactConstraints( wheels[i]->GetClipModel()->GetId(), contacts, 2 );
2234 for ( int j = 0; j < numContacts; j++ ) {
2235 gameLocal.smokeParticles->EmitSmoke( dustSmoke, gameLocal.time, gameLocal.random.RandomFloat(), contacts[j]->GetContact().point, contacts[j]->GetContact().normal.ToMat3() );
2242 if ( thinkFlags & TH_UPDATEVISUALS ) {
2250 ===============================================================================
2252 idAFEntity_SteamPipe
2254 ===============================================================================
2257 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_SteamPipe )
2263 idAFEntity_SteamPipe::idAFEntity_SteamPipe
2266 idAFEntity_SteamPipe::idAFEntity_SteamPipe( void ) {
2269 steamUpForce = 0.0f;
2270 steamModelDefHandle = -1;
2271 memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) );
2276 idAFEntity_SteamPipe::~idAFEntity_SteamPipe
2279 idAFEntity_SteamPipe::~idAFEntity_SteamPipe( void ) {
2280 if ( steamModelDefHandle >= 0 ){
2281 gameRenderWorld->FreeEntityDef( steamModelDefHandle );
2287 idAFEntity_SteamPipe::Save
2290 void idAFEntity_SteamPipe::Save( idSaveGame *savefile ) const {
2295 idAFEntity_SteamPipe::Restore
2298 void idAFEntity_SteamPipe::Restore( idRestoreGame *savefile ) {
2304 idAFEntity_SteamPipe::Spawn
2307 void idAFEntity_SteamPipe::Spawn( void ) {
2309 const char *steamBodyName;
2315 SetPhysics( af.GetPhysics() );
2317 fl.takedamage = true;
2319 steamBodyName = spawnArgs.GetString( "steamBody", "" );
2320 steamForce = spawnArgs.GetFloat( "steamForce", "2000" );
2321 steamUpForce = spawnArgs.GetFloat( "steamUpForce", "10" );
2322 steamDir = af.GetPhysics()->GetAxis( steamBody )[2];
2323 steamBody = af.GetPhysics()->GetBodyId( steamBodyName );
2324 force.SetPosition( af.GetPhysics(), steamBody, af.GetPhysics()->GetOrigin( steamBody ) );
2325 force.SetForce( steamDir * -steamForce );
2327 InitSteamRenderEntity();
2329 BecomeActive( TH_THINK );
2334 idAFEntity_SteamPipe::InitSteamRenderEntity
2337 void idAFEntity_SteamPipe::InitSteamRenderEntity( void ) {
2339 const idDeclModelDef *modelDef;
2341 memset( &steamRenderEntity, 0, sizeof( steamRenderEntity ) );
2342 steamRenderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
2343 steamRenderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0f;
2344 steamRenderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
2346 temp = spawnArgs.GetString ( "model_steam" );
2347 if ( *temp != '\0' ) {
2348 if ( !strstr( temp, "." ) ) {
2349 modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, temp, false ) );
2351 steamRenderEntity.hModel = modelDef->ModelHandle();
2355 if ( !steamRenderEntity.hModel ) {
2356 steamRenderEntity.hModel = renderModelManager->FindModel( temp );
2359 if ( steamRenderEntity.hModel ) {
2360 steamRenderEntity.bounds = steamRenderEntity.hModel->Bounds( &steamRenderEntity );
2362 steamRenderEntity.bounds.Zero();
2364 steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody );
2365 steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody );
2366 steamModelDefHandle = gameRenderWorld->AddEntityDef( &steamRenderEntity );
2372 idAFEntity_SteamPipe::Think
2375 void idAFEntity_SteamPipe::Think( void ) {
2378 if ( thinkFlags & TH_THINK ) {
2379 steamDir.x = gameLocal.random.CRandomFloat() * steamForce;
2380 steamDir.y = gameLocal.random.CRandomFloat() * steamForce;
2381 steamDir.z = steamUpForce;
2382 force.SetForce( steamDir );
2383 force.Evaluate( gameLocal.time );
2384 //gameRenderWorld->DebugArrow( colorWhite, af.GetPhysics()->GetOrigin( steamBody ), af.GetPhysics()->GetOrigin( steamBody ) - 10.0f * steamDir, 4 );
2387 if ( steamModelDefHandle >= 0 ){
2388 steamRenderEntity.origin = af.GetPhysics()->GetOrigin( steamBody );
2389 steamRenderEntity.axis = af.GetPhysics()->GetAxis( steamBody );
2390 gameRenderWorld->UpdateEntityDef( steamModelDefHandle, &steamRenderEntity );
2393 idAFEntity_Base::Think();
2398 ===============================================================================
2400 idAFEntity_ClawFourFingers
2402 ===============================================================================
2405 const idEventDef EV_SetFingerAngle( "setFingerAngle", "f" );
2406 const idEventDef EV_StopFingers( "stopFingers" );
2408 CLASS_DECLARATION( idAFEntity_Base, idAFEntity_ClawFourFingers )
2409 EVENT( EV_SetFingerAngle, idAFEntity_ClawFourFingers::Event_SetFingerAngle )
2410 EVENT( EV_StopFingers, idAFEntity_ClawFourFingers::Event_StopFingers )
2413 static const char *clawConstraintNames[] = {
2414 "claw1", "claw2", "claw3", "claw4"
2419 idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers
2422 idAFEntity_ClawFourFingers::idAFEntity_ClawFourFingers( void ) {
2431 idAFEntity_ClawFourFingers::Save
2434 void idAFEntity_ClawFourFingers::Save( idSaveGame *savefile ) const {
2437 for ( i = 0; i < 4; i++ ) {
2438 fingers[i]->Save( savefile );
2444 idAFEntity_ClawFourFingers::Restore
2447 void idAFEntity_ClawFourFingers::Restore( idRestoreGame *savefile ) {
2450 for ( i = 0; i < 4; i++ ) {
2451 fingers[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( clawConstraintNames[i] ));
2452 fingers[i]->Restore( savefile );
2461 idAFEntity_ClawFourFingers::Spawn
2464 void idAFEntity_ClawFourFingers::Spawn( void ) {
2471 af.GetPhysics()->LockWorldConstraints( true );
2472 af.GetPhysics()->SetForcePushable( true );
2473 SetPhysics( af.GetPhysics() );
2475 fl.takedamage = true;
2477 for ( i = 0; i < 4; i++ ) {
2478 fingers[i] = static_cast<idAFConstraint_Hinge *>(af.GetPhysics()->GetConstraint( clawConstraintNames[i] ));
2479 if ( !fingers[i] ) {
2480 gameLocal.Error( "idClaw_FourFingers '%s': can't find claw constraint '%s'", name.c_str(), clawConstraintNames[i] );
2487 idAFEntity_ClawFourFingers::Event_SetFingerAngle
2490 void idAFEntity_ClawFourFingers::Event_SetFingerAngle( float angle ) {
2493 for ( i = 0; i < 4; i++ ) {
2494 fingers[i]->SetSteerAngle( angle );
2495 fingers[i]->SetSteerSpeed( 0.5f );
2497 af.GetPhysics()->Activate();
2502 idAFEntity_ClawFourFingers::Event_StopFingers
2505 void idAFEntity_ClawFourFingers::Event_StopFingers( void ) {
2508 for ( i = 0; i < 4; i++ ) {
2509 fingers[i]->SetSteerAngle( fingers[i]->GetAngle() );
2515 ===============================================================================
2517 editor support routines
2519 ===============================================================================
2525 idGameEdit::AF_SpawnEntity
2528 bool idGameEdit::AF_SpawnEntity( const char *fileName ) {
2531 idAFEntity_Generic *ent;
2536 player = gameLocal.GetLocalPlayer();
2537 if ( !player || !gameLocal.CheatsOk( false ) ) {
2541 af = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, fileName ) );
2546 yaw = player->viewAngles.yaw;
2547 args.Set( "angle", va( "%f", yaw + 180 ) );
2548 org = player->GetPhysics()->GetOrigin() + idAngles( 0, yaw, 0 ).ToForward() * 80 + idVec3( 0, 0, 1 );
2549 args.Set( "origin", org.ToString() );
2550 args.Set( "spawnclass", "idAFEntity_Generic" );
2551 if ( af->model[0] ) {
2552 args.Set( "model", af->model.c_str() );
2554 args.Set( "model", fileName );
2556 if ( af->skin[0] ) {
2557 args.Set( "skin", af->skin.c_str() );
2559 args.Set( "articulatedFigure", fileName );
2560 args.Set( "nodrop", "1" );
2561 ent = static_cast<idAFEntity_Generic *>(gameLocal.SpawnEntityType( idAFEntity_Generic::Type, &args));
2563 // always update this entity
2564 ent->BecomeActive( TH_THINK );
2565 ent->KeepRunningPhysics();
2566 ent->fl.forcePhysicsUpdate = true;
2568 player->dragEntity.SetSelected( ent );
2575 idGameEdit::AF_UpdateEntities
2578 void idGameEdit::AF_UpdateEntities( const char *fileName ) {
2580 idAFEntity_Base *af;
2584 name.StripFileExtension();
2586 // reload any idAFEntity_Generic which uses the given articulated figure file
2587 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2588 if ( ent->IsType( idAFEntity_Base::Type ) ) {
2589 af = static_cast<idAFEntity_Base *>(ent);
2590 if ( name.Icmp( af->GetAFName() ) == 0 ) {
2592 af->GetAFPhysics()->PutToRest();
2600 idGameEdit::AF_UndoChanges
2603 void idGameEdit::AF_UndoChanges( void ) {
2606 idAFEntity_Base *af;
2609 c = declManager->GetNumDecls( DECL_AF );
2610 for ( i = 0; i < c; i++ ) {
2611 decl = static_cast<idDeclAF *>( const_cast<idDecl *>( declManager->DeclByIndex( DECL_AF, i, false ) ) );
2612 if ( !decl->modified ) {
2617 declManager->FindType( DECL_AF, decl->GetName() );
2619 // reload all AF entities using the file
2620 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
2621 if ( ent->IsType( idAFEntity_Base::Type ) ) {
2622 af = static_cast<idAFEntity_Base *>(ent);
2623 if ( idStr::Icmp( decl->GetName(), af->GetAFName() ) == 0 ) {
2637 renderEntity_t *ent;
2638 const idMD5Joint *joints;
2639 } jointTransformData_t;
2641 static bool GetJointTransform( void *model, const idJointMat *frame, const char *jointName, idVec3 &origin, idMat3 &axis ) {
2643 jointTransformData_t *data = reinterpret_cast<jointTransformData_t *>(model);
2645 for ( i = 0; i < data->ent->numJoints; i++ ) {
2646 if ( data->joints[i].name.Icmp( jointName ) == 0 ) {
2650 if ( i >= data->ent->numJoints ) {
2653 origin = frame[i].ToVec3();
2654 axis = frame[i].ToMat3();
2663 static const char *GetArgString( const idDict &args, const idDict *defArgs, const char *key ) {
2666 s = args.GetString( key );
2667 if ( !s[0] && defArgs ) {
2668 s = defArgs->GetString( key );
2675 idGameEdit::AF_CreateMesh
2678 idRenderModel *idGameEdit::AF_CreateMesh( const idDict &args, idVec3 &meshOrigin, idMat3 &meshAxis, bool &poseIsSet ) {
2681 const idDeclAF_Body *fb;
2683 idVec3 origin, *bodyOrigin, *newBodyOrigin, *modifiedOrigin;
2684 idMat3 axis, *bodyAxis, *newBodyAxis, *modifiedAxis;
2685 declAFJointMod_t *jointMod;
2687 const idDict *defArgs;
2688 const idKeyValue *arg;
2690 jointTransformData_t data;
2691 const char *classname, *afName, *modelName;
2693 const idDeclModelDef *modelDef;
2694 const idMD5Anim *MD5anim;
2695 const idMD5Joint *MD5joint;
2696 const idMD5Joint *MD5joints;
2698 idJointMat *originalJoints;
2703 meshAxis.Identity();
2705 classname = args.GetString( "classname" );
2706 defArgs = gameLocal.FindEntityDefDict( classname );
2708 // get the articulated figure
2709 afName = GetArgString( args, defArgs, "articulatedFigure" );
2710 af = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, afName ) );
2715 // get the md5 model
2716 modelName = GetArgString( args, defArgs, "model" );
2717 modelDef = static_cast< const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) );
2722 // make sure model hasn't been purged
2723 if ( modelDef->ModelHandle() && !modelDef->ModelHandle()->IsLoaded() ) {
2724 modelDef->ModelHandle()->LoadModel();
2728 md5 = modelDef->ModelHandle();
2729 if ( !md5 || md5->IsDefaultModel() ) {
2733 // get the articulated figure pose anim
2734 int animNum = modelDef->GetAnim( "af_pose" );
2738 const idAnim *anim = modelDef->GetAnim( animNum );
2742 MD5anim = anim->MD5Anim( 0 );
2743 MD5joints = md5->GetJoints();
2744 numMD5joints = md5->NumJoints();
2746 // setup a render entity
2747 memset( &ent, 0, sizeof( ent ) );
2748 ent.customSkin = modelDef->GetSkin();
2750 ent.numJoints = numMD5joints;
2751 ent.joints = ( idJointMat * )_alloca16( ent.numJoints * sizeof( *ent.joints ) );
2753 // create animation from of the af_pose
2754 ANIM_CreateAnimFrame( md5, MD5anim, ent.numJoints, ent.joints, 1, modelDef->GetVisualOffset(), false );
2756 // buffers to store the initial origin and axis for each body
2757 bodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
2758 bodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
2759 newBodyOrigin = (idVec3 *) _alloca16( af->bodies.Num() * sizeof( idVec3 ) );
2760 newBodyAxis = (idMat3 *) _alloca16( af->bodies.Num() * sizeof( idMat3 ) );
2762 // finish the AF positions
2764 data.joints = MD5joints;
2765 af->Finish( GetJointTransform, ent.joints, &data );
2767 // get the initial origin and axis for each AF body
2768 for ( i = 0; i < af->bodies.Num(); i++ ) {
2771 if ( fb->modelType == TRM_BONE ) {
2772 // axis of bone trace model
2773 axis[2] = fb->v2.ToVec3() - fb->v1.ToVec3();
2774 axis[2].Normalize();
2775 axis[2].NormalVectors( axis[0], axis[1] );
2778 axis = fb->angles.ToMat3();
2781 newBodyOrigin[i] = bodyOrigin[i] = fb->origin.ToVec3();
2782 newBodyAxis[i] = bodyAxis[i] = axis;
2785 // get any new body transforms stored in the key/value pairs
2786 for ( arg = args.MatchPrefix( "body ", NULL ); arg; arg = args.MatchPrefix( "body ", arg ) ) {
2787 name = arg->GetKey();
2788 name.Strip( "body " );
2789 for ( i = 0; i < af->bodies.Num(); i++ ) {
2791 if ( fb->name.Icmp( name ) == 0 ) {
2795 if ( i >= af->bodies.Num() ) {
2798 sscanf( arg->GetValue(), "%f %f %f %f %f %f", &origin.x, &origin.y, &origin.z, &angles.pitch, &angles.yaw, &angles.roll );
2800 if ( fb->jointName.Icmp( "origin" ) == 0 ) {
2801 meshAxis = bodyAxis[i].Transpose() * angles.ToMat3();
2802 meshOrigin = origin - bodyOrigin[i] * meshAxis;
2805 newBodyOrigin[i] = origin;
2806 newBodyAxis[i] = angles.ToMat3();
2810 // save the original joints
2811 originalJoints = ( idJointMat * )_alloca16( numMD5joints * sizeof( originalJoints[0] ) );
2812 memcpy( originalJoints, ent.joints, numMD5joints * sizeof( originalJoints[0] ) );
2814 // buffer to store the joint mods
2815 jointMod = (declAFJointMod_t *) _alloca16( numMD5joints * sizeof( declAFJointMod_t ) );
2816 memset( jointMod, -1, numMD5joints * sizeof( declAFJointMod_t ) );
2817 modifiedOrigin = (idVec3 *) _alloca16( numMD5joints * sizeof( idVec3 ) );
2818 memset( modifiedOrigin, 0, numMD5joints * sizeof( idVec3 ) );
2819 modifiedAxis = (idMat3 *) _alloca16( numMD5joints * sizeof( idMat3 ) );
2820 memset( modifiedAxis, 0, numMD5joints * sizeof( idMat3 ) );
2822 // get all the joint modifications
2823 for ( i = 0; i < af->bodies.Num(); i++ ) {
2826 if ( fb->jointName.Icmp( "origin" ) == 0 ) {
2830 for ( jointNum = 0; jointNum < numMD5joints; jointNum++ ) {
2831 if ( MD5joints[jointNum].name.Icmp( fb->jointName ) == 0 ) {
2836 if ( jointNum >= 0 && jointNum < ent.numJoints ) {
2837 jointMod[ jointNum ] = fb->jointMod;
2838 modifiedAxis[ jointNum ] = ( bodyAxis[i] * originalJoints[jointNum].ToMat3().Transpose() ).Transpose() * ( newBodyAxis[i] * meshAxis.Transpose() );
2839 // FIXME: calculate correct modifiedOrigin
2840 modifiedOrigin[ jointNum ] = originalJoints[ jointNum ].ToVec3();
2844 // apply joint modifications to the skeleton
2845 MD5joint = MD5joints + 1;
2846 for( i = 1; i < numMD5joints; i++, MD5joint++ ) {
2848 parentNum = MD5joint->parent - MD5joints;
2849 idMat3 parentAxis = originalJoints[ parentNum ].ToMat3();
2850 idMat3 localm = originalJoints[i].ToMat3() * parentAxis.Transpose();
2851 idVec3 localt = ( originalJoints[i].ToVec3() - originalJoints[ parentNum ].ToVec3() ) * parentAxis.Transpose();
2853 switch( jointMod[i] ) {
2854 case DECLAF_JOINTMOD_ORIGIN: {
2855 ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
2856 ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
2859 case DECLAF_JOINTMOD_AXIS: {
2860 ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
2861 ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
2864 case DECLAF_JOINTMOD_BOTH: {
2865 ent.joints[ i ].SetRotation( modifiedAxis[ i ] );
2866 ent.joints[ i ].SetTranslation( modifiedOrigin[ i ] );
2870 ent.joints[ i ].SetRotation( localm * ent.joints[ parentNum ].ToMat3() );
2871 ent.joints[ i ].SetTranslation( ent.joints[ parentNum ].ToVec3() + localt * ent.joints[ parentNum ].ToMat3() );
2877 // instantiate a mesh using the joint information from the render entity
2878 return md5->InstantiateDynamicModel( &ent, NULL, NULL );