DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

AF.cpp (34665B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition 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.
     13 
     14 Doom 3 BFG Edition 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.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition 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 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     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.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #include "../idlib/precompiled.h"
     30 #pragma hdrstop
     31 
     32 #include "Game_local.h"
     33 
     34 
     35 /*
     36 ===============================================================================
     37 
     38   Articulated figure controller.
     39 
     40 ===============================================================================
     41 */
     42 #define ARTICULATED_FIGURE_ANIM		"af_pose"
     43 #define POSE_BOUNDS_EXPANSION		5.0f
     44 
     45 /*
     46 ================
     47 idAF::idAF
     48 ================
     49 */
     50 idAF::idAF() {
     51 	self = NULL;
     52 	animator = NULL;
     53 	modifiedAnim = 0;
     54 	baseOrigin.Zero();
     55 	baseAxis.Identity();
     56 	poseTime = -1;
     57 	restStartTime = -1;
     58 	isLoaded = false;
     59 	isActive = false;
     60 	hasBindConstraints = false;
     61 }
     62 
     63 /*
     64 ================
     65 idAF::~idAF
     66 ================
     67 */
     68 idAF::~idAF() {
     69 }
     70 
     71 /*
     72 ================
     73 idAF::Save
     74 ================
     75 */
     76 void idAF::Save( idSaveGame *savefile ) const {
     77 	savefile->WriteObject( self );
     78 	savefile->WriteString( GetName() );
     79 	savefile->WriteBool( hasBindConstraints );
     80 	savefile->WriteVec3( baseOrigin );
     81 	savefile->WriteMat3( baseAxis );
     82 	savefile->WriteInt( poseTime );
     83 	savefile->WriteInt( restStartTime );
     84 	savefile->WriteBool( isLoaded );
     85 	savefile->WriteBool( isActive );
     86 	savefile->WriteStaticObject( physicsObj );
     87 }
     88 
     89 /*
     90 ================
     91 idAF::Restore
     92 ================
     93 */
     94 void idAF::Restore( idRestoreGame *savefile ) {
     95 	savefile->ReadObject( reinterpret_cast<idClass *&>( self ) );
     96 	savefile->ReadString( name );
     97 	savefile->ReadBool( hasBindConstraints );
     98 	savefile->ReadVec3( baseOrigin );
     99 	savefile->ReadMat3( baseAxis );
    100 	savefile->ReadInt( poseTime );
    101 	savefile->ReadInt( restStartTime );
    102 	savefile->ReadBool( isLoaded );
    103 	savefile->ReadBool( isActive );
    104 
    105 	animator = NULL;
    106 	modifiedAnim = 0;
    107 
    108 	if ( self ) {
    109 		SetAnimator( self->GetAnimator() );
    110 		Load( self, name );
    111 		if ( hasBindConstraints ) {
    112 			AddBindConstraints();
    113 		}
    114 	}
    115 
    116 	savefile->ReadStaticObject( physicsObj );
    117 
    118 	if ( self ) {
    119 		if ( isActive ) {
    120 			// clear all animations
    121 			animator->ClearAllAnims( gameLocal.time, 0 );
    122 			animator->ClearAllJoints();
    123 
    124 			// switch to articulated figure physics
    125 			self->RestorePhysics( &physicsObj );
    126 			physicsObj.EnableClip();
    127 		}
    128 		UpdateAnimation();
    129 	}
    130 }
    131 
    132 /*
    133 ================
    134 idAF::UpdateAnimation
    135 ================
    136 */
    137 bool idAF::UpdateAnimation() {
    138 	int i;
    139 	idVec3 origin, renderOrigin, bodyOrigin;
    140 	idMat3 axis, renderAxis, bodyAxis;
    141 	renderEntity_t *renderEntity;
    142 
    143 	if ( !IsLoaded() ) {
    144 		return false;
    145 	}
    146 
    147 	if ( !IsActive() ) {
    148 		return false;
    149 	}
    150 
    151 	renderEntity = self->GetRenderEntity();
    152 	if ( !renderEntity ) {
    153 		return false;
    154 	}
    155 
    156 	if ( physicsObj.IsAtRest() ) {
    157 		if ( restStartTime == physicsObj.GetRestStartTime() ) {
    158 			return false;
    159 		}
    160 		restStartTime = physicsObj.GetRestStartTime();
    161 	}
    162 
    163 	// get the render position
    164 	origin = physicsObj.GetOrigin( 0 );
    165 	axis = physicsObj.GetAxis( 0 );
    166 	renderAxis = baseAxis.Transpose() * axis;
    167 	renderOrigin = origin - baseOrigin * renderAxis;
    168 
    169 	// create an animation frame which reflects the current pose of the articulated figure
    170 	animator->InitAFPose();
    171 	for ( i = 0; i < jointMods.Num(); i++ ) {
    172 		// check for the origin joint
    173 		if ( jointMods[i].jointHandle == 0 ) {
    174 			continue;
    175 		}
    176 		bodyOrigin = physicsObj.GetOrigin( jointMods[i].bodyId );
    177 		bodyAxis = physicsObj.GetAxis( jointMods[i].bodyId );
    178 		axis = jointMods[i].jointBodyAxis.Transpose() * ( bodyAxis * renderAxis.Transpose() );
    179 		origin = ( bodyOrigin - jointMods[i].jointBodyOrigin * axis - renderOrigin ) * renderAxis.Transpose();
    180 		animator->SetAFPoseJointMod( jointMods[i].jointHandle, jointMods[i].jointMod, axis, origin );
    181 	}
    182 	animator->FinishAFPose( modifiedAnim, GetBounds().Expand( POSE_BOUNDS_EXPANSION ), gameLocal.time );
    183 	animator->SetAFPoseBlendWeight( 1.0f );
    184 
    185 	return true;
    186 }
    187 
    188 /*
    189 ================
    190 idAF::GetBounds
    191 
    192   returns bounds for the current pose
    193 ================
    194 */
    195 idBounds idAF::GetBounds() const {
    196 	int i;
    197 	idAFBody *body;
    198 	idVec3 origin, entityOrigin;
    199 	idMat3 axis, entityAxis;
    200 	idBounds bounds, b;
    201 
    202 	bounds.Clear();
    203 
    204 	// get model base transform
    205 	origin = physicsObj.GetOrigin( 0 );
    206 	axis = physicsObj.GetAxis( 0 );
    207 
    208 	entityAxis = baseAxis.Transpose() * axis;
    209 	entityOrigin = origin - baseOrigin * entityAxis;
    210 
    211 	// get bounds relative to base
    212 	for ( i = 0; i < jointMods.Num(); i++ ) {
    213 		body = physicsObj.GetBody( jointMods[i].bodyId );
    214 		origin = ( body->GetWorldOrigin() - entityOrigin ) * entityAxis.Transpose();
    215 		axis = body->GetWorldAxis() * entityAxis.Transpose();
    216 		b.FromTransformedBounds( body->GetClipModel()->GetBounds(), origin, axis );
    217 
    218 		bounds += b;
    219 	}
    220 
    221 	return bounds;
    222 }
    223 
    224 /*
    225 ================
    226 idAF::SetupPose
    227 
    228   Transforms the articulated figure to match the current animation pose of the given entity.
    229 ================
    230 */
    231 void idAF::SetupPose( idEntity *ent, int time ) {
    232 	int i;
    233 	idAFBody *body;
    234 	idVec3 origin;
    235 	idMat3 axis;
    236 	idAnimator *animatorPtr;
    237 	renderEntity_t *renderEntity;
    238 
    239 	if ( !IsLoaded() || !ent ) {
    240 		return;
    241 	}
    242 
    243 	animatorPtr = ent->GetAnimator();
    244 	if ( !animatorPtr ) {
    245 		return;
    246 	}
    247 
    248 	renderEntity = ent->GetRenderEntity();
    249 	if ( !renderEntity ) {
    250 		return;
    251 	}
    252 
    253 	// if the animation is driven by the physics
    254 	if ( self->GetPhysics() == &physicsObj ) {
    255 		return;
    256 	}
    257 
    258 	// if the pose was already updated this frame
    259 	if ( poseTime == time ) {
    260 		return;
    261 	}
    262 	poseTime = time;
    263 
    264 	for ( i = 0; i < jointMods.Num(); i++ ) {
    265 		body = physicsObj.GetBody( jointMods[i].bodyId );
    266 		animatorPtr->GetJointTransform( jointMods[i].jointHandle, time, origin, axis );
    267 		body->SetWorldOrigin( renderEntity->origin + ( origin + jointMods[i].jointBodyOrigin * axis ) * renderEntity->axis );
    268 		body->SetWorldAxis( jointMods[i].jointBodyAxis * axis * renderEntity->axis );
    269 	}
    270 
    271 	if ( isActive ) {
    272 		physicsObj.UpdateClipModels();
    273 	}
    274 }
    275 
    276 /*
    277 ================
    278 idAF::ChangePose
    279 
    280    Change the articulated figure to match the current animation pose of the given entity
    281    and set the velocity relative to the previous pose.
    282 ================
    283 */
    284 void idAF::ChangePose( idEntity *ent, int time ) {
    285 	int i;
    286 	float invDelta;
    287 	idAFBody *body;
    288 	idVec3 origin, lastOrigin;
    289 	idMat3 axis;
    290 	idAnimator *animatorPtr;
    291 	renderEntity_t *renderEntity;
    292 
    293 	if ( !IsLoaded() || !ent ) {
    294 		return;
    295 	}
    296 
    297 	animatorPtr = ent->GetAnimator();
    298 	if ( !animatorPtr ) {
    299 		return;
    300 	}
    301 
    302 	renderEntity = ent->GetRenderEntity();
    303 	if ( !renderEntity ) {
    304 		return;
    305 	}
    306 
    307 	// if the animation is driven by the physics
    308 	if ( self->GetPhysics() == &physicsObj ) {
    309 		return;
    310 	}
    311 
    312 	// if the pose was already updated this frame
    313 	if ( poseTime == time ) {
    314 		return;
    315 	}
    316 	invDelta = 1.0f / MS2SEC( time - poseTime );
    317 	poseTime = time;
    318 
    319 	for ( i = 0; i < jointMods.Num(); i++ ) {
    320 		body = physicsObj.GetBody( jointMods[i].bodyId );
    321 		animatorPtr->GetJointTransform( jointMods[i].jointHandle, time, origin, axis );
    322 		lastOrigin = body->GetWorldOrigin();
    323 		body->SetWorldOrigin( renderEntity->origin + ( origin + jointMods[i].jointBodyOrigin * axis ) * renderEntity->axis );
    324 		body->SetWorldAxis( jointMods[i].jointBodyAxis * axis * renderEntity->axis );
    325 		body->SetLinearVelocity( ( body->GetWorldOrigin() - lastOrigin ) * invDelta );
    326 	}
    327 
    328 	physicsObj.UpdateClipModels();
    329 }
    330 
    331 /*
    332 ================
    333 idAF::EntitiesTouchingAF
    334 ================
    335 */
    336 int idAF::EntitiesTouchingAF( afTouch_t touchList[ MAX_GENTITIES ] ) const {
    337 	int i, j, numClipModels;
    338 	idAFBody *body;
    339 	idClipModel *cm;
    340 	idClipModel *clipModels[ MAX_GENTITIES ];
    341 	int numTouching;
    342 
    343 	if ( !IsLoaded() ) {
    344 		return 0;
    345 	}
    346 
    347 	numTouching = 0;
    348 	numClipModels = gameLocal.clip.ClipModelsTouchingBounds( physicsObj.GetAbsBounds(), -1, clipModels, MAX_GENTITIES );
    349 
    350 	for ( i = 0; i < jointMods.Num(); i++ ) {
    351 		body = physicsObj.GetBody( jointMods[i].bodyId );
    352 
    353 		for ( j = 0; j < numClipModels; j++ ) {
    354 			cm = clipModels[j];
    355 
    356 			if ( !cm || cm->GetEntity() == self ) {
    357 				continue;
    358 			}
    359 
    360 			if ( !cm->IsTraceModel() ) {
    361 				continue;
    362 			}
    363 
    364 			if ( !body->GetClipModel()->GetAbsBounds().IntersectsBounds( cm->GetAbsBounds() ) ) {
    365 				continue;
    366 			}
    367 
    368 			if ( gameLocal.clip.ContentsModel( body->GetWorldOrigin(), body->GetClipModel(), body->GetWorldAxis(), -1, cm->Handle(), cm->GetOrigin(), cm->GetAxis() ) ) {
    369 				touchList[ numTouching ].touchedByBody = body;
    370 				touchList[ numTouching ].touchedClipModel = cm;
    371 				touchList[ numTouching ].touchedEnt  = cm->GetEntity();
    372 				numTouching++;
    373 				clipModels[j] = NULL;
    374 			}
    375 		}
    376 	}
    377 
    378 	return numTouching;
    379 }
    380 
    381 /*
    382 ================
    383 idAF::BodyForClipModelId
    384 ================
    385 */
    386 int idAF::BodyForClipModelId( int id ) const {
    387 	if ( id >= 0 ) {
    388 		return id;
    389 	} else {
    390 		id = CLIPMODEL_ID_TO_JOINT_HANDLE( id );
    391 		if ( id < jointBody.Num() ) {
    392 			return jointBody[id];
    393 		} else {
    394 			return 0;
    395 		}
    396 	}
    397 }
    398 
    399 /*
    400 ================
    401 idAF::GetPhysicsToVisualTransform
    402 ================
    403 */
    404 void idAF::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) const {
    405 	origin = - baseOrigin;
    406 	axis = baseAxis.Transpose();
    407 }
    408 
    409 /*
    410 ================
    411 idAF::GetImpactInfo
    412 ================
    413 */
    414 void idAF::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
    415 	SetupPose( self, gameLocal.time );
    416 	physicsObj.GetImpactInfo( BodyForClipModelId( id ), point, info );
    417 }
    418 
    419 /*
    420 ================
    421 idAF::ApplyImpulse
    422 ================
    423 */
    424 void idAF::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
    425 	SetupPose( self, gameLocal.time );
    426 	physicsObj.ApplyImpulse( BodyForClipModelId( id ), point, impulse );
    427 }
    428 
    429 /*
    430 ================
    431 idAF::AddForce
    432 ================
    433 */
    434 void idAF::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
    435 	SetupPose( self, gameLocal.time );
    436 	physicsObj.AddForce( BodyForClipModelId( id ), point, force );
    437 }
    438 
    439 /*
    440 ================
    441 idAF::AddBody
    442 
    443   Adds a body.
    444 ================
    445 */
    446 void idAF::AddBody( idAFBody *body, const idJointMat *joints, const char *jointName, const AFJointModType_t mod ) {
    447 	int index;
    448 	jointHandle_t handle;
    449 	idVec3 origin;
    450 	idMat3 axis;
    451 
    452 	handle = animator->GetJointHandle( jointName );
    453 	if ( handle == INVALID_JOINT ) {
    454 		gameLocal.Error( "idAF for entity '%s' at (%s) modifies unknown joint '%s'", self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0), jointName );
    455 	}
    456 
    457 	assert( handle < animator->NumJoints() );
    458 	origin = joints[ handle ].ToVec3();
    459 	axis = joints[ handle ].ToMat3();
    460 
    461 	index = jointMods.Num();
    462 	jointMods.SetNum( index + 1 );
    463 	jointMods[index].bodyId = physicsObj.GetBodyId( body );
    464 	jointMods[index].jointHandle = handle;
    465 	jointMods[index].jointMod = mod;
    466 	jointMods[index].jointBodyOrigin = ( body->GetWorldOrigin() - origin ) * axis.Transpose();
    467 	jointMods[index].jointBodyAxis = body->GetWorldAxis() * axis.Transpose();
    468 }
    469 
    470 /*
    471 ================
    472 idAF::SetBase
    473 
    474   Sets the base body.
    475 ================
    476 */
    477 void idAF::SetBase( idAFBody *body, const idJointMat *joints ) {
    478 	physicsObj.ForceBodyId( body, 0 );
    479 	baseOrigin = body->GetWorldOrigin();
    480 	baseAxis = body->GetWorldAxis();
    481 	AddBody( body, joints, animator->GetJointName( animator->GetFirstChild( "origin" ) ), AF_JOINTMOD_AXIS );
    482 }
    483 
    484 /*
    485 ================
    486 idAF::LoadBody
    487 ================
    488 */
    489 bool idAF::LoadBody( const idDeclAF_Body *fb, const idJointMat *joints ) {
    490 	int id, i;
    491 	float length, mass;
    492 	idTraceModel trm;
    493 	idClipModel *clip;
    494 	idAFBody *body;
    495 	idMat3 axis, inertiaTensor;
    496 	idVec3 centerOfMass, origin;
    497 	idBounds bounds;
    498 	idList<jointHandle_t, TAG_AF> jointList;
    499 
    500 	origin = fb->origin.ToVec3();
    501 	axis = fb->angles.ToMat3();
    502 	bounds[0] = fb->v1.ToVec3();
    503 	bounds[1] = fb->v2.ToVec3();
    504 
    505 	switch( fb->modelType ) {
    506 		case TRM_BOX: {
    507 			trm.SetupBox( bounds );
    508 			break;
    509 		}
    510 		case TRM_OCTAHEDRON: {
    511 			trm.SetupOctahedron( bounds );
    512 			break;
    513 		}
    514 		case TRM_DODECAHEDRON: {
    515 			trm.SetupDodecahedron( bounds );
    516 			break;
    517 		}
    518 		case TRM_CYLINDER: {
    519 			trm.SetupCylinder( bounds, fb->numSides );
    520 			break;
    521 		}
    522 		case TRM_CONE: {
    523 			// place the apex at the origin
    524 			bounds[0].z -= bounds[1].z;
    525 			bounds[1].z = 0.0f;
    526 			trm.SetupCone( bounds, fb->numSides );
    527 			break;
    528 		}
    529 		case TRM_BONE: {
    530 			// direction of bone
    531 			axis[2] = fb->v2.ToVec3() - fb->v1.ToVec3();
    532 			length = axis[2].Normalize();
    533 			// axis of bone trace model
    534 			axis[2].NormalVectors( axis[0], axis[1] );
    535 			axis[1] = -axis[1];
    536 			// create bone trace model
    537 			trm.SetupBone( length, fb->width );
    538 			break;
    539 		}
    540 		default:
    541 			assert( 0 );
    542 			break;
    543 	}
    544 	trm.GetMassProperties( 1.0f, mass, centerOfMass, inertiaTensor );
    545 	trm.Translate( -centerOfMass );
    546 	origin += centerOfMass * axis;
    547 
    548 	body = physicsObj.GetBody( fb->name );
    549 	if ( body ) {
    550 		clip = body->GetClipModel();
    551 		if ( !clip->IsEqual( trm ) ) {
    552 			clip = new (TAG_PHYSICS_CLIP_AF) idClipModel( trm );
    553 			clip->SetContents( fb->contents );
    554 			clip->Link( gameLocal.clip, self, 0, origin, axis );
    555 			body->SetClipModel( clip );
    556 		}
    557 		clip->SetContents( fb->contents );
    558 		body->SetDensity( fb->density, fb->inertiaScale );
    559 		body->SetWorldOrigin( origin );
    560 		body->SetWorldAxis( axis );
    561 		id = physicsObj.GetBodyId( body );
    562 	}
    563 	else {
    564 		clip = new (TAG_PHYSICS_CLIP_AF) idClipModel( trm );
    565 		clip->SetContents( fb->contents );
    566 		clip->Link( gameLocal.clip, self, 0, origin, axis );
    567 		body = new (TAG_PHYSICS_AF) idAFBody( fb->name, clip, fb->density );
    568 		if ( fb->inertiaScale != mat3_identity ) {
    569 			body->SetDensity( fb->density, fb->inertiaScale );
    570 		}
    571 		id = physicsObj.AddBody( body );
    572 	}
    573 	if ( fb->linearFriction != -1.0f ) {
    574 		body->SetFriction( fb->linearFriction, fb->angularFriction, fb->contactFriction );
    575 	}
    576 	body->SetClipMask( fb->clipMask );
    577 	body->SetSelfCollision( fb->selfCollision );
    578 
    579 	if ( fb->jointName == "origin" ) {
    580 		SetBase( body, joints );
    581 	} else {
    582 		AFJointModType_t mod;
    583 		if ( fb->jointMod == DECLAF_JOINTMOD_AXIS ) {
    584 			mod = AF_JOINTMOD_AXIS;
    585 		} else if ( fb->jointMod == DECLAF_JOINTMOD_ORIGIN ) {
    586 			mod = AF_JOINTMOD_ORIGIN;
    587 		} else if ( fb->jointMod == DECLAF_JOINTMOD_BOTH ) {
    588 			mod = AF_JOINTMOD_BOTH;
    589 		} else {
    590 			mod = AF_JOINTMOD_AXIS;
    591 		}
    592 		AddBody( body, joints, fb->jointName, mod );
    593 	}
    594 
    595 	if ( fb->frictionDirection.ToVec3() != vec3_origin ) {
    596 		body->SetFrictionDirection( fb->frictionDirection.ToVec3() );
    597 	}
    598 	if ( fb->contactMotorDirection.ToVec3() != vec3_origin ) {
    599 		body->SetContactMotorDirection( fb->contactMotorDirection.ToVec3() );
    600 	}
    601 
    602 	// update table to find the nearest articulated figure body for a joint of the skeletal model
    603 	animator->GetJointList( fb->containedJoints, jointList );
    604 	for( i = 0; i < jointList.Num(); i++ ) {
    605 		if ( jointBody[ jointList[ i ] ] != -1 ) {
    606 			gameLocal.Warning( "%s: joint '%s' is already contained by body '%s'",
    607 						name.c_str(), animator->GetJointName( (jointHandle_t)jointList[i] ),
    608 							physicsObj.GetBody( jointBody[ jointList[ i ] ] )->GetName().c_str() );
    609 		}
    610 		jointBody[ jointList[ i ] ] = id;
    611 	}
    612 
    613 	return true;
    614 }
    615 
    616 /*
    617 ================
    618 idAF::LoadConstraint
    619 ================
    620 */
    621 bool idAF::LoadConstraint( const idDeclAF_Constraint *fc ) {
    622 	idAFBody *body1, *body2;
    623 	idAngles angles;
    624 	idMat3 axis;	
    625 
    626 	body1 = physicsObj.GetBody( fc->body1 );
    627 	body2 = physicsObj.GetBody( fc->body2 );
    628 
    629 	switch( fc->type ) {
    630 		case DECLAF_CONSTRAINT_FIXED: {
    631 			idAFConstraint_Fixed *c;
    632 			c = static_cast<idAFConstraint_Fixed *>(physicsObj.GetConstraint( fc->name ));
    633 			if ( c ) {
    634 				c->SetBody1( body1 );
    635 				c->SetBody2( body2 );
    636 			}
    637 			else {
    638 				c = new (TAG_PHYSICS_AF) idAFConstraint_Fixed( fc->name, body1, body2 );
    639 				physicsObj.AddConstraint( c );
    640 			}
    641 			break;
    642 		}
    643 		case DECLAF_CONSTRAINT_BALLANDSOCKETJOINT: {
    644 			idAFConstraint_BallAndSocketJoint *c;
    645 			c = static_cast<idAFConstraint_BallAndSocketJoint *>(physicsObj.GetConstraint( fc->name ));
    646 			if ( c ) {
    647 				c->SetBody1( body1 );
    648 				c->SetBody2( body2 );
    649 			}
    650 			else {
    651 				c = new (TAG_PHYSICS_AF) idAFConstraint_BallAndSocketJoint( fc->name, body1, body2 );
    652 				physicsObj.AddConstraint( c );
    653 			}
    654 			c->SetAnchor( fc->anchor.ToVec3() );
    655 			c->SetFriction( fc->friction );
    656 			switch( fc->limit ) {
    657 				case idDeclAF_Constraint::LIMIT_CONE: {
    658 					c->SetConeLimit( fc->limitAxis.ToVec3(), fc->limitAngles[0], fc->shaft[0].ToVec3() );
    659 					break;
    660 				}
    661 				case idDeclAF_Constraint::LIMIT_PYRAMID: {
    662 					angles = fc->limitAxis.ToVec3().ToAngles();
    663 					angles.roll = fc->limitAngles[2];
    664 					axis = angles.ToMat3();
    665 					c->SetPyramidLimit( axis[0], axis[1], fc->limitAngles[0], fc->limitAngles[1], fc->shaft[0].ToVec3() );
    666 					break;
    667 				}
    668 				default: {
    669 					c->SetNoLimit();
    670 					break;
    671 				}
    672 			}
    673 			break;
    674 		}
    675 		case DECLAF_CONSTRAINT_UNIVERSALJOINT: {
    676 			idAFConstraint_UniversalJoint *c;
    677 			c = static_cast<idAFConstraint_UniversalJoint *>(physicsObj.GetConstraint( fc->name ));
    678 			if ( c ) {
    679 				c->SetBody1( body1 );
    680 				c->SetBody2( body2 );
    681 			}
    682 			else {
    683 				c = new (TAG_PHYSICS_AF) idAFConstraint_UniversalJoint( fc->name, body1, body2 );
    684 				physicsObj.AddConstraint( c );
    685 			}
    686 			c->SetAnchor( fc->anchor.ToVec3() );
    687 			c->SetShafts( fc->shaft[0].ToVec3(), fc->shaft[1].ToVec3() );
    688 			c->SetFriction( fc->friction );
    689 			switch( fc->limit ) {
    690 				case idDeclAF_Constraint::LIMIT_CONE: {
    691 					c->SetConeLimit( fc->limitAxis.ToVec3(), fc->limitAngles[0] );
    692 					break;
    693 				}
    694 				case idDeclAF_Constraint::LIMIT_PYRAMID: {
    695 					angles = fc->limitAxis.ToVec3().ToAngles();
    696 					angles.roll = fc->limitAngles[2];
    697 					axis = angles.ToMat3();
    698 					c->SetPyramidLimit( axis[0], axis[1], fc->limitAngles[0], fc->limitAngles[1] );
    699 					break;
    700 				}
    701 				default: {
    702 					c->SetNoLimit();
    703 					break;
    704 				}
    705 			}
    706 			break;
    707 		}
    708 		case DECLAF_CONSTRAINT_HINGE: {
    709 			idAFConstraint_Hinge *c;
    710 			c = static_cast<idAFConstraint_Hinge *>(physicsObj.GetConstraint( fc->name ));
    711 			if ( c ) {
    712 				c->SetBody1( body1 );
    713 				c->SetBody2( body2 );
    714 			}
    715 			else {
    716 				c = new (TAG_PHYSICS_AF) idAFConstraint_Hinge( fc->name, body1, body2 );
    717 				physicsObj.AddConstraint( c );
    718 			}
    719 			c->SetAnchor( fc->anchor.ToVec3() );
    720 			c->SetAxis( fc->axis.ToVec3() );
    721 			c->SetFriction( fc->friction );
    722 			switch( fc->limit ) {
    723 				case idDeclAF_Constraint::LIMIT_CONE: {
    724 					idVec3 left, up, axis, shaft;
    725 					fc->axis.ToVec3().OrthogonalBasis( left, up );
    726 					axis = left * idRotation( vec3_origin, fc->axis.ToVec3(), fc->limitAngles[0] );
    727 					shaft = left * idRotation( vec3_origin, fc->axis.ToVec3(), fc->limitAngles[2] );
    728 					c->SetLimit( axis, fc->limitAngles[1], shaft );
    729 					break;
    730 				}
    731 				default: {
    732 					c->SetNoLimit();
    733 					break;
    734 				}
    735 			}
    736 			break;
    737 		}
    738 		case DECLAF_CONSTRAINT_SLIDER: {
    739 			idAFConstraint_Slider *c;
    740 			c = static_cast<idAFConstraint_Slider *>(physicsObj.GetConstraint( fc->name ));
    741 			if ( c ) {
    742 				c->SetBody1( body1 );
    743 				c->SetBody2( body2 );
    744 			}
    745 			else {
    746 				c = new (TAG_PHYSICS_AF) idAFConstraint_Slider( fc->name, body1, body2 );
    747 				physicsObj.AddConstraint( c );
    748 			}
    749 			c->SetAxis( fc->axis.ToVec3() );
    750 			break;
    751 		}
    752 		case DECLAF_CONSTRAINT_SPRING: {
    753 			idAFConstraint_Spring *c;
    754 			c = static_cast<idAFConstraint_Spring *>(physicsObj.GetConstraint( fc->name ));
    755 			if ( c ) {
    756 				c->SetBody1( body1 );
    757 				c->SetBody2( body2 );
    758 			}
    759 			else {
    760 				c = new (TAG_PHYSICS_AF) idAFConstraint_Spring( fc->name, body1, body2 );
    761 				physicsObj.AddConstraint( c );
    762 			}
    763 			c->SetAnchor( fc->anchor.ToVec3(), fc->anchor2.ToVec3() );
    764 			c->SetSpring( fc->stretch, fc->compress, fc->damping, fc->restLength );
    765 			c->SetLimit( fc->minLength, fc->maxLength );
    766 			break;
    767 		}
    768 	}
    769 	return true;
    770 }
    771 
    772 /*
    773 ================
    774 GetJointTransform
    775 ================
    776 */
    777 static bool GetJointTransform( void *model, const idJointMat *frame, const char *jointName, idVec3 &origin, idMat3 &axis ) {
    778 	jointHandle_t	joint;
    779 
    780 	joint = reinterpret_cast<idAnimator *>(model)->GetJointHandle( jointName );
    781 	if ( ( joint >= 0 ) && ( joint < reinterpret_cast<idAnimator *>(model)->NumJoints() ) ) {
    782 		origin = frame[ joint ].ToVec3();
    783 		axis = frame[ joint ].ToMat3();
    784 		return true;
    785 	} else {
    786 		return false;
    787 	}
    788 }
    789 
    790 /*
    791 ================
    792 idAF::Load
    793 ================
    794 */
    795 bool idAF::Load( idEntity *ent, const char *fileName ) {
    796 	int i, j;
    797 	const idDeclAF *file;
    798 	const idDeclModelDef *modelDef;
    799 	idRenderModel *model;
    800 	int numJoints;
    801 	idJointMat *joints;
    802 
    803 	assert( ent );
    804 
    805 	self = ent;
    806 	physicsObj.SetSelf( self );
    807 
    808 	if ( animator == NULL ) {
    809 		gameLocal.Warning( "Couldn't load af '%s' for entity '%s' at (%s): NULL animator\n", name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0) );
    810 		return false;
    811 	}
    812 
    813 	name = fileName;
    814 	name.StripFileExtension();
    815 
    816 	file = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, name ) );
    817 	if ( !file ) {
    818 		gameLocal.Warning( "Couldn't load af '%s' for entity '%s' at (%s)\n", name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0) );
    819 		return false;
    820 	}
    821 
    822 	if ( file->bodies.Num() == 0 || file->bodies[0]->jointName != "origin" ) {
    823 		gameLocal.Warning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no body which modifies the origin joint.",
    824 							name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0) );
    825 		return false;
    826 	}
    827 
    828 	modelDef = animator->ModelDef();
    829 	if ( modelDef == NULL || modelDef->GetState() == DS_DEFAULTED ) {
    830 		gameLocal.Warning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no or defaulted modelDef '%s'",
    831 							name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0), modelDef ? modelDef->GetName() : "" );
    832 		return false;
    833 	}
    834 
    835 	model = animator->ModelHandle();
    836 	if ( model == NULL || model->IsDefaultModel() ) {
    837 		gameLocal.Warning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no or defaulted model '%s'",
    838 							name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0), model ? model->Name() : "" );
    839 		return false;
    840 	}
    841 
    842 	// get the modified animation
    843 	modifiedAnim = animator->GetAnim( ARTICULATED_FIGURE_ANIM );
    844 	if ( !modifiedAnim ) {
    845 		gameLocal.Warning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no modified animation '%s'",
    846 							name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(0), ARTICULATED_FIGURE_ANIM );
    847 		return false;
    848 	}
    849 
    850 	// create the animation frame used to setup the articulated figure
    851 	numJoints = animator->NumJoints();
    852 	joints = ( idJointMat * )_alloca16( numJoints * sizeof( joints[0] ) );
    853 	gameEdit->ANIM_CreateAnimFrame( model, animator->GetAnim( modifiedAnim )->MD5Anim( 0 ), numJoints, joints, 1, animator->ModelDef()->GetVisualOffset(), animator->RemoveOrigin() );
    854 
    855 	// set all vector positions from model joints
    856 	file->Finish( GetJointTransform, joints, animator );
    857 
    858 	// initialize articulated figure physics
    859 	physicsObj.SetGravity( gameLocal.GetGravity() );
    860 	physicsObj.SetClipMask( file->clipMask );
    861 	physicsObj.SetDefaultFriction( file->defaultLinearFriction, file->defaultAngularFriction, file->defaultContactFriction );
    862 	physicsObj.SetSuspendSpeed( file->suspendVelocity, file->suspendAcceleration );
    863 	physicsObj.SetSuspendTolerance( file->noMoveTime, file->noMoveTranslation, file->noMoveRotation );
    864 	physicsObj.SetSuspendTime( file->minMoveTime, file->maxMoveTime );
    865 	physicsObj.SetSelfCollision( file->selfCollision );
    866 
    867 	// clear the list with transforms from joints to bodies
    868 	jointMods.SetNum( 0 );
    869 
    870 	// clear the joint to body conversion list
    871 	jointBody.AssureSize( animator->NumJoints() );
    872 	for ( i = 0; i < jointBody.Num(); i++ ) {
    873 		jointBody[i] = -1;
    874 	}
    875 
    876 	// delete any bodies in the physicsObj that are no longer in the idDeclAF
    877 	for ( i = 0; i < physicsObj.GetNumBodies(); i++ ) {
    878 		idAFBody *body = physicsObj.GetBody( i );
    879 		for ( j = 0; j < file->bodies.Num(); j++ ) {
    880 			if ( file->bodies[j]->name.Icmp( body->GetName() ) == 0 ) {
    881 				break;
    882 			}
    883 		}
    884 		if ( j >= file->bodies.Num() ) {
    885 			physicsObj.DeleteBody( i );
    886 			i--;
    887 		}
    888 	}
    889 
    890 	// delete any constraints in the physicsObj that are no longer in the idDeclAF
    891 	for ( i = 0; i < physicsObj.GetNumConstraints(); i++ ) {
    892 		idAFConstraint *constraint = physicsObj.GetConstraint( i );
    893 		for ( j = 0; j < file->constraints.Num(); j++ ) {
    894 			// idADConstraint enum is a superset of declADConstraint, so the cast is valid
    895 			if ( file->constraints[j]->name.Icmp( constraint->GetName() ) == 0 &&
    896 					(constraintType_t)(file->constraints[j]->type) == constraint->GetType() ) {
    897 				break;
    898 			}
    899 		}
    900 		if ( j >= file->constraints.Num() ) {
    901 			physicsObj.DeleteConstraint( i );
    902 			i--;
    903 		}
    904 	}
    905 
    906 	// load bodies from the file
    907 	for ( i = 0; i < file->bodies.Num(); i++ ) {
    908 		LoadBody( file->bodies[i], joints );
    909 	}
    910 
    911 	// load constraints from the file
    912 	for ( i = 0; i < file->constraints.Num(); i++ ) {
    913 		LoadConstraint( file->constraints[i] );
    914 	}
    915 
    916 	physicsObj.UpdateClipModels();
    917 
    918 	// check if each joint is contained by a body
    919 	for( i = 0; i < animator->NumJoints(); i++ ) {
    920 		if ( jointBody[i] == -1 ) {
    921 			gameLocal.Warning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) joint '%s' is not contained by a body",
    922 				name.c_str(), self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0), animator->GetJointName( (jointHandle_t)i ) );
    923 		}
    924 	}
    925 
    926 	physicsObj.SetMass( file->totalMass );
    927 	physicsObj.SetChanged();
    928 
    929 	// disable the articulated figure for collision detection until activated
    930 	physicsObj.DisableClip();
    931 
    932 	isLoaded = true;
    933 
    934 	return true;
    935 }
    936 
    937 /*
    938 ================
    939 idAF::Start
    940 ================
    941 */
    942 void idAF::Start() {
    943 	if ( !IsLoaded() ) {
    944 		return;
    945 	}
    946 	// clear all animations
    947 	animator->ClearAllAnims( gameLocal.time, 0 );
    948 	animator->ClearAllJoints();
    949 	// switch to articulated figure physics
    950 	self->SetPhysics( &physicsObj );
    951 	// start the articulated figure physics simulation
    952 	physicsObj.EnableClip();
    953 	physicsObj.Activate();
    954 	isActive = true;
    955 }
    956 
    957 /*
    958 ================
    959 idAF::TestSolid
    960 ================
    961 */
    962 bool idAF::TestSolid() const {
    963 	int i;
    964 	idAFBody *body;
    965 	trace_t trace;
    966 	idStr str;
    967 	bool solid;
    968 
    969 	if ( !IsLoaded() ) {
    970 		return false;
    971 	}
    972 
    973 	if ( !af_testSolid.GetBool() ) {
    974 		return false;
    975 	}
    976 
    977 	solid = false;
    978 
    979 	for ( i = 0; i < physicsObj.GetNumBodies(); i++ ) {
    980 		body = physicsObj.GetBody( i );
    981 		if ( gameLocal.clip.Translation( trace, body->GetWorldOrigin(), body->GetWorldOrigin(), body->GetClipModel(), body->GetWorldAxis(), body->GetClipMask(), self ) ) {
    982 			float depth = idMath::Fabs( trace.c.point * trace.c.normal - trace.c.dist );
    983 
    984 			body->SetWorldOrigin( body->GetWorldOrigin() + trace.c.normal * ( depth + 8.0f ) );
    985 
    986 			gameLocal.DWarning( "%s: body '%s' stuck in %d (normal = %.2f %.2f %.2f, depth = %.2f)", self->name.c_str(),
    987 						body->GetName().c_str(), trace.c.contents, trace.c.normal.x, trace.c.normal.y, trace.c.normal.z, depth );
    988 			solid = true;
    989 
    990 		}
    991 	}
    992 	return solid;
    993 }
    994 
    995 /*
    996 ================
    997 idAF::StartFromCurrentPose
    998 ================
    999 */
   1000 void idAF::StartFromCurrentPose( int inheritVelocityTime ) {
   1001 
   1002 	if ( !IsLoaded() ) {
   1003 		return;
   1004 	}
   1005 
   1006 	// if the ragdoll should inherit velocity from the animation
   1007 	if ( inheritVelocityTime > 0 ) {
   1008 
   1009 		// make sure the ragdoll is at rest
   1010 		physicsObj.PutToRest();
   1011 
   1012 		// set the pose for some time back
   1013 		SetupPose( self, gameLocal.time - inheritVelocityTime );
   1014 
   1015 		// change the pose for the current time and set velocities
   1016 		ChangePose( self, gameLocal.time );
   1017 	}
   1018 	else {
   1019 		// transform the articulated figure to reflect the current animation pose
   1020 		SetupPose( self, gameLocal.time );
   1021 	}
   1022 
   1023 	physicsObj.UpdateClipModels();
   1024 
   1025 	TestSolid();
   1026 
   1027 	Start();
   1028 
   1029 	UpdateAnimation();
   1030 
   1031 	// update the render entity origin and axis
   1032 	self->UpdateModel();
   1033 
   1034 	// make sure the renderer gets the updated origin and axis
   1035 	self->Present();
   1036 }
   1037 
   1038 /*
   1039 ================
   1040 idAF::Stop
   1041 ================
   1042 */
   1043 void idAF::Stop() {
   1044 	// disable the articulated figure for collision detection
   1045 	physicsObj.UnlinkClip();
   1046 	isActive = false;
   1047 }
   1048 
   1049 /*
   1050 ================
   1051 idAF::Rest
   1052 ================
   1053 */
   1054 void idAF::Rest() {
   1055 	physicsObj.PutToRest();
   1056 }
   1057 
   1058 /*
   1059 ================
   1060 idAF::SetConstraintPosition
   1061 
   1062   Only moves constraints that bind the entity to another entity.
   1063 ================
   1064 */
   1065 void idAF::SetConstraintPosition( const char *name, const idVec3 &pos ) {
   1066 	idAFConstraint *constraint;
   1067 
   1068 	constraint = GetPhysics()->GetConstraint( name );
   1069 
   1070 	if ( !constraint ) {
   1071 		gameLocal.Warning( "can't find a constraint with the name '%s'", name );
   1072 		return;
   1073 	}
   1074 
   1075 	if ( constraint->GetBody2() != NULL ) {
   1076 		gameLocal.Warning( "constraint '%s' does not bind to another entity", name );
   1077 		return;
   1078 	}
   1079 
   1080 	switch( constraint->GetType() ) {
   1081 		case CONSTRAINT_BALLANDSOCKETJOINT: {
   1082 			idAFConstraint_BallAndSocketJoint *bs = static_cast<idAFConstraint_BallAndSocketJoint *>(constraint);
   1083 			bs->Translate( pos - bs->GetAnchor() );
   1084 			break;
   1085 		}
   1086 		case CONSTRAINT_UNIVERSALJOINT: {
   1087 			idAFConstraint_UniversalJoint *uj = static_cast<idAFConstraint_UniversalJoint *>(constraint);
   1088 			uj->Translate( pos - uj->GetAnchor() );
   1089 			break;
   1090 		}
   1091 		case CONSTRAINT_HINGE: {
   1092 			idAFConstraint_Hinge *hinge = static_cast<idAFConstraint_Hinge *>(constraint);
   1093 			hinge->Translate( pos - hinge->GetAnchor() );
   1094 			break;
   1095 		}
   1096 		default: {
   1097 			gameLocal.Warning( "cannot set the constraint position for '%s'", name );
   1098 			break;
   1099 		}
   1100 	}
   1101 }
   1102 
   1103 /*
   1104 ================
   1105 idAF::SaveState
   1106 ================
   1107 */
   1108 void idAF::SaveState( idDict &args ) const {
   1109 	int i;
   1110 	idAFBody *body;
   1111 	idStr key, value;
   1112 
   1113 	for ( i = 0; i < jointMods.Num(); i++ ) {
   1114 		body = physicsObj.GetBody( jointMods[i].bodyId );
   1115 
   1116 		key = "body " + body->GetName();
   1117 		value = body->GetWorldOrigin().ToString( 8 );
   1118 		value += " ";
   1119 		value += body->GetWorldAxis().ToAngles().ToString( 8 );
   1120 		args.Set( key, value );
   1121 	}
   1122 }
   1123 
   1124 /*
   1125 ================
   1126 idAF::LoadState
   1127 ================
   1128 */
   1129 void idAF::LoadState( const idDict &args ) {
   1130 	const idKeyValue *kv;
   1131 	idStr name;
   1132 	idAFBody *body;
   1133 	idVec3 origin;
   1134 	idAngles angles;
   1135 
   1136 	kv = args.MatchPrefix( "body ", NULL );
   1137 	while ( kv ) {
   1138 
   1139 		name = kv->GetKey();
   1140 		name.Strip( "body " );
   1141 		body = physicsObj.GetBody( name );
   1142 		if ( body ) {
   1143 			sscanf( kv->GetValue(), "%f %f %f %f %f %f", &origin.x, &origin.y, &origin.z, &angles.pitch, &angles.yaw, &angles.roll );
   1144 			body->SetWorldOrigin( origin );
   1145 			body->SetWorldAxis( angles.ToMat3() );
   1146 		} else {
   1147 			gameLocal.Warning("Unknown body part %s in articulated figure %s", name.c_str(), this->name.c_str()); 
   1148 		}
   1149 
   1150 		kv = args.MatchPrefix( "body ", kv );
   1151 	}
   1152 
   1153 	physicsObj.UpdateClipModels();
   1154 }
   1155 
   1156 /*
   1157 ================
   1158 idAF::AddBindConstraints
   1159 ================
   1160 */
   1161 void idAF::AddBindConstraints() {
   1162 	const idKeyValue *kv;
   1163 	idStr name;
   1164 	idAFBody *body;
   1165 	idLexer lexer;
   1166 	idToken type, bodyName, jointName;
   1167 	idVec3 origin, renderOrigin;
   1168 	idMat3 axis, renderAxis;
   1169 
   1170 	if ( !IsLoaded() ) {
   1171 		return;
   1172 	}
   1173 
   1174 	const idDict &args = self->spawnArgs;
   1175 
   1176 	// get the render position
   1177 	origin = physicsObj.GetOrigin( 0 );
   1178 	axis = physicsObj.GetAxis( 0 );
   1179 	renderAxis = baseAxis.Transpose() * axis;
   1180 	renderOrigin = origin - baseOrigin * renderAxis;
   1181 
   1182 	// parse all the bind constraints
   1183 	for ( kv = args.MatchPrefix( "bindConstraint ", NULL ); kv; kv = args.MatchPrefix( "bindConstraint ", kv ) ) {
   1184 		name = kv->GetKey();
   1185 		name.Strip( "bindConstraint " );
   1186 
   1187 		lexer.LoadMemory( kv->GetValue(), kv->GetValue().Length(), kv->GetKey() );
   1188 		lexer.ReadToken( &type );
   1189 
   1190 		lexer.ReadToken( &bodyName );
   1191 		body = physicsObj.GetBody( bodyName );
   1192 		if ( !body ) {
   1193 			gameLocal.Warning( "idAF::AddBindConstraints: body '%s' not found on entity '%s'", bodyName.c_str(), self->name.c_str() );
   1194 			lexer.FreeSource();
   1195 			continue;
   1196 		}
   1197 
   1198 		if ( type.Icmp( "fixed" ) == 0 ) {
   1199 			idAFConstraint_Fixed *c;
   1200 
   1201 			c = new (TAG_PHYSICS_AF) idAFConstraint_Fixed( name, body, NULL );
   1202 			physicsObj.AddConstraint( c );
   1203 		}
   1204 		else if ( type.Icmp( "ballAndSocket" ) == 0 ) {
   1205 			idAFConstraint_BallAndSocketJoint *c;
   1206 
   1207 			c = new (TAG_PHYSICS_AF) idAFConstraint_BallAndSocketJoint( name, body, NULL );
   1208 			physicsObj.AddConstraint( c );
   1209 			lexer.ReadToken( &jointName );
   1210 
   1211 			jointHandle_t joint = animator->GetJointHandle( jointName );
   1212 			if ( joint == INVALID_JOINT ) {
   1213 				gameLocal.Warning( "idAF::AddBindConstraints: joint '%s' not found", jointName.c_str() );
   1214 			}
   1215 
   1216 			animator->GetJointTransform( joint, gameLocal.time, origin, axis );
   1217 			c->SetAnchor( renderOrigin + origin * renderAxis );
   1218 		}
   1219 		else if ( type.Icmp( "universal" ) == 0 ) {
   1220 			idAFConstraint_UniversalJoint *c;
   1221 
   1222 			c = new (TAG_PHYSICS_AF) idAFConstraint_UniversalJoint( name, body, NULL );
   1223 			physicsObj.AddConstraint( c );
   1224 			lexer.ReadToken( &jointName );
   1225 
   1226 			jointHandle_t joint = animator->GetJointHandle( jointName );
   1227 			if ( joint == INVALID_JOINT ) {
   1228 				gameLocal.Warning( "idAF::AddBindConstraints: joint '%s' not found", jointName.c_str() );
   1229 			}
   1230 			animator->GetJointTransform( joint, gameLocal.time, origin, axis );
   1231 			c->SetAnchor( renderOrigin + origin * renderAxis );
   1232 			c->SetShafts( idVec3( 0, 0, 1 ), idVec3( 0, 0, -1 ) );
   1233 		}
   1234 		else {
   1235 			gameLocal.Warning( "idAF::AddBindConstraints: unknown constraint type '%s' on entity '%s'", type.c_str(), self->name.c_str() );
   1236 		}
   1237 
   1238 		lexer.FreeSource();
   1239 	}
   1240 
   1241 	hasBindConstraints = true;
   1242 }
   1243 
   1244 /*
   1245 ================
   1246 idAF::RemoveBindConstraints
   1247 ================
   1248 */
   1249 void idAF::RemoveBindConstraints() {
   1250 	const idKeyValue *kv;
   1251 
   1252 	if ( !IsLoaded() ) {
   1253 		return;
   1254 	}
   1255 
   1256 	const idDict &args = self->spawnArgs;
   1257 	idStr name;
   1258 
   1259 	kv = args.MatchPrefix( "bindConstraint ", NULL );
   1260 	while ( kv ) {
   1261 		name = kv->GetKey();
   1262 		name.Strip( "bindConstraint " );
   1263 
   1264 		if ( physicsObj.GetConstraint( name ) ) {
   1265             physicsObj.DeleteConstraint( name );
   1266 		}
   1267 
   1268 		kv = args.MatchPrefix( "bindConstraint ", kv );
   1269 	}
   1270 
   1271 	hasBindConstraints = false;
   1272 }