DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

IK.cpp (32654B)


      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   idIK
     38 
     39 ===============================================================================
     40 */
     41 
     42 /*
     43 ================
     44 idIK::idIK
     45 ================
     46 */
     47 idIK::idIK() {
     48 	ik_activate = false;
     49 	initialized = false;
     50 	self = NULL;
     51 	animator = NULL;
     52 	modifiedAnim = 0;
     53 	modelOffset.Zero();
     54 }
     55 
     56 /*
     57 ================
     58 idIK::~idIK
     59 ================
     60 */
     61 idIK::~idIK() {
     62 }
     63 
     64 /*
     65 ================
     66 idIK::Save
     67 ================
     68 */
     69 void idIK::Save( idSaveGame *savefile ) const {
     70 	savefile->WriteBool( initialized );
     71 	savefile->WriteBool( ik_activate );
     72 	savefile->WriteObject( self );
     73 	savefile->WriteString( animator != NULL && animator->GetAnim( modifiedAnim ) ? animator->GetAnim( modifiedAnim )->Name() : "" );
     74 	savefile->WriteVec3( modelOffset );
     75 }
     76 
     77 /*
     78 ================
     79 idIK::Restore
     80 ================
     81 */
     82 void idIK::Restore( idRestoreGame *savefile ) {
     83 	idStr anim;
     84 
     85 	savefile->ReadBool( initialized );
     86 	savefile->ReadBool( ik_activate );
     87 	savefile->ReadObject( reinterpret_cast<idClass *&>( self ) );
     88 	savefile->ReadString( anim );
     89 	savefile->ReadVec3( modelOffset );
     90 
     91 	if ( self ) {
     92 		animator = self->GetAnimator();
     93 		if ( animator == NULL || animator->ModelDef() == NULL ) {
     94 			gameLocal.Warning( "idIK::Restore: IK for entity '%s' at (%s) has no model set.",
     95 								self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
     96 			return;
     97 		}
     98 		modifiedAnim = animator->GetAnim( anim );
     99 		if ( modifiedAnim == 0 ) {
    100 			gameLocal.Warning( "idIK::Restore: IK for entity '%s' at (%s) has no modified animation.",
    101 									self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
    102 		}
    103 	} else {
    104 		animator = NULL;
    105 		modifiedAnim = 0;
    106 	}
    107 }
    108 
    109 /*
    110 ================
    111 idIK::IsInitialized
    112 ================
    113 */
    114 bool idIK::IsInitialized() const {
    115 	return initialized && ik_enable.GetBool();
    116 }
    117 
    118 /*
    119 ================
    120 idIK::Init
    121 ================
    122 */
    123 bool idIK::Init( idEntity *self, const char *anim, const idVec3 &modelOffset ) {
    124 	idRenderModel *model;
    125 
    126 	if ( self == NULL ) {
    127 		return false;
    128 	}
    129 
    130 	this->self = self;
    131 
    132 	animator = self->GetAnimator();
    133 	if ( animator == NULL || animator->ModelDef() == NULL ) {
    134 		gameLocal.Warning( "idIK::Init: IK for entity '%s' at (%s) has no model set.",
    135 							self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
    136 		return false;
    137 	}
    138 	if ( animator->ModelDef()->ModelHandle() == NULL ) {
    139 		gameLocal.Warning( "idIK::Init: IK for entity '%s' at (%s) uses default model.",
    140 							self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
    141 		return false;
    142 	}
    143 	model = animator->ModelHandle();
    144 	if ( model == NULL ) {
    145 		gameLocal.Warning( "idIK::Init: IK for entity '%s' at (%s) has no model set.",
    146 							self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
    147 		return false;
    148 	}
    149 	modifiedAnim = animator->GetAnim( anim );
    150 	if ( modifiedAnim == 0 ) {
    151 		gameLocal.Warning( "idIK::Init: IK for entity '%s' at (%s) has no modified animation.",
    152 								self->name.c_str(), self->GetPhysics()->GetOrigin().ToString(0) );
    153 		return false;
    154 	}
    155 	
    156 	this->modelOffset = modelOffset;
    157 
    158 	return true;
    159 }
    160 
    161 /*
    162 ================
    163 idIK::Evaluate
    164 ================
    165 */
    166 void idIK::Evaluate() {
    167 }
    168 
    169 /*
    170 ================
    171 idIK::ClearJointMods
    172 ================
    173 */
    174 void idIK::ClearJointMods() {
    175 	ik_activate = false;
    176 }
    177 
    178 /*
    179 ================
    180 idIK::SolveTwoBones
    181 ================
    182 */
    183 bool idIK::SolveTwoBones( const idVec3 &startPos, const idVec3 &endPos, const idVec3 &dir, float len0, float len1, idVec3 &jointPos ) {
    184 	float length, lengthSqr, lengthInv, x, y;
    185 	idVec3 vec0, vec1;
    186 
    187 	vec0 = endPos - startPos;
    188 	lengthSqr = vec0.LengthSqr();
    189 	lengthInv = idMath::InvSqrt( lengthSqr );
    190 	length = lengthInv * lengthSqr;
    191 
    192 	// if the start and end position are too far out or too close to each other
    193 	if ( length > len0 + len1 || length < idMath::Fabs( len0 - len1 ) ) {
    194 		jointPos = startPos + 0.5f * vec0;
    195 		return false;
    196 	}
    197 
    198 	vec0 *= lengthInv;
    199 	vec1 = dir - vec0 * dir * vec0;
    200 	vec1.Normalize();
    201 
    202 	x = ( length * length + len0 * len0 - len1 * len1 ) * ( 0.5f * lengthInv );
    203 	y = idMath::Sqrt( len0 * len0 - x * x );
    204 
    205 	jointPos = startPos + x * vec0 + y * vec1;
    206 
    207 	return true;
    208 }
    209 
    210 /*
    211 ================
    212 idIK::GetBoneAxis
    213 ================
    214 */
    215 float idIK::GetBoneAxis( const idVec3 &startPos, const idVec3 &endPos, const idVec3 &dir, idMat3 &axis ) {
    216 	float length;
    217 	axis[0] = endPos - startPos;
    218 	length = axis[0].Normalize();
    219 	axis[1] = dir - axis[0] * dir * axis[0];
    220 	axis[1].Normalize();
    221 	axis[2].Cross( axis[1], axis[0] );
    222 	return length;
    223 }
    224 
    225 
    226 /*
    227 ===============================================================================
    228 
    229   idIK_Walk
    230 
    231 ===============================================================================
    232 */
    233 
    234 /*
    235 ================
    236 idIK_Walk::idIK_Walk
    237 ================
    238 */
    239 idIK_Walk::idIK_Walk() {
    240 	int i;
    241 
    242 	initialized = false;
    243 	footModel = NULL;
    244 	numLegs = 0;
    245 	enabledLegs = 0;
    246 	for ( i = 0; i < MAX_LEGS; i++ ) {
    247 		footJoints[i] = INVALID_JOINT;
    248 		ankleJoints[i] = INVALID_JOINT;
    249 		kneeJoints[i] = INVALID_JOINT;
    250 		hipJoints[i] = INVALID_JOINT;
    251 		dirJoints[i] = INVALID_JOINT;
    252 		hipForward[i].Zero();
    253 		kneeForward[i].Zero();
    254 		upperLegLength[i] = 0.0f;
    255 		lowerLegLength[i] = 0.0f;
    256 		upperLegToHipJoint[i].Identity();
    257 		lowerLegToKneeJoint[i].Identity();
    258 		oldAnkleHeights[i] = 0.0f;
    259 	}
    260 	waistJoint = INVALID_JOINT;
    261 
    262 	smoothing = 0.75f;
    263 	waistSmoothing = 0.5f;
    264 	footShift = 0.0f;
    265 	waistShift = 0.0f;
    266 	minWaistFloorDist = 0.0f;
    267 	minWaistAnkleDist = 0.0f;
    268 	footUpTrace = 32.0f;
    269 	footDownTrace = 32.0f;
    270 	tiltWaist = false;
    271 	usePivot = false;
    272 
    273 	pivotFoot = -1;
    274 	pivotYaw = 0.0f;
    275 	pivotPos.Zero();
    276 
    277 	oldHeightsValid = false;
    278 	oldWaistHeight = 0.0f;
    279 	waistOffset.Zero();
    280 }
    281 
    282 /*
    283 ================
    284 idIK_Walk::~idIK_Walk
    285 ================
    286 */
    287 idIK_Walk::~idIK_Walk() {
    288 	if ( footModel ) {
    289 		delete footModel;
    290 	}
    291 }
    292 
    293 /*
    294 ================
    295 idIK_Walk::Save
    296 ================
    297 */
    298 void idIK_Walk::Save( idSaveGame *savefile ) const {
    299 	int i;
    300 	
    301 	idIK::Save( savefile );
    302 
    303 	savefile->WriteClipModel( footModel );
    304 
    305 	savefile->WriteInt( numLegs );
    306 	savefile->WriteInt( enabledLegs );
    307 	for ( i = 0; i < MAX_LEGS; i++ ) 
    308 		savefile->WriteInt( footJoints[i] );
    309 	for ( i = 0; i < MAX_LEGS; i++ ) 
    310 		savefile->WriteInt( ankleJoints[i] );
    311 	for ( i = 0; i < MAX_LEGS; i++ ) 
    312 		savefile->WriteInt( kneeJoints[i] );
    313 	for ( i = 0; i < MAX_LEGS; i++ ) 
    314 		savefile->WriteInt( hipJoints[i] );
    315 	for ( i = 0; i < MAX_LEGS; i++ ) 
    316 		savefile->WriteInt( dirJoints[i] );
    317 	savefile->WriteInt( waistJoint );
    318 	
    319 	for ( i = 0; i < MAX_LEGS; i++ )
    320 		savefile->WriteVec3( hipForward[i] );
    321 	for ( i = 0; i < MAX_LEGS; i++ )
    322 		savefile->WriteVec3( kneeForward[i] );
    323 	
    324 	for ( i = 0; i < MAX_LEGS; i++ )
    325 		savefile->WriteFloat( upperLegLength[i] );
    326 	for ( i = 0; i < MAX_LEGS; i++ )
    327 		savefile->WriteFloat( lowerLegLength[i] );
    328 	
    329 	for ( i = 0; i < MAX_LEGS; i++ )
    330 		savefile->WriteMat3( upperLegToHipJoint[i] );
    331 	for ( i = 0; i < MAX_LEGS; i++ )
    332 		savefile->WriteMat3( lowerLegToKneeJoint[i] );
    333 	
    334 	savefile->WriteFloat( smoothing );
    335 	savefile->WriteFloat( waistSmoothing );
    336 	savefile->WriteFloat( footShift );
    337 	savefile->WriteFloat( waistShift );
    338 	savefile->WriteFloat( minWaistFloorDist );
    339 	savefile->WriteFloat( minWaistAnkleDist );
    340 	savefile->WriteFloat( footUpTrace );
    341 	savefile->WriteFloat( footDownTrace );
    342 	savefile->WriteBool( tiltWaist );
    343 	savefile->WriteBool( usePivot );
    344 
    345 	savefile->WriteInt( pivotFoot );
    346 	savefile->WriteFloat( pivotYaw );
    347 	savefile->WriteVec3( pivotPos );
    348 	savefile->WriteBool( oldHeightsValid );
    349 	savefile->WriteFloat( oldWaistHeight );
    350 	for ( i = 0; i < MAX_LEGS; i++ ) {
    351 		savefile->WriteFloat( oldAnkleHeights[i] );
    352 	}
    353 	savefile->WriteVec3( waistOffset );
    354 }
    355 
    356 /*
    357 ================
    358 idIK_Walk::Restore
    359 ================
    360 */
    361 void idIK_Walk::Restore( idRestoreGame *savefile ) {
    362 	int i;
    363 	
    364 	idIK::Restore( savefile );
    365 
    366 	savefile->ReadClipModel( footModel );
    367 
    368 	savefile->ReadInt( numLegs );
    369 	savefile->ReadInt( enabledLegs );
    370 	for ( i = 0; i < MAX_LEGS; i++ )
    371 		savefile->ReadInt( (int&)footJoints[i] );
    372 	for ( i = 0; i < MAX_LEGS; i++ )
    373 		savefile->ReadInt( (int&)ankleJoints[i] );
    374 	for ( i = 0; i < MAX_LEGS; i++ )
    375 		savefile->ReadInt( (int&)kneeJoints[i] );
    376 	for ( i = 0; i < MAX_LEGS; i++ )
    377 		savefile->ReadInt( (int&)hipJoints[i] );
    378 	for ( i = 0; i < MAX_LEGS; i++ )
    379 		savefile->ReadInt( (int&)dirJoints[i] );
    380 	savefile->ReadInt( (int&)waistJoint );
    381 	
    382 	for ( i = 0; i < MAX_LEGS; i++ )
    383 		savefile->ReadVec3( hipForward[i] );
    384 	for ( i = 0; i < MAX_LEGS; i++ )
    385 		savefile->ReadVec3( kneeForward[i] );
    386 	
    387 	for ( i = 0; i < MAX_LEGS; i++ )
    388 		savefile->ReadFloat( upperLegLength[i] );
    389 	for ( i = 0; i < MAX_LEGS; i++ )
    390 		savefile->ReadFloat( lowerLegLength[i] );
    391 	
    392 	for ( i = 0; i < MAX_LEGS; i++ )
    393 		savefile->ReadMat3( upperLegToHipJoint[i] );
    394 	for ( i = 0; i < MAX_LEGS; i++ )
    395 		savefile->ReadMat3( lowerLegToKneeJoint[i] );
    396 	
    397 	savefile->ReadFloat( smoothing );
    398 	savefile->ReadFloat( waistSmoothing );
    399 	savefile->ReadFloat( footShift );
    400 	savefile->ReadFloat( waistShift );
    401 	savefile->ReadFloat( minWaistFloorDist );
    402 	savefile->ReadFloat( minWaistAnkleDist );
    403 	savefile->ReadFloat( footUpTrace );
    404 	savefile->ReadFloat( footDownTrace );
    405 	savefile->ReadBool( tiltWaist );
    406 	savefile->ReadBool( usePivot );
    407 
    408 	savefile->ReadInt( pivotFoot );
    409 	savefile->ReadFloat( pivotYaw );
    410 	savefile->ReadVec3( pivotPos );
    411 	savefile->ReadBool( oldHeightsValid );
    412 	savefile->ReadFloat( oldWaistHeight );
    413 	for ( i = 0; i < MAX_LEGS; i++ ) {
    414 		savefile->ReadFloat( oldAnkleHeights[i] );
    415 	}
    416 	savefile->ReadVec3( waistOffset );
    417 }
    418 
    419 /*
    420 ================
    421 idIK_Walk::Init
    422 ================
    423 */
    424 bool idIK_Walk::Init( idEntity *self, const char *anim, const idVec3 &modelOffset ) {
    425 	int i;
    426 	float footSize;
    427 	idVec3 verts[4];
    428 	idTraceModel trm;
    429 	const char *jointName;
    430 	idVec3 dir, ankleOrigin, kneeOrigin, hipOrigin, dirOrigin;
    431 	idMat3 axis, ankleAxis, kneeAxis, hipAxis;
    432 
    433 	static idVec3 footWinding[4] = {
    434 		idVec3(  1.0f,  1.0f, 0.0f ),
    435 		idVec3( -1.0f,  1.0f, 0.0f ),
    436 		idVec3( -1.0f, -1.0f, 0.0f ),
    437 		idVec3(  1.0f, -1.0f, 0.0f )
    438 	};
    439 
    440 	if ( !self ) {
    441 		return false;
    442 	}
    443 
    444 	numLegs = Min( self->spawnArgs.GetInt( "ik_numLegs", "0" ), MAX_LEGS );
    445 	if ( numLegs == 0 ) {
    446 		return true;
    447 	}
    448 
    449 	if ( !idIK::Init( self, anim, modelOffset ) ) {
    450 		return false;
    451 	}
    452 
    453 	int numJoints = animator->NumJoints();
    454 	idJointMat *joints = ( idJointMat * )_alloca16( numJoints * sizeof( joints[0] ) );
    455 
    456 	// create the animation frame used to setup the IK
    457 	gameEdit->ANIM_CreateAnimFrame( animator->ModelHandle(), animator->GetAnim( modifiedAnim )->MD5Anim( 0 ), numJoints, joints, 1, animator->ModelDef()->GetVisualOffset() + modelOffset, animator->RemoveOrigin() );
    458 
    459 	enabledLegs = 0;
    460 
    461 	// get all the joints
    462 	for ( i = 0; i < numLegs; i++ ) {
    463 
    464 		jointName = self->spawnArgs.GetString( va( "ik_foot%d", i+1 ) );
    465 		footJoints[i] = animator->GetJointHandle( jointName );
    466 		if ( footJoints[i] == INVALID_JOINT ) {
    467 			gameLocal.Error( "idIK_Walk::Init: invalid foot joint '%s'", jointName );
    468 		}
    469 
    470 		jointName = self->spawnArgs.GetString( va( "ik_ankle%d", i+1 ) );
    471 		ankleJoints[i] = animator->GetJointHandle( jointName );
    472 		if ( ankleJoints[i] == INVALID_JOINT ) {
    473 			gameLocal.Error( "idIK_Walk::Init: invalid ankle joint '%s'", jointName );
    474 		}
    475 
    476 		jointName = self->spawnArgs.GetString( va( "ik_knee%d", i+1 ) );
    477 		kneeJoints[i] = animator->GetJointHandle( jointName );
    478 		if ( kneeJoints[i] == INVALID_JOINT ) {
    479 			gameLocal.Error( "idIK_Walk::Init: invalid knee joint '%s'\n", jointName );
    480 		}
    481 
    482 		jointName = self->spawnArgs.GetString( va( "ik_hip%d", i+1 ) );
    483 		hipJoints[i] = animator->GetJointHandle( jointName );
    484 		if ( hipJoints[i] == INVALID_JOINT ) {
    485 			gameLocal.Error( "idIK_Walk::Init: invalid hip joint '%s'\n", jointName );
    486 		}
    487 
    488 		jointName = self->spawnArgs.GetString( va( "ik_dir%d", i+1 ) );
    489 		dirJoints[i] = animator->GetJointHandle( jointName );
    490 
    491 		enabledLegs |= 1 << i;
    492 	}
    493 
    494 	jointName = self->spawnArgs.GetString( "ik_waist" );
    495 	waistJoint = animator->GetJointHandle( jointName );
    496 	if ( waistJoint == INVALID_JOINT ) {
    497 		gameLocal.Error( "idIK_Walk::Init: invalid waist joint '%s'\n", jointName );
    498 	}
    499 
    500 	// get the leg bone lengths and rotation matrices
    501 	for ( i = 0; i < numLegs; i++ ) {
    502 		oldAnkleHeights[i] = 0.0f;
    503 
    504 		ankleAxis = joints[ ankleJoints[ i ] ].ToMat3();
    505 		ankleOrigin = joints[ ankleJoints[ i ] ].ToVec3();
    506 
    507 		kneeAxis = joints[ kneeJoints[ i ] ].ToMat3();
    508 		kneeOrigin = joints[ kneeJoints[ i ] ].ToVec3();
    509 
    510 		hipAxis = joints[ hipJoints[ i ] ].ToMat3();
    511 		hipOrigin = joints[ hipJoints[ i ] ].ToVec3();
    512 
    513 		// get the IK direction
    514 		if ( dirJoints[i] != INVALID_JOINT ) {
    515 			dirOrigin = joints[ dirJoints[ i ] ].ToVec3();
    516 			dir = dirOrigin - kneeOrigin;
    517 		} else {
    518 			dir.Set( 1.0f, 0.0f, 0.0f );
    519 		}
    520 
    521 		hipForward[i] = dir * hipAxis.Transpose();
    522 		kneeForward[i] = dir * kneeAxis.Transpose();
    523 
    524 		// conversion from upper leg bone axis to hip joint axis
    525 		upperLegLength[i] = GetBoneAxis( hipOrigin, kneeOrigin, dir, axis );
    526 		upperLegToHipJoint[i] = hipAxis * axis.Transpose();
    527 
    528 		// conversion from lower leg bone axis to knee joint axis
    529 		lowerLegLength[i] = GetBoneAxis( kneeOrigin, ankleOrigin, dir, axis );
    530 		lowerLegToKneeJoint[i] = kneeAxis * axis.Transpose();
    531 	}
    532 
    533 	smoothing = self->spawnArgs.GetFloat( "ik_smoothing", "0.75" );
    534 	waistSmoothing = self->spawnArgs.GetFloat( "ik_waistSmoothing", "0.75" );
    535 	footShift = self->spawnArgs.GetFloat( "ik_footShift", "0" );
    536 	waistShift = self->spawnArgs.GetFloat( "ik_waistShift", "0" );
    537 	minWaistFloorDist = self->spawnArgs.GetFloat( "ik_minWaistFloorDist", "0" );
    538 	minWaistAnkleDist = self->spawnArgs.GetFloat( "ik_minWaistAnkleDist", "0" );
    539 	footUpTrace = self->spawnArgs.GetFloat( "ik_footUpTrace", "32" );
    540 	footDownTrace = self->spawnArgs.GetFloat( "ik_footDownTrace", "32" );
    541 	tiltWaist = self->spawnArgs.GetBool( "ik_tiltWaist", "0" );
    542 	usePivot = self->spawnArgs.GetBool( "ik_usePivot", "0" );
    543 
    544 	// setup a clip model for the feet
    545 	footSize = self->spawnArgs.GetFloat( "ik_footSize", "4" ) * 0.5f;
    546 	if ( footSize > 0.0f ) {
    547 		for ( i = 0; i < 4; i++ ) {
    548 			verts[i] = footWinding[i] * footSize;
    549 		}
    550 		trm.SetupPolygon( verts, 4 );
    551 		footModel = new (TAG_PHYSICS_CLIP) idClipModel( trm );
    552 	}
    553 
    554 	initialized = true;
    555 
    556 	return true;
    557 }
    558 
    559 /*
    560 ================
    561 idIK_Walk::Evaluate
    562 ================
    563 */
    564 void idIK_Walk::Evaluate() {
    565 	int i, newPivotFoot = -1;
    566 	float modelHeight, jointHeight, lowestHeight, floorHeights[MAX_LEGS];
    567 	float shift, smallestShift, newHeight, step, newPivotYaw, height, largestAnkleHeight;
    568 	idVec3 modelOrigin, normal, hipDir, kneeDir, start, end, jointOrigins[MAX_LEGS];
    569 	idVec3 footOrigin, ankleOrigin, kneeOrigin, hipOrigin, waistOrigin;
    570 	idMat3 modelAxis, waistAxis, axis;
    571 	idMat3 hipAxis[MAX_LEGS], kneeAxis[MAX_LEGS], ankleAxis[MAX_LEGS];
    572 	trace_t results;
    573 
    574 	if ( !self || !gameLocal.isNewFrame ) {
    575 		return;
    576 	}
    577 
    578 	// if no IK enabled on any legs
    579 	if ( !enabledLegs ) {
    580 		return;
    581 	}
    582 
    583 	normal = - self->GetPhysics()->GetGravityNormal();
    584 	modelOrigin = self->GetPhysics()->GetOrigin();
    585 	modelAxis = self->GetRenderEntity()->axis;
    586 	modelHeight = modelOrigin * normal;
    587 
    588 	modelOrigin += modelOffset * modelAxis;
    589 
    590 	// create frame without joint mods
    591 	animator->CreateFrame( gameLocal.time, false );
    592 
    593 	// get the joint positions for the feet
    594 	lowestHeight = idMath::INFINITY;
    595 	for ( i = 0; i < numLegs; i++ ) {
    596 		animator->GetJointTransform( footJoints[i], gameLocal.time, footOrigin, axis );
    597 		jointOrigins[i] = modelOrigin + footOrigin * modelAxis;
    598 		jointHeight = jointOrigins[i] * normal;
    599 		if ( jointHeight < lowestHeight ) {
    600 			lowestHeight = jointHeight;
    601 			newPivotFoot = i;
    602 		}
    603 	}
    604 
    605 	if ( usePivot ) {
    606 
    607 		newPivotYaw = modelAxis[0].ToYaw();
    608 
    609 		// change pivot foot
    610 		if ( newPivotFoot != pivotFoot || idMath::Fabs( idMath::AngleNormalize180( newPivotYaw - pivotYaw ) ) > 30.0f ) {
    611 			pivotFoot = newPivotFoot;
    612 			pivotYaw = newPivotYaw;
    613 			animator->GetJointTransform( footJoints[pivotFoot], gameLocal.time, footOrigin, axis );
    614 			pivotPos = modelOrigin + footOrigin * modelAxis;
    615 		}
    616 
    617 		// keep pivot foot in place
    618 		jointOrigins[pivotFoot] = pivotPos;
    619 	}
    620 
    621 	// get the floor heights for the feet
    622 	for ( i = 0; i < numLegs; i++ ) {
    623 
    624 		if ( !( enabledLegs & ( 1 << i ) ) ) {
    625 			continue;
    626 		}
    627 
    628 		start = jointOrigins[i] + normal * footUpTrace;
    629 		end = jointOrigins[i] - normal * footDownTrace;
    630 		gameLocal.clip.Translation( results, start, end, footModel, mat3_identity, CONTENTS_SOLID|CONTENTS_IKCLIP, self );
    631 		floorHeights[i] = results.endpos * normal;
    632 
    633 		if ( ik_debug.GetBool() && footModel ) {
    634 			idFixedWinding w;
    635 			for ( int j = 0; j < footModel->GetTraceModel()->numVerts; j++ ) {
    636 				w += footModel->GetTraceModel()->verts[j];
    637 			}
    638 			gameRenderWorld->DebugWinding( colorRed, w, results.endpos, results.endAxis );
    639 		}
    640 	}
    641 
    642 	const idPhysics *phys = self->GetPhysics();
    643 
    644 	// test whether or not the character standing on the ground
    645 	bool onGround = phys->HasGroundContacts();
    646 
    647 	// test whether or not the character is standing on a plat
    648 	bool onPlat = false;
    649 	for ( i = 0; i < phys->GetNumContacts(); i++ ) {
    650 		idEntity *ent = gameLocal.entities[ phys->GetContact( i ).entityNum ];
    651 		if ( ent != NULL && ent->IsType( idPlat::Type ) ) {
    652 			onPlat = true;
    653 			break;
    654 		}
    655 	}
    656 
    657 	// adjust heights of the ankles
    658 	smallestShift = idMath::INFINITY;
    659 	largestAnkleHeight = -idMath::INFINITY;
    660 	for ( i = 0; i < numLegs; i++ ) {
    661 
    662 		if ( onGround && ( enabledLegs & ( 1 << i ) ) ) {
    663 			shift = floorHeights[i] - modelHeight + footShift;
    664 		} else {
    665 			shift = 0.0f;
    666 		}
    667 
    668 		if ( shift < smallestShift ) {
    669 			smallestShift = shift;
    670 		}
    671 
    672 		animator->GetJointTransform( ankleJoints[i], gameLocal.time, ankleOrigin, ankleAxis[i] );
    673 		jointOrigins[i] = modelOrigin + ankleOrigin * modelAxis;
    674 
    675 		height = jointOrigins[i] * normal;
    676 
    677 		if ( oldHeightsValid && !onPlat ) {
    678 			step = height + shift - oldAnkleHeights[i];
    679 			shift -= smoothing * step;
    680 		}
    681 
    682 		newHeight = height + shift;
    683 		if ( newHeight > largestAnkleHeight ) {
    684 			largestAnkleHeight = newHeight;
    685 		}
    686 
    687 		oldAnkleHeights[i] = newHeight;
    688 
    689 		jointOrigins[i] += shift * normal;
    690 	}
    691 
    692 	animator->GetJointTransform( waistJoint, gameLocal.time, waistOrigin, waistAxis );
    693 	waistOrigin = modelOrigin + waistOrigin * modelAxis;
    694 
    695 	// adjust position of the waist
    696 	waistOffset = ( smallestShift + waistShift ) * normal;
    697 
    698 	// if the waist should be at least a certain distance above the floor
    699 	if ( minWaistFloorDist > 0.0f && waistOffset * normal < 0.0f ) {
    700 		start = waistOrigin;
    701 		end = waistOrigin + waistOffset - normal * minWaistFloorDist;
    702 		gameLocal.clip.Translation( results, start, end, footModel, modelAxis, CONTENTS_SOLID|CONTENTS_IKCLIP, self );
    703 		height = ( waistOrigin + waistOffset - results.endpos ) * normal;
    704 		if ( height < minWaistFloorDist ) {
    705 			waistOffset += ( minWaistFloorDist - height ) * normal;
    706 		}
    707 	}
    708 
    709 	// if the waist should be at least a certain distance above the ankles
    710 	if ( minWaistAnkleDist > 0.0f ) {
    711 		height = ( waistOrigin + waistOffset ) * normal;
    712 		if ( height - largestAnkleHeight < minWaistAnkleDist ) {
    713 			waistOffset += ( minWaistAnkleDist - ( height - largestAnkleHeight ) ) * normal;
    714 		}
    715 	}
    716 
    717 	if ( oldHeightsValid ) {
    718 		// smoothly adjust height of waist
    719 		newHeight = ( waistOrigin + waistOffset ) * normal;
    720 		step = newHeight - oldWaistHeight;
    721 		waistOffset -= waistSmoothing * step * normal;
    722 	}
    723 
    724 	// save height of waist for smoothing
    725 	oldWaistHeight = ( waistOrigin + waistOffset ) * normal;
    726 
    727 	if ( !oldHeightsValid ) {
    728 		oldHeightsValid = true;
    729 		return;
    730 	}
    731 
    732 	// solve IK
    733 	for ( i = 0; i < numLegs; i++ ) {
    734 
    735 		// get the position of the hip in world space
    736 		animator->GetJointTransform( hipJoints[i], gameLocal.time, hipOrigin, axis );
    737 		hipOrigin = modelOrigin + waistOffset + hipOrigin * modelAxis;
    738 		hipDir = hipForward[i] * axis * modelAxis;
    739 
    740 		// get the IK bend direction
    741 		animator->GetJointTransform( kneeJoints[i], gameLocal.time, kneeOrigin, axis );
    742 		kneeDir = kneeForward[i] * axis * modelAxis;
    743 
    744 		// solve IK and calculate knee position
    745 		SolveTwoBones( hipOrigin, jointOrigins[i], kneeDir, upperLegLength[i], lowerLegLength[i], kneeOrigin );
    746 
    747 		if ( ik_debug.GetBool() ) {
    748 			gameRenderWorld->DebugLine( colorCyan, hipOrigin, kneeOrigin );
    749 			gameRenderWorld->DebugLine( colorRed, kneeOrigin, jointOrigins[i] );
    750 			gameRenderWorld->DebugLine( colorYellow, kneeOrigin, kneeOrigin + hipDir );
    751 			gameRenderWorld->DebugLine( colorGreen, kneeOrigin, kneeOrigin + kneeDir );
    752 		}
    753 
    754 		// get the axis for the hip joint
    755 		GetBoneAxis( hipOrigin, kneeOrigin, hipDir, axis );
    756 		hipAxis[i] = upperLegToHipJoint[i] * ( axis * modelAxis.Transpose() );
    757 
    758 		// get the axis for the knee joint
    759 		GetBoneAxis( kneeOrigin, jointOrigins[i], kneeDir, axis );
    760 		kneeAxis[i] = lowerLegToKneeJoint[i] * ( axis * modelAxis.Transpose() );
    761 	}
    762 
    763 	// set the joint mods
    764 	animator->SetJointAxis( waistJoint, JOINTMOD_WORLD_OVERRIDE, waistAxis );
    765 	animator->SetJointPos( waistJoint, JOINTMOD_WORLD_OVERRIDE, ( waistOrigin + waistOffset - modelOrigin ) * modelAxis.Transpose() );
    766 	for ( i = 0; i < numLegs; i++ ) {
    767 		animator->SetJointAxis( hipJoints[i], JOINTMOD_WORLD_OVERRIDE, hipAxis[i] );
    768 		animator->SetJointAxis( kneeJoints[i], JOINTMOD_WORLD_OVERRIDE, kneeAxis[i] );
    769 		animator->SetJointAxis( ankleJoints[i], JOINTMOD_WORLD_OVERRIDE, ankleAxis[i] );
    770 	}
    771 
    772 	ik_activate = true;
    773 }
    774 
    775 /*
    776 ================
    777 idIK_Walk::ClearJointMods
    778 ================
    779 */
    780 void idIK_Walk::ClearJointMods() {
    781 	int i;
    782 
    783 	if ( !self || !ik_activate ) {
    784 		return;
    785 	}
    786 
    787 	animator->SetJointAxis( waistJoint, JOINTMOD_NONE, mat3_identity );
    788 	animator->SetJointPos( waistJoint, JOINTMOD_NONE, vec3_origin );
    789 	for ( i = 0; i < numLegs; i++ ) {
    790 		animator->SetJointAxis( hipJoints[i], JOINTMOD_NONE, mat3_identity );
    791 		animator->SetJointAxis( kneeJoints[i], JOINTMOD_NONE, mat3_identity );
    792 		animator->SetJointAxis( ankleJoints[i], JOINTMOD_NONE, mat3_identity );
    793 	}
    794 
    795 	ik_activate = false;
    796 }
    797 
    798 /*
    799 ================
    800 idIK_Walk::EnableAll
    801 ================
    802 */
    803 void idIK_Walk::EnableAll() {
    804 	enabledLegs = ( 1 << numLegs ) - 1;
    805 	oldHeightsValid = false;
    806 }
    807 
    808 /*
    809 ================
    810 idIK_Walk::DisableAll
    811 ================
    812 */
    813 void idIK_Walk::DisableAll() {
    814 	enabledLegs = 0;
    815 	oldHeightsValid = false;
    816 }
    817 
    818 /*
    819 ================
    820 idIK_Walk::EnableLeg
    821 ================
    822 */
    823 void idIK_Walk::EnableLeg( int num ) {
    824 	enabledLegs |= 1 << num;
    825 }
    826 
    827 /*
    828 ================
    829 idIK_Walk::DisableLeg
    830 ================
    831 */
    832 void idIK_Walk::DisableLeg( int num ) {
    833 	enabledLegs &= ~( 1 << num );
    834 }
    835 
    836 
    837 /*
    838 ===============================================================================
    839 
    840   idIK_Reach
    841 
    842 ===============================================================================
    843 */
    844 
    845 /*
    846 ================
    847 idIK_Reach::idIK_Reach
    848 ================
    849 */
    850 idIK_Reach::idIK_Reach() {
    851 	int i;
    852 
    853 	initialized = false;
    854 	numArms = 0;
    855 	enabledArms = 0;
    856 	for ( i = 0; i < MAX_ARMS; i++ ) {
    857 		handJoints[i] = INVALID_JOINT;
    858 		elbowJoints[i] = INVALID_JOINT;
    859 		shoulderJoints[i] = INVALID_JOINT;
    860 		dirJoints[i] = INVALID_JOINT;
    861 		shoulderForward[i].Zero();
    862 		elbowForward[i].Zero();
    863 		upperArmLength[i] = 0.0f;
    864 		lowerArmLength[i] = 0.0f;
    865 		upperArmToShoulderJoint[i].Identity();
    866 		lowerArmToElbowJoint[i].Identity();
    867 	}
    868 }
    869 
    870 /*
    871 ================
    872 idIK_Reach::~idIK_Reach
    873 ================
    874 */
    875 idIK_Reach::~idIK_Reach() {
    876 }
    877 
    878 /*
    879 ================
    880 idIK_Reach::Save
    881 ================
    882 */
    883 void idIK_Reach::Save( idSaveGame *savefile ) const {
    884 	int i;
    885 	idIK::Save( savefile );
    886 
    887 	savefile->WriteInt( numArms );
    888 	savefile->WriteInt( enabledArms );
    889 	for ( i = 0; i <  MAX_ARMS; i++ )
    890 		savefile->WriteInt( handJoints[i] );
    891 	for ( i = 0; i <  MAX_ARMS; i++ )
    892 		savefile->WriteInt( elbowJoints[i] );
    893 	for ( i = 0; i <  MAX_ARMS; i++ )
    894 		savefile->WriteInt( shoulderJoints[i] );
    895 	for ( i = 0; i <  MAX_ARMS; i++ )
    896 		savefile->WriteInt( dirJoints[i] );
    897 		
    898 	for ( i = 0; i <  MAX_ARMS; i++ )
    899 		savefile->WriteVec3( shoulderForward[i] );
    900 	for ( i = 0; i <  MAX_ARMS; i++ )
    901 		savefile->WriteVec3( elbowForward[i] );
    902 		
    903 	for ( i = 0; i <  MAX_ARMS; i++ )
    904 		savefile->WriteFloat( upperArmLength[i] );
    905 	for ( i = 0; i <  MAX_ARMS; i++ )
    906 		savefile->WriteFloat( lowerArmLength[i] );
    907 		
    908 	for ( i = 0; i <  MAX_ARMS; i++ )
    909 		savefile->WriteMat3( upperArmToShoulderJoint[i] );
    910 	for ( i = 0; i <  MAX_ARMS; i++ )
    911 		savefile->WriteMat3( lowerArmToElbowJoint[i] );
    912 }
    913 
    914 /*
    915 ================
    916 idIK_Reach::Restore
    917 ================
    918 */
    919 void idIK_Reach::Restore( idRestoreGame *savefile ) {
    920 	int i;
    921 	idIK::Restore( savefile );
    922 
    923 	savefile->ReadInt( numArms );
    924 	savefile->ReadInt( enabledArms );
    925 	for ( i = 0; i <  MAX_ARMS; i++ )
    926 		savefile->ReadInt( (int&)handJoints[i] );
    927 	for ( i = 0; i <  MAX_ARMS; i++ )
    928 		savefile->ReadInt( (int&)elbowJoints[i] );
    929 	for ( i = 0; i <  MAX_ARMS; i++ )
    930 		savefile->ReadInt( (int&)shoulderJoints[i] );
    931 	for ( i = 0; i <  MAX_ARMS; i++ )
    932 		savefile->ReadInt( (int&)dirJoints[i] );
    933 		
    934 	for ( i = 0; i <  MAX_ARMS; i++ )
    935 		savefile->ReadVec3( shoulderForward[i] );
    936 	for ( i = 0; i <  MAX_ARMS; i++ )
    937 		savefile->ReadVec3( elbowForward[i] );
    938 		
    939 	for ( i = 0; i <  MAX_ARMS; i++ )
    940 		savefile->ReadFloat( upperArmLength[i] );
    941 	for ( i = 0; i <  MAX_ARMS; i++ )
    942 		savefile->ReadFloat( lowerArmLength[i] );
    943 		
    944 	for ( i = 0; i <  MAX_ARMS; i++ )
    945 		savefile->ReadMat3( upperArmToShoulderJoint[i] );
    946 	for ( i = 0; i <  MAX_ARMS; i++ )
    947 		savefile->ReadMat3( lowerArmToElbowJoint[i] );
    948 }
    949 
    950 /*
    951 ================
    952 idIK_Reach::Init
    953 ================
    954 */
    955 bool idIK_Reach::Init( idEntity *self, const char *anim, const idVec3 &modelOffset ) {
    956 	int i;
    957 	const char *jointName;
    958 	idTraceModel trm;
    959 	idVec3 dir, handOrigin, elbowOrigin, shoulderOrigin, dirOrigin;
    960 	idMat3 axis, handAxis, elbowAxis, shoulderAxis;
    961 
    962 	if ( !self ) {
    963 		return false;
    964 	}
    965 
    966 	numArms = Min( self->spawnArgs.GetInt( "ik_numArms", "0" ), MAX_ARMS );
    967 	if ( numArms == 0 ) {
    968 		return true;
    969 	}
    970 
    971 	if ( !idIK::Init( self, anim, modelOffset ) ) {
    972 		return false;
    973 	}
    974 
    975 	int numJoints = animator->NumJoints();
    976 	idJointMat *joints = ( idJointMat * )_alloca16( numJoints * sizeof( joints[0] ) );
    977 
    978 	// create the animation frame used to setup the IK
    979 	gameEdit->ANIM_CreateAnimFrame( animator->ModelHandle(), animator->GetAnim( modifiedAnim )->MD5Anim( 0 ), numJoints, joints, 1, animator->ModelDef()->GetVisualOffset() + modelOffset, animator->RemoveOrigin() );
    980 
    981 	enabledArms = 0;
    982 
    983 	// get all the joints
    984 	for ( i = 0; i < numArms; i++ ) {
    985 
    986 		jointName = self->spawnArgs.GetString( va( "ik_hand%d", i+1 ) );
    987 		handJoints[i] = animator->GetJointHandle( jointName );
    988 		if ( handJoints[i] == INVALID_JOINT ) {
    989 			gameLocal.Error( "idIK_Reach::Init: invalid hand joint '%s'", jointName );
    990 		}
    991 
    992 		jointName = self->spawnArgs.GetString( va( "ik_elbow%d", i+1 ) );
    993 		elbowJoints[i] = animator->GetJointHandle( jointName );
    994 		if ( elbowJoints[i] == INVALID_JOINT ) {
    995 			gameLocal.Error( "idIK_Reach::Init: invalid elbow joint '%s'\n", jointName );
    996 		}
    997 
    998 		jointName = self->spawnArgs.GetString( va( "ik_shoulder%d", i+1 ) );
    999 		shoulderJoints[i] = animator->GetJointHandle( jointName );
   1000 		if ( shoulderJoints[i] == INVALID_JOINT ) {
   1001 			gameLocal.Error( "idIK_Reach::Init: invalid shoulder joint '%s'\n", jointName );
   1002 		}
   1003 
   1004 		jointName = self->spawnArgs.GetString( va( "ik_elbowDir%d", i+1 ) );
   1005 		dirJoints[i] = animator->GetJointHandle( jointName );
   1006 
   1007 		enabledArms |= 1 << i;
   1008 	}
   1009 
   1010 	// get the arm bone lengths and rotation matrices
   1011 	for ( i = 0; i < numArms; i++ ) {
   1012 
   1013 		handAxis = joints[ handJoints[ i ] ].ToMat3();
   1014 		handOrigin = joints[ handJoints[ i ] ].ToVec3();
   1015 
   1016 		elbowAxis = joints[ elbowJoints[ i ] ].ToMat3();
   1017 		elbowOrigin = joints[ elbowJoints[ i ] ].ToVec3();
   1018 
   1019 		shoulderAxis = joints[ shoulderJoints[ i ] ].ToMat3();
   1020 		shoulderOrigin = joints[ shoulderJoints[ i ] ].ToVec3();
   1021 
   1022 		// get the IK direction
   1023 		if ( dirJoints[i] != INVALID_JOINT ) {
   1024 			dirOrigin = joints[ dirJoints[ i ] ].ToVec3();
   1025 			dir = dirOrigin - elbowOrigin;
   1026 		} else {
   1027 			dir.Set( -1.0f, 0.0f, 0.0f );
   1028 		}
   1029 
   1030 		shoulderForward[i] = dir * shoulderAxis.Transpose();
   1031 		elbowForward[i] = dir * elbowAxis.Transpose();
   1032 
   1033 		// conversion from upper arm bone axis to should joint axis
   1034 		upperArmLength[i] = GetBoneAxis( shoulderOrigin, elbowOrigin, dir, axis );
   1035 		upperArmToShoulderJoint[i] = shoulderAxis * axis.Transpose();
   1036 
   1037 		// conversion from lower arm bone axis to elbow joint axis
   1038 		lowerArmLength[i] = GetBoneAxis( elbowOrigin, handOrigin, dir, axis );
   1039 		lowerArmToElbowJoint[i] = elbowAxis * axis.Transpose();
   1040 	}
   1041 
   1042 	initialized = true;
   1043 
   1044 	return true;
   1045 }
   1046 
   1047 /*
   1048 ================
   1049 idIK_Reach::Evaluate
   1050 ================
   1051 */
   1052 void idIK_Reach::Evaluate() {
   1053 	int i;
   1054 	idVec3 modelOrigin, shoulderOrigin, elbowOrigin, handOrigin, shoulderDir, elbowDir;
   1055 	idMat3 modelAxis, axis;
   1056 	idMat3 shoulderAxis[MAX_ARMS], elbowAxis[MAX_ARMS];
   1057 	trace_t trace;
   1058 
   1059 	modelOrigin = self->GetRenderEntity()->origin;
   1060 	modelAxis = self->GetRenderEntity()->axis;
   1061 
   1062 	// solve IK
   1063 	for ( i = 0; i < numArms; i++ ) {
   1064 
   1065 		// get the position of the shoulder in world space
   1066 		animator->GetJointTransform( shoulderJoints[i], gameLocal.time, shoulderOrigin, axis );
   1067 		shoulderOrigin = modelOrigin + shoulderOrigin * modelAxis;
   1068 		shoulderDir = shoulderForward[i] * axis * modelAxis;
   1069 
   1070 		// get the position of the hand in world space
   1071 		animator->GetJointTransform( handJoints[i], gameLocal.time, handOrigin, axis );
   1072 		handOrigin = modelOrigin + handOrigin * modelAxis;
   1073 
   1074 		// get first collision going from shoulder to hand
   1075 		gameLocal.clip.TracePoint( trace, shoulderOrigin, handOrigin, CONTENTS_SOLID, self );
   1076 		handOrigin = trace.endpos;
   1077 
   1078 		// get the IK bend direction
   1079 		animator->GetJointTransform( elbowJoints[i], gameLocal.time, elbowOrigin, axis );
   1080 		elbowDir = elbowForward[i] * axis * modelAxis;
   1081 
   1082 		// solve IK and calculate elbow position
   1083 		SolveTwoBones( shoulderOrigin, handOrigin, elbowDir, upperArmLength[i], lowerArmLength[i], elbowOrigin );
   1084 
   1085 		if ( ik_debug.GetBool() ) {
   1086 			gameRenderWorld->DebugLine( colorCyan, shoulderOrigin, elbowOrigin );
   1087 			gameRenderWorld->DebugLine( colorRed, elbowOrigin, handOrigin );
   1088 			gameRenderWorld->DebugLine( colorYellow, elbowOrigin, elbowOrigin + elbowDir );
   1089 			gameRenderWorld->DebugLine( colorGreen, elbowOrigin, elbowOrigin + shoulderDir );
   1090 		}
   1091 
   1092 		// get the axis for the shoulder joint
   1093 		GetBoneAxis( shoulderOrigin, elbowOrigin, shoulderDir, axis );
   1094 		shoulderAxis[i] = upperArmToShoulderJoint[i] * ( axis * modelAxis.Transpose() );
   1095 
   1096 		// get the axis for the elbow joint
   1097 		GetBoneAxis( elbowOrigin, handOrigin, elbowDir, axis );
   1098 		elbowAxis[i] = lowerArmToElbowJoint[i] * ( axis * modelAxis.Transpose() );
   1099 	}
   1100 
   1101 	for ( i = 0; i < numArms; i++ ) {
   1102 		animator->SetJointAxis( shoulderJoints[i], JOINTMOD_WORLD_OVERRIDE, shoulderAxis[i] );
   1103 		animator->SetJointAxis( elbowJoints[i], JOINTMOD_WORLD_OVERRIDE, elbowAxis[i] );
   1104 	}
   1105 
   1106 	ik_activate = true;
   1107 }
   1108 
   1109 /*
   1110 ================
   1111 idIK_Reach::ClearJointMods
   1112 ================
   1113 */
   1114 void idIK_Reach::ClearJointMods() {
   1115 	int i;
   1116 
   1117 	if ( !self || !ik_activate ) {
   1118 		return;
   1119 	}
   1120 
   1121 	for ( i = 0; i < numArms; i++ ) {
   1122 		animator->SetJointAxis( shoulderJoints[i], JOINTMOD_NONE, mat3_identity );
   1123 		animator->SetJointAxis( elbowJoints[i], JOINTMOD_NONE, mat3_identity );
   1124 		animator->SetJointAxis( handJoints[i], JOINTMOD_NONE, mat3_identity );
   1125 	}
   1126 
   1127 	ik_activate = false;
   1128 }