DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Anim_Blend.cpp (123962B)


      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 #pragma hdrstop
     30 #include "../../idlib/precompiled.h"
     31 
     32 
     33 #include "../Game_local.h"
     34 
     35 static const char *channelNames[ ANIM_NumAnimChannels ] = {
     36 	"all", "torso", "legs", "head", "eyelids"
     37 };
     38 
     39 /***********************************************************************
     40 
     41 	idAnim
     42 
     43 ***********************************************************************/
     44 
     45 /*
     46 =====================
     47 idAnim::idAnim
     48 =====================
     49 */
     50 idAnim::idAnim() {
     51 	modelDef = NULL;
     52 	numAnims = 0;
     53 	memset( anims, 0, sizeof( anims ) );
     54 	memset( &flags, 0, sizeof( flags ) );
     55 }
     56 
     57 /*
     58 =====================
     59 idAnim::idAnim
     60 =====================
     61 */
     62 idAnim::idAnim( const idDeclModelDef *modelDef, const idAnim *anim ) {
     63 	int i;
     64 
     65 	this->modelDef = modelDef;
     66 	numAnims = anim->numAnims;
     67 	name = anim->name;
     68 	realname = anim->realname;
     69 	flags = anim->flags;
     70 
     71 	memset( anims, 0, sizeof( anims ) );
     72 	for( i = 0; i < numAnims; i++ ) {
     73 		anims[ i ] = anim->anims[ i ];
     74 		anims[ i ]->IncreaseRefs();
     75 	}
     76 
     77 	frameLookup.SetNum( anim->frameLookup.Num() );
     78 	memcpy( frameLookup.Ptr(), anim->frameLookup.Ptr(), frameLookup.MemoryUsed() );
     79 
     80 	frameCommands.SetNum( anim->frameCommands.Num() );
     81 	for( i = 0; i < frameCommands.Num(); i++ ) {
     82 		frameCommands[ i ] = anim->frameCommands[ i ];
     83 		if ( anim->frameCommands[ i ].string ) {
     84 			frameCommands[ i ].string = new (TAG_ANIM) idStr( *anim->frameCommands[ i ].string );
     85 		}
     86 	}
     87 }
     88 
     89 /*
     90 =====================
     91 idAnim::~idAnim
     92 =====================
     93 */
     94 idAnim::~idAnim() {
     95 	int i;
     96 
     97 	for( i = 0; i < numAnims; i++ ) {
     98 		anims[ i ]->DecreaseRefs();
     99 	}
    100 
    101 	for( i = 0; i < frameCommands.Num(); i++ ) {
    102 		delete frameCommands[ i ].string;
    103 	}
    104 }
    105 
    106 /*
    107 =====================
    108 idAnim::SetAnim
    109 =====================
    110 */
    111 void idAnim::SetAnim( const idDeclModelDef *modelDef, const char *sourcename, const char *animname, int num, const idMD5Anim *md5anims[ ANIM_MaxSyncedAnims ] ) {
    112 	int i;
    113 
    114 	this->modelDef = modelDef;
    115 
    116 	for( i = 0; i < numAnims; i++ ) {
    117 		anims[ i ]->DecreaseRefs();
    118 		anims[ i ] = NULL;
    119 	}
    120 
    121 	assert( ( num > 0 ) && ( num <= ANIM_MaxSyncedAnims ) );
    122 	numAnims	= num;
    123 	realname	= sourcename;
    124 	name		= animname;
    125 
    126 	for( i = 0; i < num; i++ ) {
    127 		anims[ i ] = md5anims[ i ];
    128 		anims[ i ]->IncreaseRefs();
    129 	}
    130 
    131 	memset( &flags, 0, sizeof( flags ) );
    132 
    133 	for( i = 0; i < frameCommands.Num(); i++ ) {
    134 		delete frameCommands[ i ].string;
    135 	}
    136 
    137 	frameLookup.Clear();
    138 	frameCommands.Clear();
    139 }
    140 
    141 /*
    142 =====================
    143 idAnim::Name
    144 =====================
    145 */
    146 const char *idAnim::Name() const {
    147 	return name;
    148 }
    149 
    150 /*
    151 =====================
    152 idAnim::FullName
    153 =====================
    154 */
    155 const char *idAnim::FullName() const {
    156 	return realname;
    157 }
    158 
    159 /*
    160 =====================
    161 idAnim::MD5Anim
    162 
    163 index 0 will never be NULL.  Any anim >= NumAnims will return NULL.
    164 =====================
    165 */
    166 const idMD5Anim *idAnim::MD5Anim( int num ) const {
    167 	if ( anims[0] == NULL ) { 
    168 		return NULL;
    169 	}
    170 	return anims[ num ];
    171 }
    172 
    173 /*
    174 =====================
    175 idAnim::ModelDef
    176 =====================
    177 */
    178 const idDeclModelDef *idAnim::ModelDef() const {
    179 	return modelDef;
    180 }
    181 
    182 /*
    183 =====================
    184 idAnim::Length
    185 =====================
    186 */
    187 int idAnim::Length() const {
    188 	if ( !anims[ 0 ] ) {
    189 		return 0;
    190 	}
    191 
    192 	return anims[ 0 ]->Length();
    193 }
    194 
    195 /*
    196 =====================
    197 idAnim::NumFrames
    198 =====================
    199 */
    200 int	idAnim::NumFrames() const { 
    201 	if ( !anims[ 0 ] ) {
    202 		return 0;
    203 	}
    204 	
    205 	return anims[ 0 ]->NumFrames();
    206 }
    207 
    208 /*
    209 =====================
    210 idAnim::NumAnims
    211 =====================
    212 */
    213 int	idAnim::NumAnims() const { 
    214 	return numAnims;
    215 }
    216 
    217 /*
    218 =====================
    219 idAnim::TotalMovementDelta
    220 =====================
    221 */
    222 const idVec3 &idAnim::TotalMovementDelta() const {
    223 	if ( !anims[ 0 ] ) {
    224 		return vec3_zero;
    225 	}
    226 	
    227 	return anims[ 0 ]->TotalMovementDelta();
    228 }
    229 
    230 /*
    231 =====================
    232 idAnim::GetOrigin
    233 =====================
    234 */
    235 bool idAnim::GetOrigin( idVec3 &offset, int animNum, int currentTime, int cyclecount ) const {
    236 	if ( !anims[ animNum ] ) {
    237 		offset.Zero();
    238 		return false;
    239 	}
    240 
    241 	anims[ animNum ]->GetOrigin( offset, currentTime, cyclecount );
    242 	return true;
    243 }
    244 
    245 /*
    246 =====================
    247 idAnim::GetOriginRotation
    248 =====================
    249 */
    250 bool idAnim::GetOriginRotation( idQuat &rotation, int animNum, int currentTime, int cyclecount ) const {
    251 	if ( !anims[ animNum ] ) {
    252 		rotation.Set( 0.0f, 0.0f, 0.0f, 1.0f );
    253 		return false;
    254 	}
    255 
    256 	anims[ animNum ]->GetOriginRotation( rotation, currentTime, cyclecount );
    257 	return true;
    258 }
    259 
    260 /*
    261 =====================
    262 idAnim::GetBounds
    263 =====================
    264 */
    265 ID_INLINE bool idAnim::GetBounds( idBounds &bounds, int animNum, int currentTime, int cyclecount ) const {
    266 	if ( !anims[ animNum ] ) {
    267 		return false;
    268 	}
    269 
    270 	anims[ animNum ]->GetBounds( bounds, currentTime, cyclecount );
    271 	return true;
    272 }
    273 
    274 
    275 /*
    276 =====================
    277 idAnim::AddFrameCommand
    278 
    279 Returns NULL if no error.
    280 =====================
    281 */
    282 const char *idAnim::AddFrameCommand( const idDeclModelDef *modelDef, int framenum, idLexer &src, const idDict *def ) {
    283 	int					i;
    284 	int					index;
    285 	idStr				text;
    286 	idStr				funcname;
    287 	frameCommand_t		fc;
    288 	idToken				token;
    289 	const jointInfo_t	*jointInfo;
    290 
    291 	// make sure we're within bounds
    292 	if ( ( framenum < 1 ) || ( framenum > anims[ 0 ]->NumFrames() ) ) {
    293 		return va( "Frame %d out of range", framenum );
    294 	}
    295 
    296 	// frame numbers are 1 based in .def files, but 0 based internally
    297 	framenum--;
    298 
    299 	memset( &fc, 0, sizeof( fc ) );
    300 
    301 	if( !src.ReadTokenOnLine( &token ) ) {
    302 		return "Unexpected end of line";
    303 	}
    304 	if ( token == "call" ) {
    305 		if( !src.ReadTokenOnLine( &token ) ) {
    306 			return "Unexpected end of line";
    307 		}
    308 		fc.type = FC_SCRIPTFUNCTION;
    309 		fc.function = gameLocal.program.FindFunction( token );
    310 		if ( !fc.function ) {
    311 			return va( "Function '%s' not found", token.c_str() );
    312 		}
    313 	} else if ( token == "object_call" ) {
    314 		if( !src.ReadTokenOnLine( &token ) ) {
    315 			return "Unexpected end of line";
    316 		}
    317 		fc.type = FC_SCRIPTFUNCTIONOBJECT;
    318 		fc.string = new (TAG_ANIM) idStr( token );
    319 	} else if ( token == "event" ) {
    320 		if( !src.ReadTokenOnLine( &token ) ) {
    321 			return "Unexpected end of line";
    322 		}
    323 		fc.type = FC_EVENTFUNCTION;
    324 		const idEventDef *ev = idEventDef::FindEvent( token );
    325 		if ( !ev ) {
    326 			return va( "Event '%s' not found", token.c_str() );
    327 		}
    328 		if ( ev->GetNumArgs() != 0 ) {
    329 			return va( "Event '%s' has arguments", token.c_str() );
    330 		}
    331 		fc.string = new (TAG_ANIM) idStr( token );
    332 	} else if ( token == "sound" ) {
    333 		if( !src.ReadTokenOnLine( &token ) ) {
    334 			return "Unexpected end of line";
    335 		}
    336 		fc.type = FC_SOUND;
    337 		if ( !token.Cmpn( "snd_", 4 ) ) {
    338 			fc.string = new (TAG_ANIM) idStr( token );
    339 		} else {
    340 			fc.soundShader = declManager->FindSound( token );
    341 			if ( fc.soundShader->GetState() == DS_DEFAULTED ) {
    342 				gameLocal.Warning( "Sound '%s' not found", token.c_str() );
    343 			}
    344 		}
    345 	} else if ( token == "sound_voice" ) {
    346 		if( !src.ReadTokenOnLine( &token ) ) {
    347 			return "Unexpected end of line";
    348 		}
    349 		fc.type = FC_SOUND_VOICE;
    350 		if ( !token.Cmpn( "snd_", 4 ) ) {
    351 			fc.string = new (TAG_ANIM) idStr( token );
    352 		} else {
    353 			fc.soundShader = declManager->FindSound( token );
    354 			if ( fc.soundShader->GetState() == DS_DEFAULTED ) {
    355 				gameLocal.Warning( "Sound '%s' not found", token.c_str() );
    356 			}
    357 		}
    358 	} else if ( token == "sound_voice2" ) {
    359 		if( !src.ReadTokenOnLine( &token ) ) {
    360 			return "Unexpected end of line";
    361 		}
    362 		fc.type = FC_SOUND_VOICE2;
    363 		if ( !token.Cmpn( "snd_", 4 ) ) {
    364 			fc.string = new (TAG_ANIM) idStr( token );
    365 		} else {
    366 			fc.soundShader = declManager->FindSound( token );
    367 			if ( fc.soundShader->GetState() == DS_DEFAULTED ) {
    368 				gameLocal.Warning( "Sound '%s' not found", token.c_str() );
    369 			}
    370 		}
    371 	} else if ( token == "sound_body" ) {
    372 		if( !src.ReadTokenOnLine( &token ) ) {
    373 			return "Unexpected end of line";
    374 		}
    375 		fc.type = FC_SOUND_BODY;
    376 		if ( !token.Cmpn( "snd_", 4 ) ) {
    377 			fc.string = new (TAG_ANIM) idStr( token );
    378 		} else {
    379 			fc.soundShader = declManager->FindSound( token );
    380 			if ( fc.soundShader->GetState() == DS_DEFAULTED ) {
    381 				gameLocal.Warning( "Sound '%s' not found", token.c_str() );
    382 			}
    383 		}
    384 	} else if ( token == "sound_body2" ) {
    385 		if( !src.ReadTokenOnLine( &token ) ) {
    386 			return "Unexpected end of line";
    387 		}
    388 		fc.type = FC_SOUND_BODY2;
    389 		if ( !token.Cmpn( "snd_", 4 ) ) {
    390 			fc.string = new (TAG_ANIM) idStr( token );
    391 		} else {
    392 			fc.soundShader = declManager->FindSound( token );
    393 			if ( fc.soundShader->GetState() == DS_DEFAULTED ) {
    394 				gameLocal.Warning( "Sound '%s' not found", token.c_str() );
    395 			}
    396 		}
    397 	} else if ( token == "sound_body3" ) {
    398 		if( !src.ReadTokenOnLine( &token ) ) {
    399 			return "Unexpected end of line";
    400 		}
    401 		fc.type = FC_SOUND_BODY3;
    402 		if ( !token.Cmpn( "snd_", 4 ) ) {
    403 			fc.string = new (TAG_ANIM) idStr( token );
    404 		} else {
    405 			fc.soundShader = declManager->FindSound( token );
    406 			if ( fc.soundShader->GetState() == DS_DEFAULTED ) {
    407 				gameLocal.Warning( "Sound '%s' not found", token.c_str() );
    408 			}
    409 		}
    410 	} else if ( token == "sound_weapon" ) {
    411 		if( !src.ReadTokenOnLine( &token ) ) {
    412 			return "Unexpected end of line";
    413 		}
    414 		fc.type = FC_SOUND_WEAPON;
    415 		if ( !token.Cmpn( "snd_", 4 ) ) {
    416 			fc.string = new (TAG_ANIM) idStr( token );
    417 		} else {
    418 			fc.soundShader = declManager->FindSound( token );
    419 			if ( fc.soundShader->GetState() == DS_DEFAULTED ) {
    420 				gameLocal.Warning( "Sound '%s' not found", token.c_str() );
    421 			}
    422 		}
    423 	} else if ( token == "sound_global" ) {
    424 		if( !src.ReadTokenOnLine( &token ) ) {
    425 			return "Unexpected end of line";
    426 		}
    427 		fc.type = FC_SOUND_GLOBAL;
    428 		if ( !token.Cmpn( "snd_", 4 ) ) {
    429 			fc.string = new (TAG_ANIM) idStr( token );
    430 		} else {
    431 			fc.soundShader = declManager->FindSound( token );
    432 			if ( fc.soundShader->GetState() == DS_DEFAULTED ) {
    433 				gameLocal.Warning( "Sound '%s' not found", token.c_str() );
    434 			}
    435 		}
    436 	} else if ( token == "sound_item" ) {
    437 		if( !src.ReadTokenOnLine( &token ) ) {
    438 			return "Unexpected end of line";
    439 		}
    440 		fc.type = FC_SOUND_ITEM;
    441 		if ( !token.Cmpn( "snd_", 4 ) ) {
    442 			fc.string = new (TAG_ANIM) idStr( token );
    443 		} else {
    444 			fc.soundShader = declManager->FindSound( token );
    445 			if ( fc.soundShader->GetState() == DS_DEFAULTED ) {
    446 				gameLocal.Warning( "Sound '%s' not found", token.c_str() );
    447 			}
    448 		}
    449 	} else if ( token == "sound_chatter" ) {
    450 		if( !src.ReadTokenOnLine( &token ) ) {
    451 			return "Unexpected end of line";
    452 		}
    453 		fc.type = FC_SOUND_CHATTER;
    454 		if ( !token.Cmpn( "snd_", 4 ) ) {
    455 			fc.string = new (TAG_ANIM) idStr( token );
    456 		} else {
    457 			fc.soundShader = declManager->FindSound( token );
    458 			if ( fc.soundShader->GetState() == DS_DEFAULTED ) {
    459 				gameLocal.Warning( "Sound '%s' not found", token.c_str() );
    460 			}
    461 		}
    462 	} else if ( token == "skin" ) {
    463 		if( !src.ReadTokenOnLine( &token ) ) {
    464 			return "Unexpected end of line";
    465 		}
    466 		fc.type = FC_SKIN;
    467 		if ( token == "none" ) {
    468 			fc.skin = NULL;
    469 		} else {
    470 			fc.skin = declManager->FindSkin( token );
    471 			if ( !fc.skin ) {
    472 				return va( "Skin '%s' not found", token.c_str() );
    473 			}
    474 		}
    475 	} else if ( token == "fx" ) {
    476 		if( !src.ReadTokenOnLine( &token ) ) {
    477 			return "Unexpected end of line";
    478 		}
    479 		fc.type = FC_FX;
    480 		if ( !declManager->FindType( DECL_FX, token.c_str() ) ) {
    481 			return va( "fx '%s' not found", token.c_str() );
    482 		}
    483 		fc.string = new (TAG_ANIM) idStr( token );
    484 	} else if ( token == "trigger" ) {
    485 		if( !src.ReadTokenOnLine( &token ) ) {
    486 			return "Unexpected end of line";
    487 		}
    488 		fc.type = FC_TRIGGER;
    489 		fc.string = new (TAG_ANIM) idStr( token );
    490 	} else if ( token == "triggerSmokeParticle" ) {
    491 		if( !src.ReadTokenOnLine( &token ) ) {
    492 			return "Unexpected end of line";
    493 		}
    494 		fc.type = FC_TRIGGER_SMOKE_PARTICLE;
    495 		if ( !declManager->FindType( DECL_PARTICLE, token.c_str() ) ) {
    496 			return va( "Particle '%s' not found", token.c_str() );
    497 		}
    498 		fc.string = new (TAG_ANIM) idStr( token );
    499 	} else if ( token == "melee" ) {
    500 		if( !src.ReadTokenOnLine( &token ) ) {
    501 			return "Unexpected end of line";
    502 		}
    503 		fc.type = FC_MELEE;
    504 		if ( !gameLocal.FindEntityDef( token.c_str(), false ) ) {
    505 			return va( "Unknown entityDef '%s'", token.c_str() );
    506 		}
    507 		fc.string = new (TAG_ANIM) idStr( token );
    508 	} else if ( token == "direct_damage" ) {
    509 		if( !src.ReadTokenOnLine( &token ) ) {
    510 			return "Unexpected end of line";
    511 		}
    512 		fc.type = FC_DIRECTDAMAGE;
    513 		if ( !gameLocal.FindEntityDef( token.c_str(), false ) ) {
    514 			return va( "Unknown entityDef '%s'", token.c_str() );
    515 		}
    516 		fc.string = new (TAG_ANIM) idStr( token );
    517 	} else if ( token == "attack_begin" ) {
    518 		if( !src.ReadTokenOnLine( &token ) ) {
    519 			return "Unexpected end of line";
    520 		}
    521 		fc.type = FC_BEGINATTACK;
    522 		if ( !gameLocal.FindEntityDef( token.c_str(), false ) ) {
    523 			return va( "Unknown entityDef '%s'", token.c_str() );
    524 		}
    525 		fc.string = new (TAG_ANIM) idStr( token );
    526 	} else if ( token == "attack_end" ) {
    527 		fc.type = FC_ENDATTACK;
    528 	} else if ( token == "muzzle_flash" ) {
    529 		if( !src.ReadTokenOnLine( &token ) ) {
    530 			return "Unexpected end of line";
    531 		}
    532 		if ( ( token != "" ) && !modelDef->FindJoint( token ) ) {
    533 			return va( "Joint '%s' not found", token.c_str() );
    534 		}
    535 		fc.type = FC_MUZZLEFLASH;
    536 		fc.string = new (TAG_ANIM) idStr( token );
    537 	} else if ( token == "create_missile" ) {
    538 		if( !src.ReadTokenOnLine( &token ) ) {
    539 			return "Unexpected end of line";
    540 		}
    541 		if ( !modelDef->FindJoint( token ) ) {
    542 			return va( "Joint '%s' not found", token.c_str() );
    543 		}
    544 		fc.type = FC_CREATEMISSILE;
    545 		fc.string = new (TAG_ANIM) idStr( token );
    546 	} else if ( token == "launch_missile" ) {
    547 		if( !src.ReadTokenOnLine( &token ) ) {
    548 			return "Unexpected end of line";
    549 		}
    550 		if ( !modelDef->FindJoint( token ) ) {
    551 			return va( "Joint '%s' not found", token.c_str() );
    552 		}
    553 		fc.type = FC_LAUNCHMISSILE;
    554 		fc.string = new (TAG_ANIM) idStr( token );
    555 	} else if ( token == "fire_missile_at_target" ) {
    556 		if( !src.ReadTokenOnLine( &token ) ) {
    557 			return "Unexpected end of line";
    558 		}
    559 		jointInfo = modelDef->FindJoint( token );
    560 		if ( !jointInfo ) {
    561 			return va( "Joint '%s' not found", token.c_str() );
    562 		}
    563 		if( !src.ReadTokenOnLine( &token ) ) {
    564 			return "Unexpected end of line";
    565 		}
    566 		fc.type = FC_FIREMISSILEATTARGET;
    567 		fc.string = new (TAG_ANIM) idStr( token );
    568 		fc.index = jointInfo->num;
    569 	} else if ( token == "launch_projectile" ) {
    570 		if( !src.ReadTokenOnLine( &token ) ) {
    571 			return "Unexpected end of line";
    572 		}
    573 		if ( !declManager->FindDeclWithoutParsing( DECL_ENTITYDEF, token, false ) ) {
    574 			return "Unknown projectile def";
    575 		}
    576 		fc.type = FC_LAUNCH_PROJECTILE;
    577 		fc.string = new (TAG_ANIM) idStr( token );
    578 	} else if ( token == "trigger_fx" ) {
    579 		
    580 		if( !src.ReadTokenOnLine( &token ) ) {
    581 			return "Unexpected end of line";
    582 		}
    583 		jointInfo = modelDef->FindJoint( token );
    584 		if ( !jointInfo ) {
    585 			return va( "Joint '%s' not found", token.c_str() );
    586 		}
    587 		if( !src.ReadTokenOnLine( &token ) ) {
    588 			return "Unexpected end of line";
    589 		}
    590 		if ( !declManager->FindType( DECL_FX, token, false ) ) {
    591 			return "Unknown FX def";
    592 		}
    593 
    594 		fc.type = FC_TRIGGER_FX;
    595 		fc.string = new (TAG_ANIM) idStr( token );
    596 		fc.index = jointInfo->num;
    597 
    598 	} else if ( token == "start_emitter" ) {
    599 
    600 		idStr str;
    601 		if( !src.ReadTokenOnLine( &token ) ) {
    602 			return "Unexpected end of line";
    603 		}
    604 		str = token + " ";
    605 
    606 		if( !src.ReadTokenOnLine( &token ) ) {
    607 			return "Unexpected end of line";
    608 		}
    609 		jointInfo = modelDef->FindJoint( token );
    610 		if ( !jointInfo ) {
    611 			return va( "Joint '%s' not found", token.c_str() );
    612 		}
    613 		if( !src.ReadTokenOnLine( &token ) ) {
    614 			return "Unexpected end of line";
    615 		}
    616 		if ( !declManager->FindType( DECL_PARTICLE, token.c_str() ) ) {
    617 			return va( "Particle '%s' not found", token.c_str() );
    618 		}
    619 		str += token;
    620 		fc.type = FC_START_EMITTER;
    621 		fc.string = new (TAG_ANIM) idStr( str );
    622 		fc.index = jointInfo->num;
    623 
    624 	} else if ( token == "stop_emitter" ) {
    625 
    626 		if( !src.ReadTokenOnLine( &token ) ) {
    627 			return "Unexpected end of line";
    628 		}
    629 		fc.type = FC_STOP_EMITTER;
    630 		fc.string = new (TAG_ANIM) idStr( token );
    631 	} else if ( token == "footstep" ) {
    632 		fc.type = FC_FOOTSTEP;
    633 	} else if ( token == "leftfoot" ) {
    634 		fc.type = FC_LEFTFOOT;
    635 	} else if ( token == "rightfoot" ) {
    636 		fc.type = FC_RIGHTFOOT;
    637 	} else if ( token == "enableEyeFocus" ) {
    638 		fc.type = FC_ENABLE_EYE_FOCUS;
    639 	} else if ( token == "disableEyeFocus" ) {
    640 		fc.type = FC_DISABLE_EYE_FOCUS;
    641 	} else if ( token == "disableGravity" ) {
    642 		fc.type = FC_DISABLE_GRAVITY;
    643 	} else if ( token == "enableGravity" ) {
    644 		fc.type = FC_ENABLE_GRAVITY;
    645 	} else if ( token == "jump" ) {
    646 		fc.type = FC_JUMP;
    647 	} else if ( token == "enableClip" ) {
    648 		fc.type = FC_ENABLE_CLIP;
    649 	} else if ( token == "disableClip" ) {
    650 		fc.type = FC_DISABLE_CLIP;
    651 	} else if ( token == "enableWalkIK" ) {
    652 		fc.type = FC_ENABLE_WALK_IK;
    653 	} else if ( token == "disableWalkIK" ) {
    654 		fc.type = FC_DISABLE_WALK_IK;
    655 	} else if ( token == "enableLegIK" ) {
    656 		if( !src.ReadTokenOnLine( &token ) ) {
    657 			return "Unexpected end of line";
    658 		}
    659 		fc.type = FC_ENABLE_LEG_IK;
    660 		fc.index = atoi( token );
    661 	} else if ( token == "disableLegIK" ) {
    662 		if( !src.ReadTokenOnLine( &token ) ) {
    663 			return "Unexpected end of line";
    664 		}
    665 		fc.type = FC_DISABLE_LEG_IK;
    666 		fc.index = atoi( token );
    667 	} else if ( token == "recordDemo" ) {
    668 		fc.type = FC_RECORDDEMO;
    669 		if( src.ReadTokenOnLine( &token ) ) {
    670 			fc.string = new (TAG_ANIM) idStr( token );
    671 		}
    672 	} else if ( token == "aviGame" ) {
    673 		fc.type = FC_AVIGAME;
    674 		if( src.ReadTokenOnLine( &token ) ) {
    675 			fc.string = new (TAG_ANIM) idStr( token );
    676 		}
    677 	} else {
    678 		return va( "Unknown command '%s'", token.c_str() );
    679 	}
    680 
    681 	// check if we've initialized the frame loopup table
    682 	if ( !frameLookup.Num() ) {
    683 		// we haven't, so allocate the table and initialize it
    684 		frameLookup.SetGranularity( 1 );
    685 		frameLookup.SetNum( anims[ 0 ]->NumFrames() );
    686 		for( i = 0; i < frameLookup.Num(); i++ ) {
    687 			frameLookup[ i ].num = 0;
    688 			frameLookup[ i ].firstCommand = 0;
    689 		}
    690 	}
    691 
    692 	// allocate space for a new command
    693 	frameCommands.Alloc();
    694 
    695 	// calculate the index of the new command
    696 	index = frameLookup[ framenum ].firstCommand + frameLookup[ framenum ].num;
    697 
    698 	// move all commands from our index onward up one to give us space for our new command
    699 	for( i = frameCommands.Num() - 1; i > index; i-- ) {
    700 		frameCommands[ i ] = frameCommands[ i - 1 ];
    701 	}
    702 
    703 	// fix the indices of any later frames to account for the inserted command
    704 	for( i = framenum + 1; i < frameLookup.Num(); i++ ) {
    705 		frameLookup[ i ].firstCommand++;
    706 	}
    707 
    708 	// store the new command 
    709 	frameCommands[ index ] = fc;
    710 
    711 	// increase the number of commands on this frame
    712 	frameLookup[ framenum ].num++;
    713 
    714 	// return with no error
    715 	return NULL;
    716 }
    717 
    718 /*
    719 =====================
    720 idAnim::CallFrameCommands
    721 =====================
    722 */
    723 void idAnim::CallFrameCommands( idEntity *ent, int from, int to ) const {
    724 	int index;
    725 	int end;
    726 	int frame;
    727 	int numframes;
    728 
    729 	numframes = anims[ 0 ]->NumFrames();
    730 
    731 	frame = from;
    732 	while( frame != to ) {
    733 		frame++;
    734 		if ( frame >= numframes ) {
    735 			frame = 0;
    736 		}
    737 
    738 		index = frameLookup[ frame ].firstCommand;
    739 		end = index + frameLookup[ frame ].num;
    740 		while( index < end ) {
    741 			const frameCommand_t &command = frameCommands[ index++ ];
    742 			switch( command.type ) {
    743 				case FC_SCRIPTFUNCTION: {
    744 					gameLocal.CallFrameCommand( ent, command.function );
    745 					break;
    746 				}
    747 				case FC_SCRIPTFUNCTIONOBJECT: {
    748 					gameLocal.CallObjectFrameCommand( ent, command.string->c_str() );
    749 					break;
    750 				}
    751 				case FC_EVENTFUNCTION: {
    752 					const idEventDef *ev = idEventDef::FindEvent( command.string->c_str() );
    753 					ent->ProcessEvent( ev );
    754 					break;
    755 				}
    756 				case FC_SOUND: {
    757 					if ( !command.soundShader ) {
    758 						if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_ANY, 0, false, NULL ) ) {
    759 							gameLocal.Warning( "Framecommand 'sound' on entity '%s', anim '%s', frame %d: Could not find sound '%s'",
    760 								ent->name.c_str(), FullName(), frame + 1, command.string->c_str() );
    761 						}
    762 					} else {
    763 						ent->StartSoundShader( command.soundShader, SND_CHANNEL_ANY, 0, false, NULL );
    764 					}
    765 					break;
    766 				}
    767 				case FC_SOUND_VOICE: {
    768 					if ( !command.soundShader ) {
    769 						if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_VOICE, 0, false, NULL ) ) {
    770 							gameLocal.Warning( "Framecommand 'sound_voice' on entity '%s', anim '%s', frame %d: Could not find sound '%s'",
    771 								ent->name.c_str(), FullName(), frame + 1, command.string->c_str() );
    772 						}
    773 					} else {
    774 						ent->StartSoundShader( command.soundShader, SND_CHANNEL_VOICE, 0, false, NULL );
    775 					}
    776 					break;
    777 				}
    778 				case FC_SOUND_VOICE2: {
    779 					if ( !command.soundShader ) {
    780 						if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_VOICE2, 0, false, NULL ) ) {
    781 							gameLocal.Warning( "Framecommand 'sound_voice2' on entity '%s', anim '%s', frame %d: Could not find sound '%s'",
    782 								ent->name.c_str(), FullName(), frame + 1, command.string->c_str() );
    783 						}
    784 					} else {
    785 						ent->StartSoundShader( command.soundShader, SND_CHANNEL_VOICE2, 0, false, NULL );
    786 					}
    787 					break;
    788 				}
    789 				case FC_SOUND_BODY: {
    790 					if ( !command.soundShader ) {
    791 						if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_BODY, 0, false, NULL ) ) {
    792 							gameLocal.Warning( "Framecommand 'sound_body' on entity '%s', anim '%s', frame %d: Could not find sound '%s'",
    793 								ent->name.c_str(), FullName(), frame + 1, command.string->c_str() );
    794 						}
    795 					} else {
    796 						ent->StartSoundShader( command.soundShader, SND_CHANNEL_BODY, 0, false, NULL );
    797 					}
    798 					break;
    799 				}
    800 				case FC_SOUND_BODY2: {
    801 					if ( !command.soundShader ) {
    802 						if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_BODY2, 0, false, NULL ) ) {
    803 							gameLocal.Warning( "Framecommand 'sound_body2' on entity '%s', anim '%s', frame %d: Could not find sound '%s'",
    804 								ent->name.c_str(), FullName(), frame + 1, command.string->c_str() );
    805 						}
    806 					} else {
    807 						ent->StartSoundShader( command.soundShader, SND_CHANNEL_BODY2, 0, false, NULL );
    808 					}
    809 					break;
    810 				}
    811 				case FC_SOUND_BODY3: {
    812 					if ( !command.soundShader ) {
    813 						if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_BODY3, 0, false, NULL ) ) {
    814 							gameLocal.Warning( "Framecommand 'sound_body3' on entity '%s', anim '%s', frame %d: Could not find sound '%s'",
    815 								ent->name.c_str(), FullName(), frame + 1, command.string->c_str() );
    816 						}
    817 					} else {
    818 						ent->StartSoundShader( command.soundShader, SND_CHANNEL_BODY3, 0, false, NULL );
    819 					}
    820 					break;
    821 									 }
    822 				case FC_SOUND_WEAPON: {
    823 					if ( !command.soundShader ) {
    824 						if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_WEAPON, 0, false, NULL ) ) {
    825 							gameLocal.Warning( "Framecommand 'sound_weapon' on entity '%s', anim '%s', frame %d: Could not find sound '%s'",
    826 								ent->name.c_str(), FullName(), frame + 1, command.string->c_str() );
    827 						}
    828 					} else {
    829 						ent->StartSoundShader( command.soundShader, SND_CHANNEL_WEAPON, 0, false, NULL );
    830 					}
    831 					break;
    832 				}
    833 				case FC_SOUND_GLOBAL: {
    834 					if ( !command.soundShader ) {
    835 						if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL ) ) {
    836 							gameLocal.Warning( "Framecommand 'sound_global' on entity '%s', anim '%s', frame %d: Could not find sound '%s'",
    837 								ent->name.c_str(), FullName(), frame + 1, command.string->c_str() );
    838 						}
    839 					} else {
    840 						ent->StartSoundShader( command.soundShader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
    841 					}
    842 					break;
    843 				}
    844 				case FC_SOUND_ITEM: {
    845 					if ( !command.soundShader ) {
    846 						if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_ITEM, 0, false, NULL ) ) {
    847 							gameLocal.Warning( "Framecommand 'sound_item' on entity '%s', anim '%s', frame %d: Could not find sound '%s'",
    848 								ent->name.c_str(), FullName(), frame + 1, command.string->c_str() );
    849 						}
    850 					} else {
    851 						ent->StartSoundShader( command.soundShader, SND_CHANNEL_ITEM, 0, false, NULL );
    852 					}
    853 					break;
    854 				}
    855 				case FC_SOUND_CHATTER: {
    856 					if ( ent->CanPlayChatterSounds() ) {
    857 						if ( !command.soundShader ) {
    858 							if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_VOICE, 0, false, NULL ) ) {
    859 								gameLocal.Warning( "Framecommand 'sound_chatter' on entity '%s', anim '%s', frame %d: Could not find sound '%s'",
    860 									ent->name.c_str(), FullName(), frame + 1, command.string->c_str() );
    861 							}
    862 						} else {
    863 							ent->StartSoundShader( command.soundShader, SND_CHANNEL_VOICE, 0, false, NULL );
    864 						}
    865 					}
    866 					break;
    867 				}
    868 				case FC_FX: {
    869 					idEntityFx::StartFx( command.string->c_str(), NULL, NULL, ent, true );
    870 					break;
    871 				}
    872 				case FC_SKIN: {
    873 					ent->SetSkin( command.skin );
    874 					break;
    875 				}
    876 				case FC_TRIGGER: {
    877 					idEntity *target;
    878 
    879 					target = gameLocal.FindEntity( command.string->c_str() );
    880 					if ( target ) {
    881 						SetTimeState ts(target->timeGroup);
    882 						target->Signal( SIG_TRIGGER );
    883 						target->ProcessEvent( &EV_Activate, ent );
    884 						target->TriggerGuis();
    885 					} else {
    886 						gameLocal.Warning( "Framecommand 'trigger' on entity '%s', anim '%s', frame %d: Could not find entity '%s'",
    887 							ent->name.c_str(), FullName(), frame + 1, command.string->c_str() );
    888 					}
    889 					break;
    890 				}
    891 				case FC_TRIGGER_SMOKE_PARTICLE: {
    892 					ent->ProcessEvent( &AI_TriggerParticles, command.string->c_str() );
    893 					break;
    894 				}
    895 				case FC_MELEE: {
    896 					ent->ProcessEvent( &AI_AttackMelee, command.string->c_str() );
    897 					break;
    898 				}
    899 				case FC_DIRECTDAMAGE: {
    900 					ent->ProcessEvent( &AI_DirectDamage, command.string->c_str() );
    901 					break;
    902 				}
    903 				case FC_BEGINATTACK: {
    904 					ent->ProcessEvent( &AI_BeginAttack, command.string->c_str() );
    905 					break;
    906 				}
    907 				case FC_ENDATTACK: {
    908 					ent->ProcessEvent( &AI_EndAttack );
    909 					break;
    910 				}
    911 				case FC_MUZZLEFLASH: {
    912 					ent->ProcessEvent( &AI_MuzzleFlash, command.string->c_str() );
    913 					break;
    914 				}
    915 				case FC_CREATEMISSILE: {
    916 					ent->ProcessEvent( &AI_CreateMissile, command.string->c_str() );
    917 					break;
    918 				}
    919 				case FC_LAUNCHMISSILE: {
    920 					ent->ProcessEvent( &AI_AttackMissile, command.string->c_str() );
    921 					break;
    922 				}
    923 				case FC_FIREMISSILEATTARGET: {
    924 					ent->ProcessEvent( &AI_FireMissileAtTarget, modelDef->GetJointName( command.index ), command.string->c_str() );
    925 					break;
    926 				}
    927 				case FC_LAUNCH_PROJECTILE: {
    928 					ent->ProcessEvent( &AI_LaunchProjectile, command.string->c_str() );
    929 					break;
    930 				}
    931 				case FC_TRIGGER_FX: {
    932 					ent->ProcessEvent( &AI_TriggerFX, modelDef->GetJointName( command.index ), command.string->c_str() );
    933 					break;
    934 				}
    935 				case FC_START_EMITTER: {
    936 					int index = command.string->Find(" ");
    937 					if(index >= 0) {
    938 						idStr name = command.string->Left(index);
    939 						idStr particle = command.string->Right(command.string->Length() - index - 1);
    940 						ent->ProcessEvent( &AI_StartEmitter, name.c_str(), modelDef->GetJointName( command.index ), particle.c_str() );
    941 					}
    942 				}
    943 
    944 				case FC_STOP_EMITTER: {
    945 					ent->ProcessEvent( &AI_StopEmitter, command.string->c_str() );
    946 				}
    947 				case FC_FOOTSTEP : {
    948 					ent->ProcessEvent( &EV_Footstep );
    949 					break;
    950 				}
    951 				case FC_LEFTFOOT: {
    952 					ent->ProcessEvent( &EV_FootstepLeft );
    953 					break;
    954 				}
    955 				case FC_RIGHTFOOT: {
    956 					ent->ProcessEvent( &EV_FootstepRight );
    957 					break;
    958 				}
    959 				case FC_ENABLE_EYE_FOCUS: {
    960 					ent->ProcessEvent( &AI_EnableEyeFocus );
    961 					break;
    962 				}
    963 				case FC_DISABLE_EYE_FOCUS: {
    964 					ent->ProcessEvent( &AI_DisableEyeFocus );
    965 					break;
    966 				}
    967 				case FC_DISABLE_GRAVITY: {
    968 					ent->ProcessEvent( &AI_DisableGravity );
    969 					break;
    970 				}
    971 				case FC_ENABLE_GRAVITY: {
    972 					ent->ProcessEvent( &AI_EnableGravity );
    973 					break;
    974 				}
    975 				case FC_JUMP: {
    976 					ent->ProcessEvent( &AI_JumpFrame );
    977 					break;
    978 				}
    979 				case FC_ENABLE_CLIP: {
    980 					ent->ProcessEvent( &AI_EnableClip );
    981 					break;
    982 				}
    983 				case FC_DISABLE_CLIP: {
    984 					ent->ProcessEvent( &AI_DisableClip );
    985 					break;
    986 				}
    987 				case FC_ENABLE_WALK_IK: {
    988 					ent->ProcessEvent( &EV_EnableWalkIK );
    989 					break;
    990 				}
    991 				case FC_DISABLE_WALK_IK: {
    992 					ent->ProcessEvent( &EV_DisableWalkIK );
    993 					break;
    994 				}
    995 				case FC_ENABLE_LEG_IK: {
    996 					ent->ProcessEvent( &EV_EnableLegIK, command.index );
    997 					break;
    998 				}
    999 				case FC_DISABLE_LEG_IK: {
   1000 					ent->ProcessEvent( &EV_DisableLegIK, command.index );
   1001 					break;
   1002 				}
   1003 				case FC_RECORDDEMO: {
   1004 					if ( command.string ) {
   1005 						cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "recordDemo %s", command.string->c_str() ) );
   1006 					} else {
   1007 						cmdSystem->BufferCommandText( CMD_EXEC_NOW, "stoprecording" );
   1008 					}
   1009 					break;
   1010 				}
   1011 				case FC_AVIGAME: {
   1012 					if ( command.string ) {
   1013 						cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "aviGame %s", command.string->c_str() ) );
   1014 					} else {
   1015 						cmdSystem->BufferCommandText( CMD_EXEC_NOW, "aviGame" );
   1016 					}
   1017 					break;
   1018 				}
   1019 			}
   1020 		}
   1021 	}
   1022 }
   1023 
   1024 /*
   1025 =====================
   1026 idAnim::FindFrameForFrameCommand
   1027 =====================
   1028 */
   1029 int	idAnim::FindFrameForFrameCommand( frameCommandType_t framecommand, const frameCommand_t **command ) const {
   1030 	int frame;
   1031 	int index;
   1032 	int numframes;
   1033 	int end;
   1034 
   1035 	if ( !frameCommands.Num() ) {
   1036 		return -1;
   1037 	}
   1038 
   1039 	numframes = anims[ 0 ]->NumFrames();
   1040 	for( frame = 0; frame < numframes; frame++ ) {
   1041 		end = frameLookup[ frame ].firstCommand + frameLookup[ frame ].num;
   1042 		for( index = frameLookup[ frame ].firstCommand; index < end; index++ ) {
   1043 			if ( frameCommands[ index ].type == framecommand ) {
   1044 				if ( command ) {
   1045 					*command = &frameCommands[ index ];
   1046 				}
   1047 				return frame;
   1048 			}
   1049 		}
   1050 	}
   1051 
   1052 	if ( command ) {
   1053 		*command = NULL;
   1054 	}
   1055 
   1056 	return -1;
   1057 }
   1058 
   1059 /*
   1060 =====================
   1061 idAnim::HasFrameCommands
   1062 =====================
   1063 */
   1064 bool idAnim::HasFrameCommands() const {
   1065 	if ( !frameCommands.Num() ) {
   1066 		return false;
   1067 	}
   1068 	return true;
   1069 }
   1070 
   1071 /*
   1072 =====================
   1073 idAnim::SetAnimFlags
   1074 =====================
   1075 */
   1076 void idAnim::SetAnimFlags( const animFlags_t &animflags ) {
   1077 	flags = animflags;
   1078 }
   1079 
   1080 /*
   1081 =====================
   1082 idAnim::GetAnimFlags
   1083 =====================
   1084 */
   1085 const animFlags_t &idAnim::GetAnimFlags() const {
   1086 	return flags;
   1087 }
   1088 
   1089 /***********************************************************************
   1090 
   1091 	idAnimBlend
   1092 
   1093 ***********************************************************************/
   1094 
   1095 /*
   1096 =====================
   1097 idAnimBlend::idAnimBlend
   1098 =====================
   1099 */
   1100 idAnimBlend::idAnimBlend() {
   1101 	Reset( NULL );
   1102 }
   1103 
   1104 /*
   1105 =====================
   1106 idAnimBlend::Save
   1107 
   1108 archives object for save game file
   1109 =====================
   1110 */
   1111 void idAnimBlend::Save( idSaveGame *savefile ) const {
   1112 	int i;
   1113 
   1114 	savefile->WriteInt( starttime );
   1115 	savefile->WriteInt( endtime );
   1116 	savefile->WriteInt( timeOffset );
   1117 	savefile->WriteFloat( rate );
   1118 
   1119 	savefile->WriteInt( blendStartTime );
   1120 	savefile->WriteInt( blendDuration );
   1121 	savefile->WriteFloat( blendStartValue );
   1122 	savefile->WriteFloat( blendEndValue );
   1123 
   1124 	for( i = 0; i < ANIM_MaxSyncedAnims; i++ ) {
   1125 		savefile->WriteFloat( animWeights[ i ] );
   1126 	}
   1127 	savefile->WriteShort( cycle );
   1128 	savefile->WriteShort( frame );
   1129 	savefile->WriteShort( animNum );
   1130 	savefile->WriteBool( allowMove );
   1131 	savefile->WriteBool( allowFrameCommands );
   1132 }
   1133 
   1134 /*
   1135 =====================
   1136 idAnimBlend::Restore
   1137 
   1138 unarchives object from save game file
   1139 =====================
   1140 */
   1141 void idAnimBlend::Restore( idRestoreGame *savefile, const idDeclModelDef *modelDef ) {
   1142 	int	i;
   1143 
   1144 	this->modelDef = modelDef;
   1145 
   1146 	savefile->ReadInt( starttime );
   1147 	savefile->ReadInt( endtime );
   1148 	savefile->ReadInt( timeOffset );
   1149 	savefile->ReadFloat( rate );
   1150 
   1151 	savefile->ReadInt( blendStartTime );
   1152 	savefile->ReadInt( blendDuration );
   1153 	savefile->ReadFloat( blendStartValue );
   1154 	savefile->ReadFloat( blendEndValue );
   1155 
   1156 	for( i = 0; i < ANIM_MaxSyncedAnims; i++ ) {
   1157 		savefile->ReadFloat( animWeights[ i ] );
   1158 	}
   1159 	savefile->ReadShort( cycle );
   1160 	savefile->ReadShort( frame );
   1161 	savefile->ReadShort( animNum );
   1162 	if ( !modelDef ) {
   1163 		animNum = 0;
   1164 	} else if ( ( animNum < 0 ) || ( animNum > modelDef->NumAnims() ) ) {
   1165 		gameLocal.Warning( "Anim number %d out of range for model '%s' during save game", animNum, modelDef->GetModelName() );
   1166 		animNum = 0;
   1167 	}
   1168 	savefile->ReadBool( allowMove );
   1169 	savefile->ReadBool( allowFrameCommands );
   1170 }
   1171 
   1172 /*
   1173 =====================
   1174 idAnimBlend::Reset
   1175 =====================
   1176 */
   1177 void idAnimBlend::Reset( const idDeclModelDef *_modelDef ) {
   1178 	modelDef	= _modelDef;
   1179 	cycle		= 1;
   1180 	starttime	= 0;
   1181 	endtime		= 0;
   1182 	timeOffset	= 0;
   1183 	rate		= 1.0f;
   1184 	frame		= 0;
   1185 	allowMove	= true;
   1186 	allowFrameCommands = true;
   1187 	animNum		= 0;
   1188 
   1189 	memset( animWeights, 0, sizeof( animWeights ) );
   1190 
   1191 	blendStartValue = 0.0f;
   1192 	blendEndValue	= 0.0f;
   1193     blendStartTime	= 0;
   1194 	blendDuration	= 0;
   1195 }
   1196 
   1197 /*
   1198 =====================
   1199 idAnimBlend::FullName
   1200 =====================
   1201 */
   1202 const char *idAnimBlend::AnimFullName() const {
   1203 	const idAnim *anim = Anim();
   1204 	if ( !anim ) {
   1205 		return "";
   1206 	}
   1207 
   1208 	return anim->FullName();
   1209 }
   1210 
   1211 /*
   1212 =====================
   1213 idAnimBlend::AnimName
   1214 =====================
   1215 */
   1216 const char *idAnimBlend::AnimName() const {
   1217 	const idAnim *anim = Anim();
   1218 	if ( !anim ) {
   1219 		return "";
   1220 	}
   1221 
   1222 	return anim->Name();
   1223 }
   1224 
   1225 /*
   1226 =====================
   1227 idAnimBlend::NumFrames
   1228 =====================
   1229 */
   1230 int idAnimBlend::NumFrames() const {
   1231 	const idAnim *anim = Anim();
   1232 	if ( !anim ) {
   1233 		return 0;
   1234 	}
   1235 
   1236 	return anim->NumFrames();
   1237 }
   1238 
   1239 /*
   1240 =====================
   1241 idAnimBlend::Length
   1242 =====================
   1243 */
   1244 int	idAnimBlend::Length() const {
   1245 	const idAnim *anim = Anim();
   1246 	if ( !anim ) {
   1247 		return 0;
   1248 	}
   1249 
   1250 	return anim->Length();
   1251 }
   1252 
   1253 /*
   1254 =====================
   1255 idAnimBlend::GetWeight
   1256 =====================
   1257 */
   1258 float idAnimBlend::GetWeight( int currentTime ) const {
   1259 	int		timeDelta;
   1260 	float	frac;
   1261 	float	w;
   1262 
   1263 	timeDelta = currentTime - blendStartTime;
   1264 	if ( timeDelta <= 0 ) {
   1265 		w = blendStartValue;
   1266 	} else if ( timeDelta >= blendDuration ) {
   1267 		w = blendEndValue;
   1268 	} else {
   1269 		frac = ( float )timeDelta / ( float )blendDuration;
   1270 		w = blendStartValue + ( blendEndValue - blendStartValue ) * frac;
   1271 	}
   1272 
   1273 	return w;
   1274 }
   1275 
   1276 /*
   1277 =====================
   1278 idAnimBlend::GetFinalWeight
   1279 =====================
   1280 */
   1281 float idAnimBlend::GetFinalWeight() const {
   1282 	return blendEndValue;
   1283 }
   1284 
   1285 /*
   1286 =====================
   1287 idAnimBlend::SetWeight
   1288 =====================
   1289 */
   1290 void idAnimBlend::SetWeight( float newweight, int currentTime, int blendTime ) {
   1291 	blendStartValue = GetWeight( currentTime );
   1292 	blendEndValue = newweight;
   1293     blendStartTime = currentTime - 1;
   1294 	blendDuration = blendTime;
   1295 
   1296 	if ( !newweight ) {
   1297 		endtime = currentTime + blendTime;
   1298 	}
   1299 }
   1300 
   1301 /*
   1302 =====================
   1303 idAnimBlend::NumSyncedAnims
   1304 =====================
   1305 */
   1306 int idAnimBlend::NumSyncedAnims() const {
   1307 	const idAnim *anim = Anim();
   1308 	if ( !anim ) {
   1309 		return 0;
   1310 	}
   1311 
   1312 	return anim->NumAnims();
   1313 }
   1314 
   1315 /*
   1316 =====================
   1317 idAnimBlend::SetSyncedAnimWeight
   1318 =====================
   1319 */
   1320 bool idAnimBlend::SetSyncedAnimWeight( int num, float weight ) {
   1321 	const idAnim *anim = Anim();
   1322 	if ( !anim ) {
   1323 		return false;
   1324 	}
   1325 
   1326 	if ( ( num < 0 ) || ( num > anim->NumAnims() ) ) {
   1327 		return false;
   1328 	}
   1329 
   1330 	animWeights[ num ] = weight;
   1331 	return true;
   1332 }
   1333 
   1334 /*
   1335 =====================
   1336 idAnimBlend::SetFrame
   1337 =====================
   1338 */
   1339 void idAnimBlend::SetFrame( const idDeclModelDef *modelDef, int _animNum, int _frame, int currentTime, int blendTime ) {
   1340 	Reset( modelDef );
   1341 	if ( !modelDef ) {
   1342 		return;
   1343 	}
   1344 	
   1345 	const idAnim *_anim = modelDef->GetAnim( _animNum );
   1346 	if ( !_anim ) {
   1347 		return;
   1348 	}
   1349 
   1350 	const idMD5Anim *md5anim = _anim->MD5Anim( 0 );
   1351 	if ( modelDef->Joints().Num() != md5anim->NumJoints() ) {
   1352 		gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", modelDef->GetModelName(), md5anim->Name() );
   1353 		return;
   1354 	}
   1355 	
   1356 	animNum				= _animNum;
   1357 	starttime			= currentTime;
   1358 	endtime				= -1;
   1359 	cycle				= -1;
   1360 	animWeights[ 0 ]	= 1.0f;
   1361 	frame				= _frame;
   1362 
   1363 	// a frame of 0 means it's not a single frame blend, so we set it to frame + 1
   1364 	if ( frame <= 0 ) {
   1365 		frame = 1;
   1366 	} else if ( frame > _anim->NumFrames() ) {
   1367 		frame = _anim->NumFrames();
   1368 	}
   1369 
   1370 	// set up blend
   1371 	blendEndValue		= 1.0f;
   1372 	blendStartTime		= currentTime - 1;
   1373 	blendDuration		= blendTime;
   1374 	blendStartValue		= 0.0f;
   1375 }
   1376 
   1377 /*
   1378 =====================
   1379 idAnimBlend::CycleAnim
   1380 =====================
   1381 */
   1382 void idAnimBlend::CycleAnim( const idDeclModelDef *modelDef, int _animNum, int currentTime, int blendTime ) {
   1383 	Reset( modelDef );
   1384 	if ( !modelDef ) {
   1385 		return;
   1386 	}
   1387 	
   1388 	const idAnim *_anim = modelDef->GetAnim( _animNum );
   1389 	if ( !_anim ) {
   1390 		return;
   1391 	}
   1392 
   1393 	const idMD5Anim *md5anim = _anim->MD5Anim( 0 );
   1394 	if ( modelDef->Joints().Num() != md5anim->NumJoints() ) {
   1395 		gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", modelDef->GetModelName(), md5anim->Name() );
   1396 		return;
   1397 	}
   1398 
   1399 	animNum				= _animNum;
   1400 	animWeights[ 0 ]	= 1.0f;
   1401 	endtime				= -1;
   1402 	cycle				= -1;
   1403 	if ( _anim->GetAnimFlags().random_cycle_start ) {
   1404 		// start the animation at a random time so that characters don't walk in sync
   1405 		starttime = currentTime - gameLocal.random.RandomFloat() * _anim->Length();
   1406 	} else {
   1407 		starttime = currentTime;
   1408 	}
   1409 
   1410 	// set up blend
   1411 	blendEndValue		= 1.0f;
   1412 	blendStartTime		= currentTime - 1;
   1413 	blendDuration		= blendTime;
   1414 	blendStartValue		= 0.0f;
   1415 }
   1416 
   1417 /*
   1418 =====================
   1419 idAnimBlend::PlayAnim
   1420 =====================
   1421 */
   1422 void idAnimBlend::PlayAnim( const idDeclModelDef *modelDef, int _animNum, int currentTime, int blendTime ) {
   1423 	Reset( modelDef );
   1424 	if ( !modelDef ) {
   1425 		return;
   1426 	}
   1427 	
   1428 	const idAnim *_anim = modelDef->GetAnim( _animNum );
   1429 	if ( !_anim ) {
   1430 		return;
   1431 	}
   1432 
   1433 	const idMD5Anim *md5anim = _anim->MD5Anim( 0 );
   1434 	if ( modelDef->Joints().Num() != md5anim->NumJoints() ) {
   1435 		gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", modelDef->GetModelName(), md5anim->Name() );
   1436 		return;
   1437 	}
   1438 
   1439 	animNum				= _animNum;
   1440 	starttime			= currentTime;
   1441 	endtime				= starttime + _anim->Length();
   1442 	cycle				= 1;
   1443 	animWeights[ 0 ]	= 1.0f;
   1444 
   1445 	// set up blend
   1446 	blendEndValue		= 1.0f;
   1447 	blendStartTime		= currentTime - 1;
   1448 	blendDuration		= blendTime;
   1449 	blendStartValue		= 0.0f;
   1450 }
   1451 
   1452 /*
   1453 =====================
   1454 idAnimBlend::Clear
   1455 =====================
   1456 */
   1457 void idAnimBlend::Clear( int currentTime, int clearTime ) {
   1458 	if ( !clearTime ) {
   1459 		Reset( modelDef );
   1460 	} else {
   1461 		SetWeight( 0.0f, currentTime, clearTime );
   1462 	}
   1463 }
   1464 
   1465 /*
   1466 =====================
   1467 idAnimBlend::IsDone
   1468 =====================
   1469 */
   1470 bool idAnimBlend::IsDone( int currentTime ) const {
   1471 	if ( !frame && ( endtime > 0 ) && ( currentTime >= endtime ) ) {
   1472 		return true;
   1473 	}
   1474 
   1475 	if ( ( blendEndValue <= 0.0f ) && ( currentTime >= ( blendStartTime + blendDuration ) ) ) {
   1476 		return true;
   1477 	}
   1478 
   1479 	return false;
   1480 }
   1481 
   1482 /*
   1483 =====================
   1484 idAnimBlend::FrameHasChanged
   1485 =====================
   1486 */
   1487 bool idAnimBlend::FrameHasChanged( int currentTime ) const {
   1488 	// if we don't have an anim, no change
   1489 	if ( !animNum ) {
   1490 		return false;
   1491 	}
   1492 
   1493 	// if anim is done playing, no change
   1494 	if ( ( endtime > 0 ) && ( currentTime > endtime ) ) {
   1495 		return false;
   1496 	}
   1497 
   1498 	// if our blend weight changes, we need to update
   1499 	if ( ( currentTime < ( blendStartTime + blendDuration ) && ( blendStartValue != blendEndValue ) ) ) {
   1500 		return true;
   1501 	}
   1502 
   1503 	// if we're a single frame anim and this isn't the frame we started on, we don't need to update
   1504 	if ( ( frame || ( NumFrames() == 1 ) ) && ( currentTime != starttime ) ) {
   1505 		return false;
   1506 	}
   1507 
   1508 	return true;
   1509 }
   1510 
   1511 /*
   1512 =====================
   1513 idAnimBlend::GetCycleCount
   1514 =====================
   1515 */
   1516 int idAnimBlend::GetCycleCount() const {
   1517 	return cycle;
   1518 }
   1519 
   1520 /*
   1521 =====================
   1522 idAnimBlend::SetCycleCount
   1523 =====================
   1524 */
   1525 void idAnimBlend::SetCycleCount( int count ) {
   1526 	const idAnim *anim = Anim();
   1527 
   1528 	if ( !anim ) {
   1529 		cycle = -1;
   1530 		endtime = 0;
   1531 	} else {
   1532 		cycle = count;
   1533 		if ( cycle < 0 ) {
   1534 			cycle = -1;
   1535 			endtime	= -1;
   1536 		} else if ( cycle == 0 ) {
   1537 			cycle = 1;
   1538 
   1539 			// most of the time we're running at the original frame rate, so avoid the int-to-float-to-int conversion
   1540 			if ( rate == 1.0f ) {
   1541 				endtime	= starttime - timeOffset + anim->Length();
   1542 			} else if ( rate != 0.0f ) {
   1543 				endtime	= starttime - timeOffset + anim->Length() / rate;
   1544 			} else {
   1545 				endtime = -1;
   1546 			}
   1547 		} else {
   1548 			// most of the time we're running at the original frame rate, so avoid the int-to-float-to-int conversion
   1549 			if ( rate == 1.0f ) {
   1550 				endtime	= starttime - timeOffset + anim->Length() * cycle;
   1551 			} else if ( rate != 0.0f ) {
   1552 				endtime	= starttime - timeOffset + ( anim->Length() * cycle ) / rate;
   1553 			} else {
   1554 				endtime = -1;
   1555 			}
   1556 		}
   1557 	}
   1558 }
   1559 
   1560 /*
   1561 =====================
   1562 idAnimBlend::SetPlaybackRate
   1563 =====================
   1564 */
   1565 void idAnimBlend::SetPlaybackRate( int currentTime, float newRate ) {
   1566 	int animTime;
   1567 
   1568 	if ( rate == newRate ) {
   1569 		return;
   1570 	}
   1571 
   1572 	animTime = AnimTime( currentTime );
   1573 	if ( newRate == 1.0f ) {
   1574 		timeOffset = animTime - ( currentTime - starttime );
   1575 	} else {
   1576 		timeOffset = animTime - ( currentTime - starttime ) * newRate;
   1577 	}
   1578 
   1579 	rate = newRate;
   1580 
   1581 	// update the anim endtime
   1582 	SetCycleCount( cycle );
   1583 }
   1584 
   1585 /*
   1586 =====================
   1587 idAnimBlend::GetPlaybackRate
   1588 =====================
   1589 */
   1590 float idAnimBlend::GetPlaybackRate() const {
   1591 	return rate;
   1592 }
   1593 
   1594 /*
   1595 =====================
   1596 idAnimBlend::SetStartTime
   1597 =====================
   1598 */
   1599 void idAnimBlend::SetStartTime( int _startTime ) {
   1600 	starttime = _startTime;
   1601 
   1602 	// update the anim endtime
   1603 	SetCycleCount( cycle );
   1604 }
   1605 
   1606 /*
   1607 =====================
   1608 idAnimBlend::GetStartTime
   1609 =====================
   1610 */
   1611 int idAnimBlend::GetStartTime() const {
   1612 	if ( !animNum ) {
   1613 		return 0;
   1614 	}
   1615 
   1616 	return starttime;
   1617 }
   1618 
   1619 /*
   1620 =====================
   1621 idAnimBlend::GetEndTime
   1622 =====================
   1623 */
   1624 int idAnimBlend::GetEndTime() const {
   1625 	if ( !animNum ) {
   1626 		return 0;
   1627 	}
   1628 
   1629 	return endtime;
   1630 }
   1631 
   1632 /*
   1633 =====================
   1634 idAnimBlend::PlayLength
   1635 =====================
   1636 */
   1637 int idAnimBlend::PlayLength() const {
   1638 	if ( !animNum ) {
   1639 		return 0;
   1640 	}
   1641 
   1642 	if ( endtime < 0 ) {
   1643 		return -1;
   1644 	}
   1645 
   1646 	return endtime - starttime + timeOffset;
   1647 }
   1648 
   1649 /*
   1650 =====================
   1651 idAnimBlend::AllowMovement
   1652 =====================
   1653 */
   1654 void idAnimBlend::AllowMovement( bool allow ) {
   1655 	allowMove = allow;
   1656 }
   1657 
   1658 /*
   1659 =====================
   1660 idAnimBlend::AllowFrameCommands
   1661 =====================
   1662 */
   1663 void idAnimBlend::AllowFrameCommands( bool allow ) {
   1664 	allowFrameCommands = allow;
   1665 }
   1666 
   1667 
   1668 /*
   1669 =====================
   1670 idAnimBlend::Anim
   1671 =====================
   1672 */
   1673 const idAnim *idAnimBlend::Anim() const {
   1674 	if ( !modelDef ) {
   1675 		return NULL;
   1676 	}
   1677 
   1678 	const idAnim *anim = modelDef->GetAnim( animNum );
   1679 	return anim;
   1680 }
   1681 
   1682 /*
   1683 =====================
   1684 idAnimBlend::AnimNum
   1685 =====================
   1686 */
   1687 int idAnimBlend::AnimNum() const {
   1688 	return animNum;
   1689 }
   1690 
   1691 /*
   1692 =====================
   1693 idAnimBlend::AnimTime
   1694 =====================
   1695 */
   1696 int idAnimBlend::AnimTime( int currentTime ) const {
   1697 	int time;
   1698 	int length;
   1699 	const idAnim *anim = Anim();
   1700 
   1701 	if ( anim ) {
   1702 		if ( frame ) {
   1703 			return FRAME2MS( frame - 1 );
   1704 		}
   1705 
   1706 		// most of the time we're running at the original frame rate, so avoid the int-to-float-to-int conversion
   1707 		if ( rate == 1.0f ) {
   1708 			time = currentTime - starttime + timeOffset;
   1709 		} else {
   1710 			time = static_cast<int>( ( currentTime - starttime ) * rate ) + timeOffset;
   1711 		}
   1712 
   1713 		// given enough time, we can easily wrap time around in our frame calculations, so
   1714 		// keep cycling animations' time within the length of the anim.
   1715 		length = anim->Length();
   1716 		if ( ( cycle < 0 ) && ( length > 0 ) ) {
   1717 			time %= length;
   1718 
   1719 			// time will wrap after 24 days (oh no!), resulting in negative results for the %.
   1720 			// adding the length gives us the proper result.
   1721 			if ( time < 0 ) {
   1722 				time += length;
   1723 			}
   1724 		}
   1725 		return time;
   1726 	} else {
   1727 		return 0;
   1728 	}
   1729 }
   1730 
   1731 /*
   1732 =====================
   1733 idAnimBlend::GetFrameNumber
   1734 =====================
   1735 */
   1736 int idAnimBlend::GetFrameNumber( int currentTime ) const {
   1737 	const idMD5Anim	*md5anim;
   1738 	frameBlend_t	frameinfo;
   1739 	int				animTime;
   1740 
   1741 	const idAnim *anim = Anim();
   1742 	if ( !anim ) {
   1743 		return 1;
   1744 	}
   1745 
   1746 	if ( frame ) {
   1747 		return frame;
   1748 	}
   1749 
   1750 	md5anim = anim->MD5Anim( 0 );
   1751 	animTime = AnimTime( currentTime );
   1752 	md5anim->ConvertTimeToFrame( animTime, cycle, frameinfo );
   1753 
   1754 	return frameinfo.frame1 + 1;
   1755 }
   1756 
   1757 /*
   1758 =====================
   1759 idAnimBlend::CallFrameCommands
   1760 =====================
   1761 */
   1762 void idAnimBlend::CallFrameCommands( idEntity *ent, int fromtime, int totime ) const {
   1763 	const idMD5Anim	*md5anim;
   1764 	frameBlend_t	frame1;
   1765 	frameBlend_t	frame2;
   1766 	int				fromFrameTime;
   1767 	int				toFrameTime;
   1768 
   1769 	if ( !allowFrameCommands || !ent || frame || ( ( endtime > 0 ) && ( fromtime > endtime ) ) ) {
   1770 		return;
   1771 	}
   1772 
   1773 	const idAnim *anim = Anim();
   1774 	if ( !anim || !anim->HasFrameCommands() ) {
   1775 		return;
   1776 	}
   1777 
   1778 	if ( totime <= starttime ) {
   1779 		// don't play until next frame or we'll play commands twice.
   1780 		// this happens on the player sometimes.
   1781 		return;
   1782 	}
   1783 
   1784 	fromFrameTime	= AnimTime( fromtime );
   1785 	toFrameTime		= AnimTime( totime );
   1786 	if ( toFrameTime < fromFrameTime ) {
   1787 		toFrameTime += anim->Length();
   1788 	}
   1789 
   1790 	md5anim = anim->MD5Anim( 0 );
   1791 	md5anim->ConvertTimeToFrame( fromFrameTime, cycle, frame1 );
   1792 	md5anim->ConvertTimeToFrame( toFrameTime, cycle, frame2 );
   1793 
   1794 	if ( fromFrameTime <= 0 ) {
   1795 		// make sure first frame is called
   1796 		anim->CallFrameCommands( ent, -1, frame2.frame1 );
   1797 	} else {
   1798 		anim->CallFrameCommands( ent, frame1.frame1, frame2.frame1 );
   1799 	}
   1800 }
   1801 
   1802 /*
   1803 =====================
   1804 idAnimBlend::BlendAnim
   1805 =====================
   1806 */
   1807 bool idAnimBlend::BlendAnim( int currentTime, int channel, int numJoints, idJointQuat *blendFrame, float &blendWeight, bool removeOriginOffset, bool overrideBlend, bool printInfo ) const {
   1808 	int				i;
   1809 	float			lerp;
   1810 	float			mixWeight;
   1811 	const idMD5Anim	*md5anim;
   1812 	idJointQuat		*ptr;
   1813 	frameBlend_t	frametime = { 0 };
   1814 	idJointQuat		*jointFrame;
   1815 	idJointQuat		*mixFrame;
   1816 	int				numAnims;
   1817 	int				time;
   1818 
   1819 	const idAnim *anim = Anim();
   1820 	if ( !anim ) {
   1821 		return false;
   1822 	}
   1823 
   1824 	float weight = GetWeight( currentTime );
   1825 	if ( blendWeight > 0.0f ) {
   1826 		if ( ( endtime >= 0 ) && ( currentTime >= endtime ) ) {
   1827 			return false;
   1828 		}
   1829 		if ( !weight ) {
   1830 			return false;
   1831 		}
   1832 		if ( overrideBlend ) {
   1833 			blendWeight = 1.0f - weight;
   1834 		}
   1835 	}
   1836 
   1837 	if ( ( channel == ANIMCHANNEL_ALL ) && !blendWeight ) {
   1838 		// we don't need a temporary buffer, so just store it directly in the blend frame
   1839 		jointFrame = blendFrame;
   1840 	} else {
   1841 		// allocate a temporary buffer to copy the joints from
   1842 		jointFrame = ( idJointQuat * )_alloca16( numJoints * sizeof( *jointFrame ) );
   1843 	}
   1844 
   1845 	time = AnimTime( currentTime );
   1846 
   1847 	numAnims = anim->NumAnims();
   1848 	if ( numAnims == 1 ) {
   1849 		md5anim = anim->MD5Anim( 0 );
   1850 		if ( frame ) {
   1851 			md5anim->GetSingleFrame( frame - 1, jointFrame, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) );
   1852 		} else {
   1853 			md5anim->ConvertTimeToFrame( time, cycle, frametime );
   1854 			md5anim->GetInterpolatedFrame( frametime, jointFrame, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) );
   1855 		}
   1856 	} else {
   1857 		//
   1858 		// need to mix the multipoint anim together first
   1859 		//
   1860 		// allocate a temporary buffer to copy the joints to
   1861 		mixFrame = ( idJointQuat * )_alloca16( numJoints * sizeof( *jointFrame ) );
   1862 
   1863 		if ( !frame ) {
   1864 			anim->MD5Anim( 0 )->ConvertTimeToFrame( time, cycle, frametime );
   1865 		}
   1866 
   1867 		ptr = jointFrame;
   1868 		mixWeight = 0.0f;
   1869 		for( i = 0; i < numAnims; i++ ) {
   1870 			if ( animWeights[ i ] > 0.0f ) {
   1871 				mixWeight += animWeights[ i ];
   1872 				lerp = animWeights[ i ] / mixWeight;
   1873 				md5anim = anim->MD5Anim( i );
   1874 				if ( frame ) {
   1875 					md5anim->GetSingleFrame( frame - 1, ptr, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) );
   1876 				} else {
   1877 					md5anim->GetInterpolatedFrame( frametime, ptr, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) );
   1878 				}
   1879 
   1880 				// only blend after the first anim is mixed in
   1881 				if ( ptr != jointFrame ) {
   1882 					SIMDProcessor->BlendJoints( jointFrame, ptr, lerp, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) );
   1883 				}
   1884 
   1885 				ptr = mixFrame;
   1886 			}
   1887 		}
   1888 
   1889 		if ( !mixWeight ) {
   1890 			return false;
   1891 		}
   1892 	}
   1893 
   1894 	if ( removeOriginOffset ) {
   1895 		if ( allowMove ) {
   1896 #ifdef VELOCITY_MOVE
   1897 			jointFrame[ 0 ].t.x = 0.0f;
   1898 #else
   1899 			jointFrame[ 0 ].t.Zero();
   1900 #endif
   1901 		}
   1902 
   1903 		if ( anim->GetAnimFlags().anim_turn ) {
   1904 			jointFrame[ 0 ].q.Set( -0.70710677f, 0.0f, 0.0f, 0.70710677f );
   1905 		}
   1906 	}
   1907 
   1908 	if ( !blendWeight ) {
   1909 		blendWeight = weight;
   1910 		if ( channel != ANIMCHANNEL_ALL ) {
   1911 			const int *index = modelDef->GetChannelJoints( channel );
   1912 			const int num = modelDef->NumJointsOnChannel( channel );
   1913 			for( i = 0; i < num; i++ ) {
   1914 				int j = index[i];
   1915 				blendFrame[j].t = jointFrame[j].t;
   1916 				blendFrame[j].q = jointFrame[j].q;
   1917 			}
   1918 		}
   1919     } else {
   1920 		blendWeight += weight;
   1921 		lerp = weight / blendWeight;
   1922 		SIMDProcessor->BlendJoints( blendFrame, jointFrame, lerp, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) );
   1923 	}
   1924 
   1925 	if ( printInfo ) {
   1926 		if ( frame ) {
   1927 			gameLocal.Printf( "  %s: '%s', %d, %.2f%%\n", channelNames[ channel ], anim->FullName(), frame, weight * 100.0f );
   1928 		} else {
   1929 			gameLocal.Printf( "  %s: '%s', %.3f, %.2f%%\n", channelNames[ channel ], anim->FullName(), ( float )frametime.frame1 + frametime.backlerp, weight * 100.0f );
   1930 		}
   1931 	}
   1932 
   1933 	return true;
   1934 }
   1935 
   1936 /*
   1937 =====================
   1938 idAnimBlend::BlendOrigin
   1939 =====================
   1940 */
   1941 void idAnimBlend::BlendOrigin( int currentTime, idVec3 &blendPos, float &blendWeight, bool removeOriginOffset ) const {
   1942 	float	lerp;
   1943 	idVec3	animpos;
   1944 	idVec3	pos;
   1945 	int		time;
   1946 	int		num;
   1947 	int		i;
   1948 
   1949 	if ( frame || ( ( endtime > 0 ) && ( currentTime > endtime ) ) ) {
   1950 		return;
   1951 	}
   1952 
   1953 	const idAnim *anim = Anim();
   1954 	if ( !anim ) {
   1955 		return;
   1956 	}
   1957 
   1958 	if ( allowMove && removeOriginOffset ) {
   1959 		return;
   1960 	}
   1961 
   1962 	float weight = GetWeight( currentTime );
   1963 	if ( !weight ) {
   1964 		return;
   1965 	}
   1966 
   1967 	time = AnimTime( currentTime );
   1968 
   1969 	pos.Zero();
   1970 	num = anim->NumAnims();
   1971 	for( i = 0; i < num; i++ ) {
   1972 		anim->GetOrigin( animpos, i, time, cycle );
   1973 		pos += animpos * animWeights[ i ];
   1974 	}
   1975 
   1976 	if ( !blendWeight ) {
   1977 		blendPos = pos;
   1978 		blendWeight = weight;
   1979 	} else {
   1980 		lerp = weight / ( blendWeight + weight );
   1981 		blendPos += lerp * ( pos - blendPos );
   1982 		blendWeight += weight;
   1983 	}
   1984 }
   1985 
   1986 /*
   1987 =====================
   1988 idAnimBlend::BlendDelta
   1989 =====================
   1990 */
   1991 void idAnimBlend::BlendDelta( int fromtime, int totime, idVec3 &blendDelta, float &blendWeight ) const {
   1992 	idVec3	pos1;
   1993 	idVec3	pos2;
   1994 	idVec3	animpos;
   1995 	idVec3	delta;
   1996 	int		time1;
   1997 	int		time2;
   1998 	float	lerp;
   1999 	int		num;
   2000 	int		i;
   2001 	
   2002 	if ( frame || !allowMove || ( ( endtime > 0 ) && ( fromtime > endtime ) ) ) {
   2003 		return;
   2004 	}
   2005 
   2006 	const idAnim *anim = Anim();
   2007 	if ( !anim ) {
   2008 		return;
   2009 	}
   2010 
   2011 	float weight = GetWeight( totime );
   2012 	if ( !weight ) {
   2013 		return;
   2014 	}
   2015 
   2016 	time1 = AnimTime( fromtime );
   2017 	time2 = AnimTime( totime );
   2018 	if ( time2 < time1 ) {
   2019 		time2 += anim->Length();
   2020 	}
   2021 
   2022 	num = anim->NumAnims();
   2023 
   2024 	pos1.Zero();
   2025 	pos2.Zero();
   2026 	for( i = 0; i < num; i++ ) {
   2027 		anim->GetOrigin( animpos, i, time1, cycle );
   2028 		pos1 += animpos * animWeights[ i ];
   2029 
   2030 		anim->GetOrigin( animpos, i, time2, cycle );
   2031 		pos2 += animpos * animWeights[ i ];
   2032 	}
   2033 
   2034 	delta = pos2 - pos1;
   2035 	if ( !blendWeight ) {
   2036 		blendDelta = delta;
   2037 		blendWeight = weight;
   2038 	} else {
   2039 		lerp = weight / ( blendWeight + weight );
   2040 		blendDelta += lerp * ( delta - blendDelta );
   2041 		blendWeight += weight;
   2042 	}
   2043 }
   2044 
   2045 /*
   2046 =====================
   2047 idAnimBlend::BlendDeltaRotation
   2048 =====================
   2049 */
   2050 void idAnimBlend::BlendDeltaRotation( int fromtime, int totime, idQuat &blendDelta, float &blendWeight ) const {
   2051 	idQuat	q1;
   2052 	idQuat	q2;
   2053 	idQuat	q3;
   2054 	int		time1;
   2055 	int		time2;
   2056 	float	lerp;
   2057 	float	mixWeight;
   2058 	int		num;
   2059 	int		i;
   2060 	
   2061 	if ( frame || !allowMove || ( ( endtime > 0 ) && ( fromtime > endtime ) ) ) {
   2062 		return;
   2063 	}
   2064 
   2065 	const idAnim *anim = Anim();
   2066 	if ( !anim || !anim->GetAnimFlags().anim_turn ) {
   2067 		return;
   2068 	}
   2069 
   2070 	float weight = GetWeight( totime );
   2071 	if ( !weight ) {
   2072 		return;
   2073 	}
   2074 
   2075 	time1 = AnimTime( fromtime );
   2076 	time2 = AnimTime( totime );
   2077 	if ( time2 < time1 ) {
   2078 		time2 += anim->Length();
   2079 	}
   2080 
   2081 	q1.Set( 0.0f, 0.0f, 0.0f, 1.0f );
   2082 	q2.Set( 0.0f, 0.0f, 0.0f, 1.0f );
   2083 
   2084 	mixWeight = 0.0f;
   2085 	num = anim->NumAnims();
   2086 	for( i = 0; i < num; i++ ) {
   2087 		if ( animWeights[ i ] > 0.0f ) {
   2088 			mixWeight += animWeights[ i ];
   2089 			if ( animWeights[ i ] == mixWeight ) {
   2090 				anim->GetOriginRotation( q1, i, time1, cycle );
   2091 				anim->GetOriginRotation( q2, i, time2, cycle );
   2092 			} else {
   2093 				lerp = animWeights[ i ] / mixWeight;
   2094 				anim->GetOriginRotation( q3, i, time1, cycle );
   2095 				q1.Slerp( q1, q3, lerp );
   2096 
   2097 				anim->GetOriginRotation( q3, i, time2, cycle );
   2098 				q2.Slerp( q1, q3, lerp );
   2099 			}
   2100 		}
   2101 	}
   2102 
   2103 	q3 = q1.Inverse() * q2;
   2104 	if ( !blendWeight ) {
   2105 		blendDelta = q3;
   2106 		blendWeight = weight;
   2107 	} else {
   2108 		lerp = weight / ( blendWeight + weight );
   2109 		blendDelta.Slerp( blendDelta, q3, lerp );
   2110 		blendWeight += weight;
   2111 	}
   2112 }
   2113 
   2114 /*
   2115 =====================
   2116 idAnimBlend::AddBounds
   2117 =====================
   2118 */
   2119 bool idAnimBlend::AddBounds( int currentTime, idBounds &bounds, bool removeOriginOffset ) const {
   2120 	int			i;
   2121 	int			num;
   2122 	idBounds	b;
   2123 	int			time;
   2124 	idVec3		pos;
   2125 	bool		addorigin;
   2126 
   2127 	if ( ( endtime > 0 ) && ( currentTime > endtime ) ) {
   2128 		return false;
   2129 	}
   2130 
   2131 	const idAnim *anim = Anim();
   2132 	if ( !anim ) {
   2133 		return false;
   2134 	}
   2135 
   2136 	float weight = GetWeight( currentTime );
   2137 	if ( !weight ) {
   2138 		return false;
   2139 	}
   2140 
   2141 	time = AnimTime( currentTime );
   2142 	num = anim->NumAnims();
   2143 	
   2144 	addorigin = !allowMove || !removeOriginOffset;
   2145 	for( i = 0; i < num; i++ ) {
   2146 		if ( anim->GetBounds( b, i, time, cycle ) ) {
   2147 			if ( addorigin ) {
   2148 				anim->GetOrigin( pos, i, time, cycle );
   2149 				b.TranslateSelf( pos );
   2150 			}
   2151 			bounds.AddBounds( b );
   2152 		}
   2153 	}
   2154 
   2155 	return true;
   2156 }
   2157 
   2158 /***********************************************************************
   2159 
   2160 	idDeclModelDef
   2161 
   2162 ***********************************************************************/
   2163 
   2164 /*
   2165 =====================
   2166 idDeclModelDef::idDeclModelDef
   2167 =====================
   2168 */
   2169 idDeclModelDef::idDeclModelDef() {
   2170 	modelHandle	= NULL;
   2171 	skin		= NULL;
   2172 	offset.Zero();
   2173 	for ( int i = 0; i < ANIM_NumAnimChannels; i++ ) {
   2174 		channelJoints[i].Clear();
   2175 	}
   2176 }
   2177 
   2178 /*
   2179 =====================
   2180 idDeclModelDef::~idDeclModelDef
   2181 =====================
   2182 */
   2183 idDeclModelDef::~idDeclModelDef() {
   2184 	FreeData();
   2185 }
   2186 
   2187 /*
   2188 =================
   2189 idDeclModelDef::Size
   2190 =================
   2191 */
   2192 size_t idDeclModelDef::Size() const {
   2193 	return sizeof( idDeclModelDef );
   2194 }
   2195 
   2196 /*
   2197 =====================
   2198 idDeclModelDef::CopyDecl
   2199 =====================
   2200 */
   2201 void idDeclModelDef::CopyDecl( const idDeclModelDef *decl ) {
   2202 	int i;
   2203 
   2204 	FreeData();
   2205 
   2206 	offset = decl->offset;
   2207 	modelHandle = decl->modelHandle;
   2208 	skin = decl->skin;
   2209 
   2210 	anims.SetNum( decl->anims.Num() );
   2211 	for( i = 0; i < anims.Num(); i++ ) {
   2212 		anims[ i ] = new (TAG_ANIM) idAnim( this, decl->anims[ i ] );
   2213 	}
   2214 
   2215 	joints.SetNum( decl->joints.Num() );
   2216 	memcpy( joints.Ptr(), decl->joints.Ptr(), decl->joints.Num() * sizeof( joints[0] ) );
   2217 	jointParents.SetNum( decl->jointParents.Num() );
   2218 	memcpy( jointParents.Ptr(), decl->jointParents.Ptr(), decl->jointParents.Num() * sizeof( jointParents[0] ) );
   2219 	for ( i = 0; i < ANIM_NumAnimChannels; i++ ) {
   2220 		channelJoints[i] = decl->channelJoints[i];
   2221 	}
   2222 }
   2223 
   2224 /*
   2225 =====================
   2226 idDeclModelDef::FreeData
   2227 =====================
   2228 */
   2229 void idDeclModelDef::FreeData() {
   2230 	anims.DeleteContents( true );
   2231 	joints.Clear();
   2232 	jointParents.Clear();
   2233 	modelHandle	= NULL;
   2234 	skin = NULL;
   2235 	offset.Zero();
   2236 	for ( int i = 0; i < ANIM_NumAnimChannels; i++ ) {
   2237 		channelJoints[i].Clear();
   2238 	}
   2239 }
   2240 
   2241 /*
   2242 ================
   2243 idDeclModelDef::DefaultDefinition
   2244 ================
   2245 */
   2246 const char *idDeclModelDef::DefaultDefinition() const {
   2247 	return "{ }";
   2248 }
   2249 
   2250 /*
   2251 ====================
   2252 idDeclModelDef::FindJoint
   2253 ====================
   2254 */
   2255 const jointInfo_t *idDeclModelDef::FindJoint( const char *name ) const {
   2256 	int					i;
   2257 	const idMD5Joint	*joint;
   2258 
   2259 	if ( !modelHandle ) {
   2260 		return NULL;
   2261 	}
   2262 	
   2263 	joint = modelHandle->GetJoints();
   2264 	for( i = 0; i < joints.Num(); i++, joint++ ) {
   2265 		if ( !joint->name.Icmp( name ) ) {
   2266 			return &joints[ i ];
   2267 		}
   2268 	}
   2269 
   2270 	return NULL;
   2271 }
   2272 
   2273 /*
   2274 =====================
   2275 idDeclModelDef::ModelHandle
   2276 =====================
   2277 */
   2278 idRenderModel *idDeclModelDef::ModelHandle() const {
   2279 	return ( idRenderModel * )modelHandle;
   2280 }
   2281 
   2282 /*
   2283 =====================
   2284 idDeclModelDef::GetJointList
   2285 =====================
   2286 */
   2287 void idDeclModelDef::GetJointList( const char *jointnames, idList<jointHandle_t> &jointList ) const {
   2288 	const char			*pos;
   2289 	idStr				jointname;
   2290 	const jointInfo_t	*joint;
   2291 	const jointInfo_t	*child;
   2292 	int					i;
   2293 	int					num;
   2294 	bool				getChildren;
   2295 	bool				subtract;
   2296 
   2297 	if ( !modelHandle ) {
   2298 		return;
   2299 	}
   2300 
   2301 	jointList.Clear();
   2302 
   2303 	num = modelHandle->NumJoints();
   2304 
   2305 	// scan through list of joints and add each to the joint list
   2306 	pos = jointnames;
   2307 	while( *pos ) {
   2308 		// skip over whitespace
   2309 		while( ( *pos != 0 ) && isspace( (unsigned char)*pos ) ) {
   2310 			pos++;
   2311 		}
   2312 
   2313 		if ( !*pos ) {
   2314 			// no more names
   2315 			break;
   2316 		}
   2317 
   2318 		// copy joint name
   2319 		jointname = "";
   2320 
   2321 		if ( *pos == '-' ) {
   2322 			subtract = true;
   2323 			pos++;
   2324 		} else {
   2325 			subtract = false;
   2326 		}
   2327 
   2328 		if ( *pos == '*' ) {
   2329 			getChildren = true;
   2330 			pos++;
   2331 		} else {
   2332 			getChildren = false;
   2333 		}
   2334 
   2335 		while( ( *pos != 0 ) && !isspace( (unsigned char)*pos ) ) {
   2336 			jointname += *pos;
   2337 			pos++;
   2338 		}
   2339 
   2340 		joint = FindJoint( jointname );
   2341 		if ( !joint ) {
   2342 			gameLocal.Warning( "Unknown joint '%s' in '%s' for model '%s'", jointname.c_str(), jointnames, GetName() );
   2343 			continue;
   2344 		}
   2345 
   2346 		if ( !subtract ) {
   2347 			jointList.AddUnique( joint->num );
   2348 		} else {
   2349 			jointList.Remove( joint->num );
   2350 		}
   2351 
   2352 		if ( getChildren ) {
   2353 			// include all joint's children
   2354 			child = joint + 1;
   2355 			for( i = joint->num + 1; i < num; i++, child++ ) {
   2356 				// all children of the joint should follow it in the list.
   2357 				// once we reach a joint without a parent or with a parent
   2358 				// who is earlier in the list than the specified joint, then
   2359 				// we've gone through all it's children.
   2360 				if ( child->parentNum < joint->num ) {
   2361 					break;
   2362 				}
   2363 
   2364 				if ( !subtract ) {
   2365 					jointList.AddUnique( child->num );
   2366 				} else {
   2367 					jointList.Remove( child->num );
   2368 				}
   2369 			}
   2370 		}
   2371 	}
   2372 }
   2373 
   2374 /*
   2375 =====================
   2376 idDeclModelDef::Touch
   2377 =====================
   2378 */
   2379 void idDeclModelDef::Touch() const {
   2380 	if ( modelHandle ) {
   2381 		renderModelManager->FindModel( modelHandle->Name() );
   2382 	}
   2383 }
   2384 
   2385 /*
   2386 =====================
   2387 idDeclModelDef::GetDefaultSkin
   2388 =====================
   2389 */
   2390 const idDeclSkin *idDeclModelDef::GetDefaultSkin() const {
   2391 	return skin;
   2392 }
   2393 
   2394 /*
   2395 =====================
   2396 idDeclModelDef::GetDefaultPose
   2397 =====================
   2398 */
   2399 const idJointQuat *idDeclModelDef::GetDefaultPose() const {
   2400 	return modelHandle->GetDefaultPose();
   2401 }
   2402 
   2403 /*
   2404 =====================
   2405 idDeclModelDef::SetupJoints
   2406 =====================
   2407 */
   2408 void idDeclModelDef::SetupJoints( int *numJoints, idJointMat **jointList, idBounds &frameBounds, bool removeOriginOffset ) const {
   2409 	int					num;
   2410 	const idJointQuat	*pose;
   2411 	idJointMat			*list;
   2412 
   2413 	if ( !modelHandle || modelHandle->IsDefaultModel() ) {
   2414 		Mem_Free16( (*jointList) );
   2415 		(*jointList) = NULL;
   2416 		frameBounds.Clear();
   2417 		return;
   2418 	}
   2419 
   2420 	// get the number of joints
   2421 	num = modelHandle->NumJoints();
   2422 
   2423 	if ( !num ) {
   2424 		gameLocal.Error( "model '%s' has no joints", modelHandle->Name() );
   2425 	}
   2426 
   2427 	// set up initial pose for model (with no pose, model is just a jumbled mess)
   2428 	list = (idJointMat *) Mem_Alloc16( SIMD_ROUND_JOINTS( num ) * sizeof( list[0] ), TAG_JOINTMAT );
   2429 	pose = GetDefaultPose();
   2430 
   2431 	// convert the joint quaternions to joint matrices
   2432 	SIMDProcessor->ConvertJointQuatsToJointMats( list, pose, joints.Num() );
   2433 
   2434 	// check if we offset the model by the origin joint
   2435 	if ( removeOriginOffset ) {
   2436 #ifdef VELOCITY_MOVE
   2437 		list[ 0 ].SetTranslation( idVec3( offset.x, offset.y + pose[0].t.y, offset.z + pose[0].t.z ) );
   2438 #else
   2439 		list[ 0 ].SetTranslation( offset );
   2440 #endif
   2441 	} else {
   2442 		list[ 0 ].SetTranslation( pose[0].t + offset );
   2443 	}
   2444 
   2445 	// transform the joint hierarchy
   2446 	SIMDProcessor->TransformJoints( list, jointParents.Ptr(), 1, joints.Num() - 1 );
   2447 
   2448 	SIMD_INIT_LAST_JOINT( list, num );
   2449 
   2450 	*numJoints = num;
   2451 	*jointList = list;
   2452 
   2453 	// get the bounds of the default pose
   2454 	frameBounds = modelHandle->Bounds( NULL );
   2455 }
   2456 
   2457 /*
   2458 =====================
   2459 idDeclModelDef::ParseAnim
   2460 =====================
   2461 */
   2462 bool idDeclModelDef::ParseAnim( idLexer &src, int numDefaultAnims ) {
   2463 	int				i;
   2464 	int				len;
   2465 	idAnim			*anim;
   2466 	const idMD5Anim	*md5anims[ ANIM_MaxSyncedAnims ];
   2467 	const idMD5Anim	*md5anim;
   2468 	idStr			alias;
   2469 	idToken			realname;
   2470 	idToken			token;
   2471 	int				numAnims;
   2472 	animFlags_t		flags;
   2473 
   2474 	numAnims = 0;
   2475 	memset( md5anims, 0, sizeof( md5anims ) );
   2476 
   2477 	if( !src.ReadToken( &realname ) ) {
   2478 		src.Warning( "Unexpected end of file" );
   2479 		MakeDefault();
   2480 		return false;
   2481 	}
   2482 	alias = realname;
   2483 
   2484 	for( i = 0; i < anims.Num(); i++ ) {
   2485 		if ( !strcmp( anims[ i ]->FullName(), realname ) ) {
   2486 			break;
   2487 		}
   2488 	}
   2489 
   2490 	if ( ( i < anims.Num() ) && ( i >= numDefaultAnims ) ) {
   2491 		src.Warning( "Duplicate anim '%s'", realname.c_str() );
   2492 		MakeDefault();
   2493 		return false;
   2494 	}
   2495 
   2496 	if ( i < numDefaultAnims ) {
   2497 		anim = anims[ i ];
   2498 	} else {
   2499 		// create the alias associated with this animation
   2500 		anim = new (TAG_ANIM) idAnim();
   2501 		anims.Append( anim );
   2502 	}
   2503 
   2504 	// random anims end with a number.  find the numeric suffix of the animation.
   2505 	len = alias.Length();
   2506 	for( i = len - 1; i > 0; i-- ) {
   2507 		if ( !isdigit( (unsigned char)alias[ i ] ) ) {
   2508 			break;
   2509 		}
   2510 	}
   2511 
   2512 	// check for zero length name, or a purely numeric name
   2513 	if ( i <= 0 ) {
   2514 		src.Warning( "Invalid animation name '%s'", alias.c_str() );
   2515 		MakeDefault();
   2516 		return false;
   2517 	}
   2518 
   2519 	// remove the numeric suffix
   2520 	alias.CapLength( i + 1 );
   2521 
   2522 	// parse the anims from the string
   2523 	do {
   2524 		if( !src.ReadToken( &token ) ) {
   2525 			src.Warning( "Unexpected end of file" );
   2526 			MakeDefault();
   2527 			return false;
   2528 		}
   2529 
   2530 		// lookup the animation
   2531 		md5anim = animationLib.GetAnim( token );
   2532 		if ( !md5anim ) {
   2533 			src.Warning( "Couldn't load anim '%s'", token.c_str() );
   2534 			MakeDefault();
   2535 			return false;
   2536 		}
   2537 
   2538 		md5anim->CheckModelHierarchy( modelHandle );
   2539 
   2540 		if ( numAnims > 0 ) {
   2541 			// make sure it's the same length as the other anims
   2542 			if ( md5anim->Length() != md5anims[ 0 ]->Length() ) {
   2543 				src.Warning( "Anim '%s' does not match length of anim '%s'", md5anim->Name(), md5anims[ 0 ]->Name() );
   2544 				MakeDefault();
   2545 				return false;
   2546 			}
   2547 		}
   2548 
   2549 		if ( numAnims >= ANIM_MaxSyncedAnims ) {
   2550 			src.Warning( "Exceeded max synced anims (%d)", ANIM_MaxSyncedAnims );
   2551 			MakeDefault();
   2552 			return false;
   2553 		}
   2554 
   2555 		// add it to our list
   2556 		md5anims[ numAnims ] = md5anim;
   2557 		numAnims++;
   2558 	} while ( src.CheckTokenString( "," ) );
   2559 
   2560 	if ( !numAnims ) {
   2561 		src.Warning( "No animation specified" );
   2562 		MakeDefault();
   2563 		return false;
   2564 	}
   2565 
   2566 	anim->SetAnim( this, realname, alias, numAnims, md5anims );
   2567 	memset( &flags, 0, sizeof( flags ) );
   2568 
   2569 	// parse any frame commands or animflags
   2570 	if ( src.CheckTokenString( "{" ) ) {
   2571 		while( 1 ) {
   2572 			if( !src.ReadToken( &token ) ) {
   2573 				src.Warning( "Unexpected end of file" );
   2574 				MakeDefault();
   2575 				return false;
   2576 			}
   2577 			if ( token == "}" ) {
   2578 				break;
   2579 			}else if ( token == "prevent_idle_override" ) {
   2580 				flags.prevent_idle_override = true;
   2581 			} else if ( token == "random_cycle_start" ) {
   2582 				flags.random_cycle_start = true;
   2583 			} else if ( token == "ai_no_turn" ) {
   2584 				flags.ai_no_turn = true;
   2585 			} else if ( token == "anim_turn" ) {
   2586 				flags.anim_turn = true;
   2587 			} else if ( token == "frame" ) {
   2588 				// create a frame command
   2589 				int			framenum;
   2590 				const char	*err;
   2591 
   2592 				// make sure we don't have any line breaks while reading the frame command so the error line # will be correct
   2593 				if ( !src.ReadTokenOnLine( &token ) ) {
   2594 					src.Warning( "Missing frame # after 'frame'" );
   2595 					MakeDefault();
   2596 					return false;
   2597 				}
   2598 				if ( token.type == TT_PUNCTUATION && token == "-" ) {
   2599 					src.Warning( "Invalid frame # after 'frame'" );
   2600 					MakeDefault();
   2601 					return false;
   2602 				} else if ( token.type != TT_NUMBER || token.subtype == TT_FLOAT ) {
   2603 					src.Error( "expected integer value, found '%s'", token.c_str() );
   2604 				}
   2605 
   2606 				// get the frame number
   2607 				framenum = token.GetIntValue();
   2608 
   2609 				// put the command on the specified frame of the animation
   2610 				err = anim->AddFrameCommand( this, framenum, src, NULL );
   2611 				if ( err ) {
   2612 					src.Warning( "%s", err );
   2613 					MakeDefault();
   2614 					return false;
   2615 				}
   2616 			} else {
   2617 				src.Warning( "Unknown command '%s'", token.c_str() );
   2618 				MakeDefault();
   2619 				return false;
   2620 			}
   2621 		}
   2622 	}
   2623 
   2624 	// set the flags
   2625 	anim->SetAnimFlags( flags );
   2626 	return true;
   2627 }
   2628 
   2629 /*
   2630 ================
   2631 idDeclModelDef::Parse
   2632 ================
   2633 */
   2634 bool idDeclModelDef::Parse( const char *text, const int textLength, bool allowBinaryVersion ) {
   2635 	int					i;
   2636 	int					num;
   2637 	idStr				filename;
   2638 	idStr				extension;
   2639 	const idMD5Joint	*md5joint;
   2640 	const idMD5Joint	*md5joints;
   2641 	idLexer				src;
   2642 	idToken				token;
   2643 	idToken				token2;
   2644 	idStr				jointnames;
   2645 	int					channel;
   2646 	jointHandle_t		jointnum;
   2647 	idList<jointHandle_t> jointList;
   2648 	int					numDefaultAnims;
   2649 
   2650 	src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
   2651 	src.SetFlags( DECL_LEXER_FLAGS );
   2652 	src.SkipUntilString( "{" );
   2653 
   2654 	numDefaultAnims = 0;
   2655 	while( 1 ) {
   2656 		if ( !src.ReadToken( &token ) ) {
   2657 			break;
   2658 		}
   2659 
   2660 		if ( !token.Icmp( "}" ) ) {
   2661 			break;
   2662 		}
   2663 
   2664 		if ( token == "inherit" ) {
   2665 			if( !src.ReadToken( &token2 ) ) {
   2666 				src.Warning( "Unexpected end of file" );
   2667 				MakeDefault();
   2668 				return false;
   2669 			}
   2670 			
   2671 			const idDeclModelDef *copy = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, token2, false ) );
   2672 			if ( !copy ) {
   2673 				common->Warning( "Unknown model definition '%s'", token2.c_str() );
   2674 			} else if ( copy->GetState() == DS_DEFAULTED ) {
   2675 				common->Warning( "inherited model definition '%s' defaulted", token2.c_str() );
   2676 				MakeDefault();
   2677 				return false;
   2678 			} else {
   2679 				CopyDecl( copy );
   2680 				numDefaultAnims = anims.Num();
   2681 			}
   2682 		} else if ( token == "skin" ) {
   2683 			if( !src.ReadToken( &token2 ) ) {
   2684 				src.Warning( "Unexpected end of file" );
   2685 				MakeDefault();
   2686 				return false;
   2687 			}
   2688 			skin = declManager->FindSkin( token2 );
   2689 			if ( !skin ) {
   2690 				src.Warning( "Skin '%s' not found", token2.c_str() );
   2691 				MakeDefault();
   2692 				return false;
   2693 			}
   2694 		} else if ( token == "mesh" ) {
   2695 			if( !src.ReadToken( &token2 ) ) {
   2696 				src.Warning( "Unexpected end of file" );
   2697 				MakeDefault();
   2698 				return false;
   2699 			}
   2700 			filename = token2;
   2701 			filename.ExtractFileExtension( extension );
   2702 			if ( extension != MD5_MESH_EXT ) {
   2703 				src.Warning( "Invalid model for MD5 mesh" );
   2704 				MakeDefault();
   2705 				return false;
   2706 			}
   2707 			modelHandle = renderModelManager->FindModel( filename );
   2708 			if ( !modelHandle ) {
   2709 				src.Warning( "Model '%s' not found", filename.c_str() );
   2710 				MakeDefault();
   2711 				return false;
   2712 			}
   2713 
   2714 			if ( modelHandle->IsDefaultModel() ) {
   2715 				src.Warning( "Model '%s' defaulted", filename.c_str() );
   2716 				MakeDefault();
   2717 				return false;
   2718 			}
   2719 
   2720 			// get the number of joints
   2721 			num = modelHandle->NumJoints();
   2722 			if ( !num ) {
   2723 				src.Warning( "Model '%s' has no joints", filename.c_str() );
   2724 			}
   2725 
   2726 			// set up the joint hierarchy
   2727 			joints.SetGranularity( 1 );
   2728 			joints.SetNum( num );
   2729 			jointParents.SetNum( num );
   2730 			channelJoints[0].SetNum( num );
   2731 			md5joints = modelHandle->GetJoints();
   2732 			md5joint = md5joints;
   2733 			for( i = 0; i < num; i++, md5joint++ ) {
   2734 				joints[i].channel = ANIMCHANNEL_ALL;
   2735 				joints[i].num = static_cast<jointHandle_t>( i );
   2736 				if ( md5joint->parent ) {
   2737 					joints[i].parentNum = static_cast<jointHandle_t>( md5joint->parent - md5joints );
   2738 				} else {
   2739 					joints[i].parentNum = INVALID_JOINT;
   2740 				}
   2741 				jointParents[i] = joints[i].parentNum;
   2742 				channelJoints[0][i] = i;
   2743 			}
   2744 		} else if ( token == "remove" ) {
   2745 			// removes any anims whos name matches
   2746 			if( !src.ReadToken( &token2 ) ) {
   2747 				src.Warning( "Unexpected end of file" );
   2748 				MakeDefault();
   2749 				return false;
   2750 			}
   2751 			num = 0;
   2752 			for( i = 0; i < anims.Num(); i++ ) {
   2753 				if ( ( token2 == anims[ i ]->Name() ) || ( token2 == anims[ i ]->FullName() ) ) {
   2754 					delete anims[ i ];
   2755 					anims.RemoveIndex( i );
   2756 					if ( i >= numDefaultAnims ) {
   2757 						src.Warning( "Anim '%s' was not inherited.  Anim should be removed from the model def.", token2.c_str() );
   2758 						MakeDefault();
   2759 						return false;
   2760 					}
   2761 					i--;
   2762 					numDefaultAnims--;
   2763 					num++;
   2764 					continue;
   2765 				}
   2766 			}
   2767 			if ( !num ) {
   2768 				src.Warning( "Couldn't find anim '%s' to remove", token2.c_str() );
   2769 				MakeDefault();
   2770 				return false;
   2771 			}
   2772 		} else if ( token == "anim" ) {
   2773 			if ( !modelHandle ) {
   2774 				src.Warning( "Must specify mesh before defining anims" );
   2775 				MakeDefault();
   2776 				return false;
   2777 			}
   2778 			if ( !ParseAnim( src, numDefaultAnims ) ) {
   2779 				MakeDefault();
   2780 				return false;
   2781 			}
   2782 		} else if ( token == "offset" ) {
   2783 			if ( !src.Parse1DMatrix( 3, offset.ToFloatPtr() ) ) {
   2784 				src.Warning( "Expected vector following 'offset'" );
   2785 				MakeDefault();
   2786 				return false;
   2787 			}
   2788 		} else if ( token == "channel" ) {
   2789 			if ( !modelHandle ) {
   2790 				src.Warning( "Must specify mesh before defining channels" );
   2791 				MakeDefault();
   2792 				return false;
   2793 			}
   2794 
   2795 			// set the channel for a group of joints
   2796 			if( !src.ReadToken( &token2 ) ) {
   2797 				src.Warning( "Unexpected end of file" );
   2798 				MakeDefault();
   2799 				return false;
   2800 			}
   2801 			if ( !src.CheckTokenString( "(" ) ) {
   2802 				src.Warning( "Expected { after '%s'\n", token2.c_str() );
   2803 				MakeDefault();
   2804 				return false;
   2805 			}
   2806 
   2807 			for( i = ANIMCHANNEL_ALL + 1; i < ANIM_NumAnimChannels; i++ ) {
   2808 				if ( !idStr::Icmp( channelNames[ i ], token2 ) ) {
   2809 					break;
   2810 				}
   2811 			}
   2812 
   2813 			if ( i >= ANIM_NumAnimChannels ) {
   2814 				src.Warning( "Unknown channel '%s'", token2.c_str() );
   2815 				MakeDefault();
   2816 				return false;
   2817 			}
   2818 
   2819 			channel = i;
   2820 			jointnames = "";
   2821 
   2822 			while( !src.CheckTokenString( ")" ) ) {
   2823 				if( !src.ReadToken( &token2 ) ) {
   2824 					src.Warning( "Unexpected end of file" );
   2825 					MakeDefault();
   2826 					return false;
   2827 				}
   2828 				jointnames += token2;
   2829 				if ( ( token2 != "*" ) && ( token2 != "-" ) ) {
   2830 					jointnames += " ";
   2831 				}
   2832 			}
   2833 
   2834 			GetJointList( jointnames, jointList );
   2835 
   2836 			channelJoints[ channel ].SetNum( jointList.Num() );
   2837 			for( num = i = 0; i < jointList.Num(); i++ ) {
   2838 				jointnum = jointList[ i ];
   2839 				if ( joints[ jointnum ].channel != ANIMCHANNEL_ALL ) {
   2840 					src.Warning( "Joint '%s' assigned to multiple channels", modelHandle->GetJointName( jointnum ) );
   2841 					continue;
   2842 				}
   2843 				joints[ jointnum ].channel = channel;
   2844 				channelJoints[ channel ][ num++ ] = jointnum;
   2845 			}
   2846 			channelJoints[ channel ].SetNum( num );
   2847 		} else {
   2848 			src.Warning( "unknown token '%s'", token.c_str() );
   2849 			MakeDefault();
   2850 			return false;
   2851 		}
   2852 	}
   2853 
   2854 	// shrink the anim list down to save space
   2855 	anims.SetGranularity( 1 );
   2856 	anims.SetNum( anims.Num() );
   2857 
   2858 	return true;
   2859 }
   2860 
   2861 /*
   2862 =====================
   2863 idDeclModelDef::HasAnim
   2864 =====================
   2865 */
   2866 bool idDeclModelDef::HasAnim( const char *name ) const {
   2867 	int	i;
   2868 
   2869 	// find any animations with same name
   2870 	for( i = 0; i < anims.Num(); i++ ) {
   2871 		if ( !strcmp( anims[ i ]->Name(), name ) ) {
   2872 			return true;
   2873 		}
   2874 	}
   2875 	
   2876 	return false;
   2877 }
   2878 
   2879 /*
   2880 =====================
   2881 idDeclModelDef::NumAnims
   2882 =====================
   2883 */
   2884 int idDeclModelDef::NumAnims() const {
   2885 	return anims.Num() + 1;
   2886 }
   2887 
   2888 /*
   2889 =====================
   2890 idDeclModelDef::GetSpecificAnim
   2891 
   2892 Gets the exact anim for the name, without randomization.
   2893 =====================
   2894 */
   2895 int idDeclModelDef::GetSpecificAnim( const char *name ) const {
   2896 	int	i;
   2897 
   2898 	// find a specific animation
   2899 	for( i = 0; i < anims.Num(); i++ ) {
   2900 		if ( !strcmp( anims[ i ]->FullName(), name ) ) {
   2901 			return i + 1;
   2902 		}
   2903 	}
   2904 
   2905 	// didn't find it
   2906 	return 0;
   2907 }
   2908 
   2909 /*
   2910 =====================
   2911 idDeclModelDef::GetAnim
   2912 =====================
   2913 */
   2914 const idAnim *idDeclModelDef::GetAnim( int index ) const {
   2915 	if ( ( index < 1 ) || ( index > anims.Num() ) ) {
   2916 		return NULL;
   2917 	}
   2918 	
   2919 	return anims[ index - 1 ];
   2920 }
   2921 
   2922 /*
   2923 =====================
   2924 idDeclModelDef::GetAnim
   2925 =====================
   2926 */
   2927 int idDeclModelDef::GetAnim( const char *name ) const {
   2928 	int				i;
   2929 	int				which;
   2930 	const int		MAX_ANIMS = 64;
   2931 	int				animList[ MAX_ANIMS ];
   2932 	int				numAnims;
   2933 	int				len;
   2934 
   2935 	len = strlen( name );
   2936 	if ( len && idStr::CharIsNumeric( name[ len - 1 ] ) ) {
   2937 		// find a specific animation
   2938 		return GetSpecificAnim( name );
   2939 	}
   2940 
   2941 	// find all animations with same name
   2942 	numAnims = 0;
   2943 	for( i = 0; i < anims.Num(); i++ ) {
   2944 		if ( !strcmp( anims[ i ]->Name(), name ) ) {
   2945 			animList[ numAnims++ ] = i;
   2946 			if ( numAnims >= MAX_ANIMS ) {
   2947 				break;
   2948 			}
   2949 		}
   2950 	}
   2951 
   2952 	if ( !numAnims ) {
   2953 		return 0;
   2954 	}
   2955 
   2956 	// get a random anim
   2957 	//FIXME: don't access gameLocal here?
   2958 	which = gameLocal.random.RandomInt( numAnims );
   2959 	return animList[ which ] + 1;
   2960 }
   2961 
   2962 /*
   2963 =====================
   2964 idDeclModelDef::GetSkin
   2965 =====================
   2966 */
   2967 const idDeclSkin *idDeclModelDef::GetSkin() const {
   2968 	return skin;
   2969 }
   2970 
   2971 /*
   2972 =====================
   2973 idDeclModelDef::GetModelName
   2974 =====================
   2975 */
   2976 const char *idDeclModelDef::GetModelName() const {
   2977 	if ( modelHandle ) {
   2978 		return modelHandle->Name();
   2979 	} else {
   2980 		return "";
   2981 	}
   2982 }
   2983 
   2984 /*
   2985 =====================
   2986 idDeclModelDef::Joints
   2987 =====================
   2988 */
   2989 const idList<jointInfo_t> &idDeclModelDef::Joints() const {
   2990 	return joints;
   2991 }
   2992 
   2993 /*
   2994 =====================
   2995 idDeclModelDef::JointParents
   2996 =====================
   2997 */
   2998 const int * idDeclModelDef::JointParents() const {
   2999 	return jointParents.Ptr();
   3000 }
   3001 
   3002 /*
   3003 =====================
   3004 idDeclModelDef::NumJoints
   3005 =====================
   3006 */
   3007 int idDeclModelDef::NumJoints() const {
   3008 	return joints.Num();
   3009 }
   3010 
   3011 /*
   3012 =====================
   3013 idDeclModelDef::GetJoint
   3014 =====================
   3015 */
   3016 const jointInfo_t *idDeclModelDef::GetJoint( int jointHandle ) const {
   3017 	if ( ( jointHandle < 0 ) || ( jointHandle > joints.Num() ) ) {
   3018 		gameLocal.Error( "idDeclModelDef::GetJoint : joint handle out of range" );
   3019 	}
   3020 	return &joints[ jointHandle ];
   3021 }
   3022 
   3023 /*
   3024 ====================
   3025 idDeclModelDef::GetJointName
   3026 ====================
   3027 */
   3028 const char *idDeclModelDef::GetJointName( int jointHandle ) const {
   3029 	const idMD5Joint *joint;
   3030 
   3031 	if ( !modelHandle ) {
   3032 		return NULL;
   3033 	}
   3034 	
   3035 	if ( ( jointHandle < 0 ) || ( jointHandle > joints.Num() ) ) {
   3036 		gameLocal.Error( "idDeclModelDef::GetJointName : joint handle out of range" );
   3037 	}
   3038 
   3039 	joint = modelHandle->GetJoints();
   3040 	return joint[ jointHandle ].name.c_str();
   3041 }
   3042 
   3043 /*
   3044 =====================
   3045 idDeclModelDef::NumJointsOnChannel
   3046 =====================
   3047 */
   3048 int idDeclModelDef::NumJointsOnChannel( int channel ) const {
   3049 	if ( ( channel < 0 ) || ( channel >= ANIM_NumAnimChannels ) ) {
   3050 		gameLocal.Error( "idDeclModelDef::NumJointsOnChannel : channel out of range" );
   3051 		return 0;
   3052 	}
   3053 	return channelJoints[ channel ].Num();
   3054 }
   3055 
   3056 /*
   3057 =====================
   3058 idDeclModelDef::GetChannelJoints
   3059 =====================
   3060 */
   3061 const int * idDeclModelDef::GetChannelJoints( int channel ) const {
   3062 	if ( ( channel < 0 ) || ( channel >= ANIM_NumAnimChannels ) ) {
   3063 		gameLocal.Error( "idDeclModelDef::GetChannelJoints : channel out of range" );
   3064 		return NULL;
   3065 	}
   3066 	return channelJoints[ channel ].Ptr();
   3067 }
   3068 
   3069 /*
   3070 =====================
   3071 idDeclModelDef::GetVisualOffset
   3072 =====================
   3073 */
   3074 const idVec3 &idDeclModelDef::GetVisualOffset() const {
   3075 	return offset;
   3076 }
   3077 
   3078 /***********************************************************************
   3079 
   3080 	idAnimator
   3081 
   3082 ***********************************************************************/
   3083 
   3084 /*
   3085 =====================
   3086 idAnimator::idAnimator
   3087 =====================
   3088 */
   3089 idAnimator::idAnimator() {
   3090 	int	i, j;
   3091 
   3092 	modelDef				= NULL;
   3093 	entity					= NULL;
   3094 	numJoints				= 0;
   3095 	joints					= NULL;
   3096 	lastTransformTime		= -1;
   3097 	stoppedAnimatingUpdate	= false;
   3098 	removeOriginOffset		= false;
   3099 	forceUpdate				= false;
   3100 
   3101 	frameBounds.Clear();
   3102 
   3103 	AFPoseJoints.SetGranularity( 1 );
   3104 	AFPoseJointMods.SetGranularity( 1 );
   3105 	AFPoseJointFrame.SetGranularity( 1 );
   3106 
   3107 	ClearAFPose();
   3108 
   3109 	for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) {
   3110 		for( j = 0; j < ANIM_MaxAnimsPerChannel; j++ ) {
   3111 			channels[ i ][ j ].Reset( NULL );
   3112 		}
   3113 	}
   3114 }
   3115 
   3116 /*
   3117 =====================
   3118 idAnimator::~idAnimator
   3119 =====================
   3120 */
   3121 idAnimator::~idAnimator() {
   3122 	FreeData();
   3123 }
   3124 
   3125 /*
   3126 =====================
   3127 idAnimator::Allocated
   3128 =====================
   3129 */
   3130 size_t idAnimator::Allocated() const {
   3131 	size_t	size;
   3132 
   3133 	size = jointMods.Allocated() + numJoints * sizeof( joints[0] ) + jointMods.Num() * sizeof( jointMods[ 0 ] ) + AFPoseJointMods.Allocated() + AFPoseJointFrame.Allocated() + AFPoseJoints.Allocated();
   3134 
   3135 	return size;
   3136 }
   3137 
   3138 /*
   3139 =====================
   3140 idAnimator::Save
   3141 
   3142 archives object for save game file
   3143 =====================
   3144 */
   3145 void idAnimator::Save( idSaveGame *savefile ) const {
   3146 	int i;
   3147 	int j;
   3148 
   3149 	savefile->WriteModelDef( modelDef );
   3150 	savefile->WriteObject( entity );
   3151 
   3152 	savefile->WriteInt( jointMods.Num() );
   3153 	for( i = 0; i < jointMods.Num(); i++ ) {
   3154 		savefile->WriteInt( jointMods[ i ]->jointnum );
   3155 		savefile->WriteMat3( jointMods[ i ]->mat );
   3156 		savefile->WriteVec3( jointMods[ i ]->pos );
   3157 		savefile->WriteInt( (int&)jointMods[ i ]->transform_pos );
   3158 		savefile->WriteInt( (int&)jointMods[ i ]->transform_axis );
   3159 	}
   3160 	
   3161 	savefile->WriteInt( numJoints );
   3162 	for ( i = 0; i < numJoints; i++ ) {
   3163 		float *data = joints[i].ToFloatPtr();
   3164 		for ( j = 0; j < 12; j++ ) {
   3165 			savefile->WriteFloat( data[j] );
   3166 		}
   3167 	}
   3168 
   3169 	savefile->WriteInt( lastTransformTime );
   3170 	savefile->WriteBool( stoppedAnimatingUpdate );
   3171 	savefile->WriteBool( forceUpdate );
   3172 	savefile->WriteBounds( frameBounds );
   3173 
   3174 	savefile->WriteFloat( AFPoseBlendWeight );
   3175 
   3176 	savefile->WriteInt( AFPoseJoints.Num() );
   3177 	for ( i = 0; i < AFPoseJoints.Num(); i++ ) {
   3178 		savefile->WriteInt( AFPoseJoints[i] );
   3179 	}
   3180 	
   3181 	savefile->WriteInt( AFPoseJointMods.Num() );
   3182 	for ( i = 0; i < AFPoseJointMods.Num(); i++ ) {
   3183 		savefile->WriteInt( (int&)AFPoseJointMods[i].mod );
   3184 		savefile->WriteMat3( AFPoseJointMods[i].axis );
   3185 		savefile->WriteVec3( AFPoseJointMods[i].origin );
   3186 	}
   3187 	
   3188 	savefile->WriteInt( AFPoseJointFrame.Num() );
   3189 	for ( i = 0; i < AFPoseJointFrame.Num(); i++ ) {
   3190 		savefile->WriteFloat( AFPoseJointFrame[i].q.x );
   3191 		savefile->WriteFloat( AFPoseJointFrame[i].q.y );
   3192 		savefile->WriteFloat( AFPoseJointFrame[i].q.z );
   3193 		savefile->WriteFloat( AFPoseJointFrame[i].q.w );
   3194 		savefile->WriteVec3( AFPoseJointFrame[i].t );
   3195 	}
   3196 	
   3197 	savefile->WriteBounds( AFPoseBounds );
   3198 	savefile->WriteInt( AFPoseTime );
   3199 
   3200 	savefile->WriteBool( removeOriginOffset );
   3201 
   3202 	for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) {
   3203 		for( j = 0; j < ANIM_MaxAnimsPerChannel; j++ ) {
   3204 			channels[ i ][ j ].Save( savefile );
   3205 		}
   3206 	}
   3207 }
   3208 
   3209 /*
   3210 =====================
   3211 idAnimator::Restore
   3212 
   3213 unarchives object from save game file
   3214 =====================
   3215 */
   3216 void idAnimator::Restore( idRestoreGame *savefile ) {
   3217 	int i;
   3218 	int j;
   3219 	int num;
   3220 
   3221 	savefile->ReadModelDef( modelDef );
   3222 	savefile->ReadObject( reinterpret_cast<idClass *&>( entity ) );
   3223 
   3224 	savefile->ReadInt( num );
   3225 	jointMods.SetNum( num );
   3226 	for( i = 0; i < num; i++ ) {
   3227 		jointMods[ i ] = new (TAG_ANIM) jointMod_t;
   3228 		savefile->ReadInt( (int&)jointMods[ i ]->jointnum );
   3229 		savefile->ReadMat3( jointMods[ i ]->mat );
   3230 		savefile->ReadVec3( jointMods[ i ]->pos );
   3231 		savefile->ReadInt( (int&)jointMods[ i ]->transform_pos );
   3232 		savefile->ReadInt( (int&)jointMods[ i ]->transform_axis );
   3233 	}
   3234 	
   3235 	savefile->ReadInt( numJoints );
   3236 	joints = (idJointMat *) Mem_Alloc16( SIMD_ROUND_JOINTS( numJoints ) * sizeof( joints[0] ), TAG_JOINTMAT );
   3237 	for ( i = 0; i < numJoints; i++ ) {
   3238 		float *data = joints[i].ToFloatPtr();
   3239 		for ( j = 0; j < 12; j++ ) {
   3240 			savefile->ReadFloat( data[j] );
   3241 		}
   3242 	}
   3243 	SIMD_INIT_LAST_JOINT( joints, numJoints );
   3244 	
   3245 	savefile->ReadInt( lastTransformTime );
   3246 	savefile->ReadBool( stoppedAnimatingUpdate );
   3247 	savefile->ReadBool( forceUpdate );
   3248 	savefile->ReadBounds( frameBounds );
   3249 
   3250 	savefile->ReadFloat( AFPoseBlendWeight );
   3251 
   3252 	savefile->ReadInt( num );
   3253 	AFPoseJoints.SetGranularity( 1 );
   3254 	AFPoseJoints.SetNum( num );
   3255 	for ( i = 0; i < AFPoseJoints.Num(); i++ ) {
   3256 		savefile->ReadInt( AFPoseJoints[i] );
   3257 	}
   3258 	
   3259 	savefile->ReadInt( num );
   3260 	AFPoseJointMods.SetGranularity( 1 );
   3261 	AFPoseJointMods.SetNum( num );
   3262 	for ( i = 0; i < AFPoseJointMods.Num(); i++ ) {
   3263 		savefile->ReadInt( (int&)AFPoseJointMods[i].mod );
   3264 		savefile->ReadMat3( AFPoseJointMods[i].axis );
   3265 		savefile->ReadVec3( AFPoseJointMods[i].origin );
   3266 	}
   3267 	
   3268 	savefile->ReadInt( num );
   3269 	AFPoseJointFrame.SetGranularity( 1 );
   3270 	AFPoseJointFrame.SetNum( num );
   3271 	for ( i = 0; i < AFPoseJointFrame.Num(); i++ ) {
   3272 		savefile->ReadFloat( AFPoseJointFrame[i].q.x );
   3273 		savefile->ReadFloat( AFPoseJointFrame[i].q.y );
   3274 		savefile->ReadFloat( AFPoseJointFrame[i].q.z );
   3275 		savefile->ReadFloat( AFPoseJointFrame[i].q.w );
   3276 		savefile->ReadVec3( AFPoseJointFrame[i].t );
   3277 	}
   3278 	
   3279 	savefile->ReadBounds( AFPoseBounds );
   3280 	savefile->ReadInt( AFPoseTime );
   3281 
   3282 	savefile->ReadBool( removeOriginOffset );
   3283 
   3284 	for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) {
   3285 		for( j = 0; j < ANIM_MaxAnimsPerChannel; j++ ) {
   3286 			channels[ i ][ j ].Restore( savefile, modelDef );
   3287 		}
   3288 	}
   3289 }
   3290 
   3291 /*
   3292 =====================
   3293 idAnimator::FreeData
   3294 =====================
   3295 */
   3296 void idAnimator::FreeData() {
   3297 	int	i, j;
   3298 
   3299 	if ( entity ) {
   3300 		entity->BecomeInactive( TH_ANIMATE );
   3301 	}
   3302 
   3303 	for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) {
   3304 		for( j = 0; j < ANIM_MaxAnimsPerChannel; j++ ) {
   3305 			channels[ i ][ j ].Reset( NULL );
   3306 		}
   3307 	}
   3308 
   3309 	jointMods.DeleteContents( true );
   3310 
   3311 	Mem_Free16( joints );
   3312 	joints = NULL;
   3313 	numJoints = 0;
   3314 
   3315 	modelDef = NULL;
   3316 
   3317 	ForceUpdate();
   3318 }
   3319 
   3320 /*
   3321 =====================
   3322 idAnimator::PushAnims
   3323 =====================
   3324 */
   3325 void idAnimator::PushAnims( int channelNum, int currentTime, int blendTime ) {
   3326 	int			i;
   3327 	idAnimBlend *channel;
   3328 
   3329 	channel = channels[ channelNum ];
   3330 	if ( !channel[ 0 ].GetWeight( currentTime ) || ( channel[ 0 ].starttime == currentTime ) ) {
   3331 		return;
   3332 	}
   3333 
   3334 	for( i = ANIM_MaxAnimsPerChannel - 1; i > 0; i-- ) {
   3335 		channel[ i ] = channel[ i - 1 ];
   3336 	}
   3337 
   3338 	channel[ 0 ].Reset( modelDef );
   3339 	channel[ 1 ].Clear( currentTime, blendTime );
   3340 	ForceUpdate();
   3341 }
   3342 
   3343 /*
   3344 =====================
   3345 idAnimator::SetModel
   3346 =====================
   3347 */
   3348 idRenderModel *idAnimator::SetModel( const char *modelname ) {
   3349 	int i, j;
   3350 
   3351 	FreeData();
   3352 
   3353 	// check if we're just clearing the model
   3354 	if ( !modelname || !*modelname ) {
   3355 		return NULL;
   3356 	}
   3357 
   3358 	modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelname, false ) );
   3359 	if ( !modelDef ) {
   3360 		return NULL;
   3361 	}
   3362 	
   3363 	idRenderModel *renderModel = modelDef->ModelHandle();
   3364 	if ( !renderModel ) {
   3365 		modelDef = NULL;
   3366 		return NULL;
   3367 	}
   3368 
   3369 	// make sure model hasn't been purged
   3370 	modelDef->Touch();
   3371 
   3372 	modelDef->SetupJoints( &numJoints, &joints, frameBounds, removeOriginOffset );
   3373 	modelDef->ModelHandle()->Reset();
   3374 
   3375 	// set the modelDef on all channels
   3376 	for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) {
   3377 		for( j = 0; j < ANIM_MaxAnimsPerChannel; j++ ) {
   3378 			channels[ i ][ j ].Reset( modelDef );
   3379 		}
   3380 	}
   3381 
   3382 	return modelDef->ModelHandle();
   3383 }
   3384 
   3385 /*
   3386 =====================
   3387 idAnimator::Size
   3388 =====================
   3389 */
   3390 size_t idAnimator::Size() const {
   3391 	return sizeof( *this ) + Allocated();
   3392 }
   3393 
   3394 /*
   3395 =====================
   3396 idAnimator::SetEntity
   3397 =====================
   3398 */
   3399 void idAnimator::SetEntity( idEntity *ent ) {
   3400 	entity = ent;
   3401 }
   3402 
   3403 /*
   3404 =====================
   3405 idAnimator::GetEntity
   3406 =====================
   3407 */
   3408 idEntity *idAnimator::GetEntity() const {
   3409 	return entity;
   3410 }
   3411 
   3412 /*
   3413 =====================
   3414 idAnimator::RemoveOriginOffset
   3415 =====================
   3416 */
   3417 void idAnimator::RemoveOriginOffset( bool remove ) {
   3418 	removeOriginOffset = remove;
   3419 }
   3420 
   3421 /*
   3422 =====================
   3423 idAnimator::RemoveOrigin
   3424 =====================
   3425 */
   3426 bool idAnimator::RemoveOrigin() const {
   3427 	return removeOriginOffset;
   3428 }
   3429 
   3430 /*
   3431 =====================
   3432 idAnimator::GetJointList
   3433 =====================
   3434 */
   3435 void idAnimator::GetJointList( const char *jointnames, idList<jointHandle_t> &jointList ) const {
   3436 	if ( modelDef ) {
   3437 		modelDef->GetJointList( jointnames, jointList );
   3438 	}
   3439 }
   3440 
   3441 /*
   3442 =====================
   3443 idAnimator::NumAnims
   3444 =====================
   3445 */
   3446 int	idAnimator::NumAnims() const {
   3447 	if ( !modelDef ) {
   3448 		return 0;
   3449 	}
   3450 	
   3451 	return modelDef->NumAnims();
   3452 }
   3453 
   3454 /*
   3455 =====================
   3456 idAnimator::GetAnim
   3457 =====================
   3458 */
   3459 const idAnim *idAnimator::GetAnim( int index ) const {
   3460 	if ( !modelDef ) {
   3461 		return NULL;
   3462 	}
   3463 	
   3464 	return modelDef->GetAnim( index );
   3465 }
   3466 
   3467 /*
   3468 =====================
   3469 idAnimator::GetAnim
   3470 =====================
   3471 */
   3472 int idAnimator::GetAnim( const char *name ) const {
   3473 	if ( !modelDef ) {
   3474 		return 0;
   3475 	}
   3476 	
   3477 	return modelDef->GetAnim( name );
   3478 }
   3479 
   3480 /*
   3481 =====================
   3482 idAnimator::HasAnim
   3483 =====================
   3484 */
   3485 bool idAnimator::HasAnim( const char *name ) const {
   3486 	if ( !modelDef ) {
   3487 		return false;
   3488 	}
   3489 	
   3490 	return modelDef->HasAnim( name );
   3491 }
   3492 
   3493 /*
   3494 =====================
   3495 idAnimator::NumJoints
   3496 =====================
   3497 */
   3498 int	idAnimator::NumJoints() const {
   3499 	return numJoints;
   3500 }
   3501 
   3502 /*
   3503 =====================
   3504 idAnimator::ModelHandle
   3505 =====================
   3506 */
   3507 idRenderModel *idAnimator::ModelHandle() const {
   3508 	if ( !modelDef ) {
   3509 		return NULL;
   3510 	}
   3511 	
   3512 	return modelDef->ModelHandle();
   3513 }
   3514 
   3515 /*
   3516 =====================
   3517 idAnimator::ModelDef
   3518 =====================
   3519 */
   3520 const idDeclModelDef *idAnimator::ModelDef() const {
   3521 	return modelDef;
   3522 }
   3523 
   3524 /*
   3525 =====================
   3526 idAnimator::CurrentAnim
   3527 =====================
   3528 */
   3529 idAnimBlend *idAnimator::CurrentAnim( int channelNum ) {
   3530 	if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) {
   3531 		gameLocal.Error( "idAnimator::CurrentAnim : channel out of range" );
   3532 		return NULL;
   3533 	}
   3534 
   3535 	return &channels[ channelNum ][ 0 ];
   3536 }
   3537 
   3538 /*
   3539 =====================
   3540 idAnimator::Clear
   3541 =====================
   3542 */
   3543 void idAnimator::Clear( int channelNum, int currentTime, int cleartime ) {
   3544 	int			i;
   3545 	idAnimBlend	*blend;
   3546 
   3547 	if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) {
   3548 		gameLocal.Error( "idAnimator::Clear : channel out of range" );
   3549 		return;
   3550 	}
   3551 
   3552 	blend = channels[ channelNum ];
   3553 	for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) {
   3554 		blend->Clear( currentTime, cleartime );
   3555 	}
   3556 	ForceUpdate();
   3557 }
   3558 
   3559 /*
   3560 =====================
   3561 idAnimator::SetFrame
   3562 =====================
   3563 */
   3564 void idAnimator::SetFrame( int channelNum, int animNum, int frame, int currentTime, int blendTime ) {
   3565 	if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) {
   3566 		gameLocal.Error( "idAnimator::SetFrame : channel out of range" );
   3567 	}
   3568 
   3569 	if ( !modelDef || !modelDef->GetAnim( animNum ) ) {
   3570 		return;
   3571 	}
   3572 
   3573 	PushAnims( channelNum, currentTime, blendTime );
   3574 	channels[ channelNum ][ 0 ].SetFrame( modelDef, animNum, frame, currentTime, blendTime );
   3575 	if ( entity ) {
   3576 		entity->BecomeActive( TH_ANIMATE );
   3577 	}
   3578 }
   3579 
   3580 /*
   3581 =====================
   3582 idAnimator::CycleAnim
   3583 =====================
   3584 */
   3585 void idAnimator::CycleAnim( int channelNum, int animNum, int currentTime, int blendTime ) {
   3586 	if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) {
   3587 		gameLocal.Error( "idAnimator::CycleAnim : channel out of range" );
   3588 	}
   3589 
   3590 	if ( !modelDef || !modelDef->GetAnim( animNum ) ) {
   3591 		return;
   3592 	}
   3593 	
   3594 	PushAnims( channelNum, currentTime, blendTime );
   3595 	channels[ channelNum ][ 0 ].CycleAnim( modelDef, animNum, currentTime, blendTime );
   3596 	if ( entity ) {
   3597 		entity->BecomeActive( TH_ANIMATE );
   3598 	}
   3599 }
   3600 
   3601 /*
   3602 =====================
   3603 idAnimator::PlayAnim
   3604 =====================
   3605 */
   3606 void idAnimator::PlayAnim( int channelNum, int animNum, int currentTime, int blendTime ) {
   3607 	if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) {
   3608 		gameLocal.Error( "idAnimator::PlayAnim : channel out of range" );
   3609 	}
   3610 
   3611 	if ( !modelDef || !modelDef->GetAnim( animNum ) ) {
   3612 		return;
   3613 	}
   3614 	
   3615 	PushAnims( channelNum, currentTime, blendTime );
   3616 	channels[ channelNum ][ 0 ].PlayAnim( modelDef, animNum, currentTime, blendTime );
   3617 	if ( entity ) {
   3618 		entity->BecomeActive( TH_ANIMATE );
   3619 	}
   3620 }
   3621 
   3622 /*
   3623 =====================
   3624 idAnimator::SyncAnimChannels
   3625 =====================
   3626 */
   3627 void idAnimator::SyncAnimChannels( int channelNum, int fromChannelNum, int currentTime, int blendTime ) {
   3628 	if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) || ( fromChannelNum < 0 ) || ( fromChannelNum >= ANIM_NumAnimChannels ) ) {
   3629 		gameLocal.Error( "idAnimator::SyncToChannel : channel out of range" );
   3630 		return;
   3631 	}
   3632 
   3633 	idAnimBlend &fromBlend = channels[ fromChannelNum ][ 0 ];
   3634 	idAnimBlend &toBlend = channels[ channelNum ][ 0 ];
   3635 
   3636 	float weight = fromBlend.blendEndValue;
   3637 	if ( ( fromBlend.Anim() != toBlend.Anim() ) || ( fromBlend.GetStartTime() != toBlend.GetStartTime() ) || ( fromBlend.GetEndTime() != toBlend.GetEndTime() ) ) {
   3638 		PushAnims( channelNum, currentTime, blendTime );
   3639 		toBlend = fromBlend;
   3640 		toBlend.blendStartValue = 0.0f;
   3641 		toBlend.blendEndValue = 0.0f;
   3642 	}
   3643     toBlend.SetWeight( weight, currentTime - 1, blendTime );
   3644 
   3645 	// disable framecommands on the current channel so that commands aren't called twice
   3646 	toBlend.AllowFrameCommands( false );
   3647 
   3648 	if ( entity ) {
   3649 		entity->BecomeActive( TH_ANIMATE );
   3650 	}
   3651 }
   3652 
   3653 /*
   3654 =====================
   3655 idAnimator::SetJointPos
   3656 =====================
   3657 */
   3658 void idAnimator::SetJointPos( jointHandle_t jointnum, jointModTransform_t transform_type, const idVec3 &pos ) {
   3659 	int i;
   3660 	jointMod_t *jointMod;
   3661 
   3662 	if ( !modelDef || !modelDef->ModelHandle() || ( jointnum < 0 ) || ( jointnum >= numJoints ) ) {
   3663 		return;
   3664 	}
   3665 
   3666 	jointMod = NULL;
   3667 	for( i = 0; i < jointMods.Num(); i++ ) {
   3668 		if ( jointMods[ i ]->jointnum == jointnum ) {
   3669 			jointMod = jointMods[ i ];
   3670 			break;
   3671 		} else if ( jointMods[ i ]->jointnum > jointnum ) {
   3672 			break;
   3673 		}
   3674 	}
   3675 
   3676 	if ( !jointMod ) {
   3677 		jointMod = new (TAG_ANIM) jointMod_t;
   3678 		jointMod->jointnum = jointnum;
   3679 		jointMod->mat.Identity();
   3680 		jointMod->transform_axis = JOINTMOD_NONE;
   3681 		jointMods.Insert( jointMod, i );
   3682 	}
   3683 
   3684 	jointMod->pos = pos;
   3685 	jointMod->transform_pos = transform_type;
   3686 
   3687 	if ( entity ) {
   3688 		entity->BecomeActive( TH_ANIMATE );
   3689 	}
   3690 	ForceUpdate();
   3691 }
   3692 
   3693 /*
   3694 =====================
   3695 idAnimator::SetJointAxis
   3696 =====================
   3697 */
   3698 void idAnimator::SetJointAxis( jointHandle_t jointnum, jointModTransform_t transform_type, const idMat3 &mat ) {
   3699 	int i;
   3700 	jointMod_t *jointMod;
   3701 
   3702 	if ( !modelDef || !modelDef->ModelHandle() || ( jointnum < 0 ) || ( jointnum >= numJoints ) ) {
   3703 		return;
   3704 	}
   3705 
   3706 	jointMod = NULL;
   3707 	for( i = 0; i < jointMods.Num(); i++ ) {
   3708 		if ( jointMods[ i ]->jointnum == jointnum ) {
   3709 			jointMod = jointMods[ i ];
   3710 			break;
   3711 		} else if ( jointMods[ i ]->jointnum > jointnum ) {
   3712 			break;
   3713 		}
   3714 	}
   3715 
   3716 	if ( !jointMod ) {
   3717 		jointMod = new (TAG_ANIM) jointMod_t;
   3718 		jointMod->jointnum = jointnum;
   3719 		jointMod->pos.Zero();
   3720 		jointMod->transform_pos = JOINTMOD_NONE;
   3721 		jointMods.Insert( jointMod, i );
   3722 	}
   3723 
   3724 	jointMod->mat = mat;
   3725 	jointMod->transform_axis = transform_type;
   3726 
   3727 	if ( entity ) {
   3728 		entity->BecomeActive( TH_ANIMATE );
   3729 	}
   3730 	ForceUpdate();
   3731 }
   3732 
   3733 /*
   3734 =====================
   3735 idAnimator::ClearJoint
   3736 =====================
   3737 */
   3738 void idAnimator::ClearJoint( jointHandle_t jointnum ) {
   3739 	int i;
   3740 
   3741 	if ( !modelDef || !modelDef->ModelHandle() || ( jointnum < 0 ) || ( jointnum >= numJoints ) ) {
   3742 		return;
   3743 	}
   3744 
   3745 	for( i = 0; i < jointMods.Num(); i++ ) {
   3746 		if ( jointMods[ i ]->jointnum == jointnum ) {
   3747 			delete jointMods[ i ];
   3748 			jointMods.RemoveIndex( i );
   3749 			ForceUpdate();
   3750 			break;
   3751 		} else if ( jointMods[ i ]->jointnum > jointnum ) {
   3752 			break;
   3753 		}
   3754 	}
   3755 }
   3756 
   3757 /*
   3758 =====================
   3759 idAnimator::ClearAllJoints
   3760 =====================
   3761 */
   3762 void idAnimator::ClearAllJoints() {
   3763 	if ( jointMods.Num() ) {
   3764 		ForceUpdate();
   3765 	}
   3766 	jointMods.DeleteContents( true );
   3767 }
   3768 
   3769 /*
   3770 =====================
   3771 idAnimator::ClearAllAnims
   3772 =====================
   3773 */
   3774 void idAnimator::ClearAllAnims( int currentTime, int cleartime ) {
   3775 	int	i;
   3776 
   3777 	for( i = 0; i < ANIM_NumAnimChannels; i++ ) {
   3778 		Clear( i, currentTime, cleartime );
   3779 	}
   3780 
   3781 	ClearAFPose();
   3782 	ForceUpdate();
   3783 }
   3784 
   3785 /*
   3786 ====================
   3787 idAnimator::GetDelta
   3788 ====================
   3789 */
   3790 void idAnimator::GetDelta( int fromtime, int totime, idVec3 &delta ) const {
   3791 	int					i;
   3792 	const idAnimBlend	*blend;
   3793 	float				blendWeight;
   3794 
   3795 	if ( !modelDef || !modelDef->ModelHandle() || ( fromtime == totime ) ) {
   3796 		delta.Zero();
   3797 		return;
   3798 	}
   3799 
   3800 	delta.Zero();
   3801 	blendWeight = 0.0f;
   3802 
   3803 	blend = channels[ ANIMCHANNEL_ALL ];
   3804 	for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) {
   3805 		blend->BlendDelta( fromtime, totime, delta, blendWeight );
   3806 	}
   3807 
   3808 	if ( modelDef->Joints()[ 0 ].channel ) {
   3809 		blend = channels[ modelDef->Joints()[ 0 ].channel ];
   3810 		for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) {
   3811 			blend->BlendDelta( fromtime, totime, delta, blendWeight );
   3812 		}
   3813 	}
   3814 }
   3815 
   3816 /*
   3817 ====================
   3818 idAnimator::GetDeltaRotation
   3819 ====================
   3820 */
   3821 bool idAnimator::GetDeltaRotation( int fromtime, int totime, idMat3 &delta ) const {
   3822 	int					i;
   3823 	const idAnimBlend	*blend;
   3824 	float				blendWeight;
   3825 	idQuat				q;
   3826 
   3827 	if ( !modelDef || !modelDef->ModelHandle() || ( fromtime == totime ) ) {
   3828 		delta.Identity();
   3829 		return false;
   3830 	}
   3831 
   3832 	q.Set( 0.0f, 0.0f, 0.0f, 1.0f );
   3833 	blendWeight = 0.0f;
   3834 
   3835 	blend = channels[ ANIMCHANNEL_ALL ];
   3836 	for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) {
   3837 		blend->BlendDeltaRotation( fromtime, totime, q, blendWeight );
   3838 	}
   3839 
   3840 	if ( modelDef->Joints()[ 0 ].channel ) {
   3841 		blend = channels[ modelDef->Joints()[ 0 ].channel ];
   3842 		for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) {
   3843 			blend->BlendDeltaRotation( fromtime, totime, q, blendWeight );
   3844 		}
   3845 	}
   3846 
   3847 	if ( blendWeight > 0.0f ) {
   3848 		delta = q.ToMat3();
   3849 		return true;
   3850 	} else {
   3851 		delta.Identity();
   3852 		return false;
   3853 	}
   3854 }
   3855 
   3856 /*
   3857 ====================
   3858 idAnimator::GetOrigin
   3859 ====================
   3860 */
   3861 void idAnimator::GetOrigin( int currentTime, idVec3 &pos ) const {
   3862 	int					i;
   3863 	const idAnimBlend	*blend;
   3864 	float				blendWeight;
   3865 
   3866 	if ( !modelDef || !modelDef->ModelHandle() ) {
   3867 		pos.Zero();
   3868 		return;
   3869 	}
   3870 
   3871 	pos.Zero();
   3872 	blendWeight = 0.0f;
   3873 
   3874 	blend = channels[ ANIMCHANNEL_ALL ];
   3875 	for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) {
   3876 		blend->BlendOrigin( currentTime, pos, blendWeight, removeOriginOffset );
   3877 	}
   3878 
   3879 	if ( modelDef->Joints()[ 0 ].channel ) {
   3880 		blend = channels[ modelDef->Joints()[ 0 ].channel ];
   3881 		for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) {
   3882 			blend->BlendOrigin( currentTime, pos, blendWeight, removeOriginOffset );
   3883 		}
   3884 	}
   3885 
   3886 	pos += modelDef->GetVisualOffset();
   3887 }
   3888 
   3889 /*
   3890 ====================
   3891 idAnimator::GetBounds
   3892 ====================
   3893 */
   3894 bool idAnimator::GetBounds( int currentTime, idBounds &bounds ) {
   3895 	int					i, j;
   3896 	const idAnimBlend	*blend;
   3897 	int					count;
   3898 
   3899 	if ( !modelDef || !modelDef->ModelHandle() ) {
   3900 		return false;
   3901 	}
   3902 
   3903 	if ( AFPoseJoints.Num() ) {
   3904 		bounds = AFPoseBounds;
   3905 		count = 1;
   3906 	} else {
   3907 		bounds.Clear();
   3908 		count = 0;
   3909 	}
   3910 
   3911 	blend = channels[ 0 ];
   3912 	for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) {
   3913 		for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) {
   3914 			if ( blend->AddBounds( currentTime, bounds, removeOriginOffset ) ) {
   3915 				count++;
   3916 			}
   3917 		}
   3918 	}
   3919 
   3920 	if ( !count ) {
   3921 		if ( !frameBounds.IsCleared() ) {
   3922 			bounds = frameBounds;
   3923 			return true;
   3924 		} else {
   3925 			bounds.Zero();
   3926 			return false;
   3927 		}
   3928 	}
   3929 
   3930 	bounds.TranslateSelf( modelDef->GetVisualOffset() );
   3931 
   3932 	if ( g_debugBounds.GetBool() ) {
   3933 		if ( bounds[1][0] - bounds[0][0] > 2048 || bounds[1][1] - bounds[0][1] > 2048 ) {
   3934 			if ( entity ) {
   3935 				gameLocal.Warning( "big frameBounds on entity '%s' with model '%s': %f,%f", entity->name.c_str(), modelDef->ModelHandle()->Name(), bounds[1][0] - bounds[0][0], bounds[1][1] - bounds[0][1] );
   3936 			} else {
   3937 				gameLocal.Warning( "big frameBounds on model '%s': %f,%f", modelDef->ModelHandle()->Name(), bounds[1][0] - bounds[0][0], bounds[1][1] - bounds[0][1] );
   3938 			}
   3939 		}
   3940 	}
   3941 
   3942 	frameBounds = bounds;
   3943 
   3944 	return true;
   3945 }
   3946 
   3947 /*
   3948 =====================
   3949 idAnimator::InitAFPose
   3950 =====================
   3951 */
   3952 void idAnimator::InitAFPose() {
   3953 
   3954 	if ( !modelDef ) {
   3955 		return;
   3956 	}
   3957 
   3958 	AFPoseJoints.SetNum( modelDef->Joints().Num() );
   3959 	AFPoseJoints.SetNum( 0 );
   3960 	AFPoseJointMods.SetNum( modelDef->Joints().Num() );
   3961 	AFPoseJointFrame.SetNum( modelDef->Joints().Num() );
   3962 }
   3963 
   3964 /*
   3965 =====================
   3966 idAnimator::SetAFPoseJointMod
   3967 =====================
   3968 */
   3969 void idAnimator::SetAFPoseJointMod( const jointHandle_t jointNum, const AFJointModType_t mod, const idMat3 &axis, const idVec3 &origin ) {
   3970 	AFPoseJointMods[jointNum].mod = mod;
   3971 	AFPoseJointMods[jointNum].axis = axis;
   3972 	AFPoseJointMods[jointNum].origin = origin;
   3973 
   3974 	int index = idBinSearch_GreaterEqual<int>( AFPoseJoints.Ptr(), AFPoseJoints.Num(), jointNum );
   3975 	if ( index >= AFPoseJoints.Num() || jointNum != AFPoseJoints[index] ) {
   3976 		AFPoseJoints.Insert( jointNum, index );
   3977 	}
   3978 }
   3979 
   3980 /*
   3981 =====================
   3982 idAnimator::FinishAFPose
   3983 =====================
   3984 */
   3985 void idAnimator::FinishAFPose( int animNum, const idBounds &bounds, const int time ) {
   3986 	int					i, j;
   3987 	int					numJoints;
   3988 	int					parentNum;
   3989 	int					jointMod;
   3990 	int					jointNum;
   3991 	const int *			jointParent;
   3992 
   3993 	if ( !modelDef ) {
   3994 		return;
   3995 	}
   3996 	
   3997 	const idAnim *anim = modelDef->GetAnim( animNum );
   3998 	if ( !anim ) {
   3999 		return;
   4000 	}
   4001 
   4002 	numJoints = modelDef->Joints().Num();
   4003 	if ( !numJoints ) {
   4004 		return;
   4005 	}
   4006 
   4007 	idRenderModel		*md5 = modelDef->ModelHandle();
   4008 	const idMD5Anim		*md5anim = anim->MD5Anim( 0 );
   4009 
   4010 	if ( numJoints != md5anim->NumJoints() ) {
   4011 		gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", md5->Name(), md5anim->Name() );
   4012 		return;
   4013 	}
   4014 
   4015 	idJointQuat *jointFrame = ( idJointQuat * )_alloca16( numJoints * sizeof( *jointFrame ) );
   4016 	md5anim->GetSingleFrame( 0, jointFrame, modelDef->GetChannelJoints( ANIMCHANNEL_ALL ), modelDef->NumJointsOnChannel( ANIMCHANNEL_ALL ) );
   4017 
   4018 	if ( removeOriginOffset ) {
   4019 #ifdef VELOCITY_MOVE
   4020 		jointFrame[ 0 ].t.x = 0.0f;
   4021 #else
   4022 		jointFrame[ 0 ].t.Zero();
   4023 #endif
   4024 	}
   4025 
   4026 	idJointMat *joints = ( idJointMat * )_alloca16( numJoints * sizeof( *joints ) );
   4027 
   4028 	// convert the joint quaternions to joint matrices
   4029 	SIMDProcessor->ConvertJointQuatsToJointMats( joints, jointFrame, numJoints );
   4030 
   4031 	// first joint is always root of entire hierarchy
   4032 	if ( AFPoseJoints.Num() && AFPoseJoints[0] == 0 ) {
   4033 		switch( AFPoseJointMods[0].mod ) {
   4034 			case AF_JOINTMOD_AXIS: {
   4035 				joints[0].SetRotation( AFPoseJointMods[0].axis );
   4036 				break;
   4037 			}
   4038 			case AF_JOINTMOD_ORIGIN: {
   4039 				joints[0].SetTranslation( AFPoseJointMods[0].origin );
   4040 				break;
   4041 			}
   4042 			case AF_JOINTMOD_BOTH: {
   4043 				joints[0].SetRotation( AFPoseJointMods[0].axis );
   4044 				joints[0].SetTranslation( AFPoseJointMods[0].origin );
   4045 				break;
   4046 			}
   4047 		}
   4048 		j = 1;
   4049 	} else {
   4050 		j = 0;
   4051 	}
   4052 
   4053 	// pointer to joint info
   4054 	jointParent = modelDef->JointParents();
   4055 
   4056 	// transform the child joints
   4057 	for( i = 1; j < AFPoseJoints.Num(); j++, i++ ) {
   4058 		jointMod = AFPoseJoints[j];
   4059 
   4060 		// transform any joints preceding the joint modifier
   4061 		SIMDProcessor->TransformJoints( joints, jointParent, i, jointMod - 1 );
   4062 		i = jointMod;
   4063 
   4064 		parentNum = jointParent[i];
   4065 
   4066 		switch( AFPoseJointMods[jointMod].mod ) {
   4067 			case AF_JOINTMOD_AXIS: {
   4068 				joints[i].SetRotation( AFPoseJointMods[jointMod].axis );
   4069 				joints[i].SetTranslation( joints[parentNum].ToVec3() + joints[i].ToVec3() * joints[parentNum].ToMat3() );
   4070 				break;
   4071 			}
   4072 			case AF_JOINTMOD_ORIGIN: {
   4073 				joints[i].SetRotation( joints[i].ToMat3() * joints[parentNum].ToMat3() );
   4074 				joints[i].SetTranslation( AFPoseJointMods[jointMod].origin );
   4075 				break;
   4076 			}
   4077 			case AF_JOINTMOD_BOTH: {
   4078 				joints[i].SetRotation( AFPoseJointMods[jointMod].axis );
   4079 				joints[i].SetTranslation( AFPoseJointMods[jointMod].origin );
   4080 				break;
   4081 			}
   4082 		}
   4083 	}
   4084 
   4085 	// transform the rest of the hierarchy
   4086 	SIMDProcessor->TransformJoints( joints, jointParent, i, numJoints - 1 );
   4087 
   4088 	// untransform hierarchy
   4089 	SIMDProcessor->UntransformJoints( joints, jointParent, 1, numJoints - 1 );
   4090 
   4091 	// convert joint matrices back to joint quaternions
   4092 	SIMDProcessor->ConvertJointMatsToJointQuats( AFPoseJointFrame.Ptr(), joints, numJoints );
   4093 
   4094 	// find all modified joints and their parents
   4095 	bool *blendJoints = (bool *) _alloca16( numJoints * sizeof( bool ) );
   4096 	memset( blendJoints, 0, numJoints * sizeof( bool ) );
   4097 
   4098 	// mark all modified joints and their parents
   4099 	for( i = 0; i < AFPoseJoints.Num(); i++ ) {
   4100 		for( jointNum = AFPoseJoints[i]; jointNum != INVALID_JOINT; jointNum = jointParent[jointNum] ) {
   4101 			blendJoints[jointNum] = true;
   4102 		}
   4103 	}
   4104 
   4105 	// lock all parents of modified joints
   4106 	AFPoseJoints.SetNum( 0 );
   4107 	for ( i = 0; i < numJoints; i++ ) {
   4108 		if ( blendJoints[i] ) {
   4109 			AFPoseJoints.Append( i );
   4110 		}
   4111 	}
   4112 
   4113 	AFPoseBounds = bounds;
   4114 	AFPoseTime = time;
   4115 
   4116 	ForceUpdate();
   4117 }
   4118 
   4119 /*
   4120 =====================
   4121 idAnimator::SetAFPoseBlendWeight
   4122 =====================
   4123 */
   4124 void idAnimator::SetAFPoseBlendWeight( float blendWeight ) {
   4125 	AFPoseBlendWeight = blendWeight;
   4126 }
   4127 
   4128 /*
   4129 =====================
   4130 idAnimator::BlendAFPose
   4131 =====================
   4132 */
   4133 bool idAnimator::BlendAFPose( idJointQuat *blendFrame ) const {
   4134 
   4135 	if ( !AFPoseJoints.Num() ) {
   4136 		return false;
   4137 	}
   4138 
   4139 	SIMDProcessor->BlendJoints( blendFrame, AFPoseJointFrame.Ptr(), AFPoseBlendWeight, AFPoseJoints.Ptr(), AFPoseJoints.Num() );
   4140 
   4141 	return true;
   4142 }
   4143 
   4144 /*
   4145 =====================
   4146 idAnimator::ClearAFPose
   4147 =====================
   4148 */
   4149 void idAnimator::ClearAFPose() {
   4150 	if ( AFPoseJoints.Num() ) {
   4151 		ForceUpdate();
   4152 	}
   4153 	AFPoseBlendWeight = 1.0f;
   4154 	AFPoseJoints.SetNum( 0 );
   4155 	AFPoseBounds.Clear();
   4156 	AFPoseTime = 0;
   4157 }
   4158 
   4159 /*
   4160 =====================
   4161 idAnimator::ServiceAnims
   4162 =====================
   4163 */
   4164 void idAnimator::ServiceAnims( int fromtime, int totime ) {
   4165 	int			i, j;
   4166 	idAnimBlend	*blend;
   4167 
   4168 	if ( !modelDef ) {
   4169 		return;
   4170 	}
   4171 
   4172 	if ( modelDef->ModelHandle() ) {
   4173 		blend = channels[ 0 ];
   4174 		for( i = 0; i < ANIM_NumAnimChannels; i++ ) {
   4175 			for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) {
   4176 				blend->CallFrameCommands( entity, fromtime, totime );
   4177 			}
   4178 		}
   4179 	}
   4180 
   4181 	if ( !IsAnimating( totime ) ) {
   4182 		stoppedAnimatingUpdate = true;
   4183 		if ( entity ) {
   4184 			entity->BecomeInactive( TH_ANIMATE );
   4185 
   4186 			// present one more time with stopped animations so the renderer can properly recreate interactions
   4187 			entity->BecomeActive( TH_UPDATEVISUALS );
   4188 		}
   4189 	}
   4190 }
   4191 
   4192 /*
   4193 =====================
   4194 idAnimator::IsAnimating
   4195 =====================
   4196 */
   4197 bool idAnimator::IsAnimating( int currentTime ) const {
   4198 	int					i, j;
   4199 	const idAnimBlend	*blend;
   4200 
   4201 	if ( !modelDef || !modelDef->ModelHandle() ) {
   4202 		return false;
   4203 	}
   4204 
   4205 	// if animating with an articulated figure
   4206 	if ( AFPoseJoints.Num() && currentTime <= AFPoseTime ) {
   4207 		return true;
   4208 	}
   4209 
   4210 	blend = channels[ 0 ];
   4211 	for( i = 0; i < ANIM_NumAnimChannels; i++ ) {
   4212 		for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) {
   4213 			if ( !blend->IsDone( currentTime ) ) {
   4214 				return true;
   4215 			}
   4216 		}
   4217 	}
   4218 
   4219 	return false;
   4220 }
   4221 
   4222 /*
   4223 =====================
   4224 idAnimator::FrameHasChanged
   4225 =====================
   4226 */
   4227 bool idAnimator::FrameHasChanged( int currentTime ) const {
   4228 	int					i, j;
   4229 	const idAnimBlend	*blend;
   4230 
   4231 	if ( !modelDef || !modelDef->ModelHandle() ) {
   4232 		return false;
   4233 	}
   4234 
   4235 	// if animating with an articulated figure
   4236 	if ( AFPoseJoints.Num() && currentTime <= AFPoseTime ) {
   4237 		return true;
   4238 	}
   4239 
   4240 	blend = channels[ 0 ];
   4241 	for( i = 0; i < ANIM_NumAnimChannels; i++ ) {
   4242 		for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) {
   4243 			if ( blend->FrameHasChanged( currentTime ) ) {
   4244 				return true;
   4245 			}
   4246 		}
   4247 	}
   4248 
   4249 	if ( forceUpdate && IsAnimating( currentTime ) ) {
   4250 		return true;
   4251 	}
   4252 
   4253 	return false;
   4254 }
   4255 
   4256 /*
   4257 =====================
   4258 idAnimator::CreateFrame
   4259 =====================
   4260 */
   4261 bool idAnimator::CreateFrame( int currentTime, bool force ) {
   4262 	int					i, j;
   4263 	int					numJoints;
   4264 	int					parentNum;
   4265 	bool				hasAnim;
   4266 	bool				debugInfo;
   4267 	float				baseBlend;
   4268 	float				blendWeight;
   4269 	const idAnimBlend *	blend;
   4270 	const int *			jointParent;
   4271 	const jointMod_t *	jointMod;
   4272 	const idJointQuat *	defaultPose;
   4273 
   4274 	static idCVar		r_showSkel( "r_showSkel", "0", CVAR_RENDERER | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
   4275 
   4276 	if ( !modelDef || !modelDef->ModelHandle() ) {
   4277 		return false;
   4278 	}
   4279 
   4280 	if ( !force && !r_showSkel.GetInteger() ) {
   4281 		if ( lastTransformTime == currentTime ) {
   4282 			return false;
   4283 		}
   4284 		if ( lastTransformTime != -1 && !stoppedAnimatingUpdate && !IsAnimating( currentTime ) ) {
   4285 			return false;
   4286 		}
   4287 	}
   4288 
   4289 	lastTransformTime = currentTime;
   4290 	stoppedAnimatingUpdate = false;
   4291 
   4292 	if ( entity && ( ( g_debugAnim.GetInteger() == entity->entityNumber ) || ( g_debugAnim.GetInteger() == -2 ) ) ) {
   4293 		debugInfo = true;
   4294 		gameLocal.Printf( "---------------\n%d: entity '%s':\n", gameLocal.time, entity->GetName() );
   4295  		gameLocal.Printf( "model '%s':\n", modelDef->GetModelName() );
   4296 	} else {
   4297 		debugInfo = false;
   4298 	}
   4299 
   4300 	// init the joint buffer
   4301 	if ( AFPoseJoints.Num() ) {
   4302 		// initialize with AF pose anim for the case where there are no other animations and no AF pose joint modifications
   4303 		defaultPose = AFPoseJointFrame.Ptr();
   4304 	} else {
   4305 		defaultPose = modelDef->GetDefaultPose();
   4306 	}
   4307 
   4308 	if ( !defaultPose ) {
   4309 		//gameLocal.Warning( "idAnimator::CreateFrame: no defaultPose on '%s'", modelDef->Name() );
   4310 		return false;
   4311 	}
   4312 
   4313 	numJoints = modelDef->Joints().Num();
   4314 	idJointQuat *jointFrame = ( idJointQuat * )_alloca16( numJoints * sizeof( jointFrame[0] ) );
   4315 	SIMDProcessor->Memcpy( jointFrame, defaultPose, numJoints * sizeof( jointFrame[0] ) );
   4316 
   4317 	hasAnim = false;
   4318 
   4319 	// blend the all channel
   4320 	baseBlend = 0.0f;
   4321 	blend = channels[ ANIMCHANNEL_ALL ];
   4322 	for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) {
   4323 		if ( blend->BlendAnim( currentTime, ANIMCHANNEL_ALL, numJoints, jointFrame, baseBlend, removeOriginOffset, false, debugInfo ) ) {
   4324 			hasAnim = true;
   4325 			if ( baseBlend >= 1.0f ) {
   4326 				break;
   4327 			}
   4328 		}
   4329 	}
   4330 
   4331 	// only blend other channels if there's enough space to blend into
   4332 	if ( baseBlend < 1.0f ) {
   4333 		for( i = ANIMCHANNEL_ALL + 1; i < ANIM_NumAnimChannels; i++ ) {
   4334 			if ( !modelDef->NumJointsOnChannel( i ) ) {
   4335 				continue;
   4336 			}
   4337 			if ( i == ANIMCHANNEL_EYELIDS ) {
   4338 				// eyelids blend over any previous anims, so skip it and blend it later
   4339 				continue;
   4340 			}
   4341 			blendWeight = baseBlend;
   4342 			blend = channels[ i ];
   4343 			for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) {
   4344 				if ( blend->BlendAnim( currentTime, i, numJoints, jointFrame, blendWeight, removeOriginOffset, false, debugInfo ) ) {
   4345 					hasAnim = true;
   4346 					if ( blendWeight >= 1.0f ) {
   4347 						// fully blended
   4348 						break;
   4349 					}
   4350 				}
   4351 			}
   4352 
   4353 			if ( debugInfo && !AFPoseJoints.Num() && !blendWeight ) {
   4354 				gameLocal.Printf( "%d: %s using default pose in model '%s'\n", gameLocal.time, channelNames[ i ], modelDef->GetModelName() );
   4355 			}
   4356 		}
   4357 	}
   4358 
   4359 	// blend in the eyelids
   4360 	if ( modelDef->NumJointsOnChannel( ANIMCHANNEL_EYELIDS ) ) {
   4361 		blend = channels[ ANIMCHANNEL_EYELIDS ];
   4362 		blendWeight = baseBlend;
   4363 		for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) {
   4364 			if ( blend->BlendAnim( currentTime, ANIMCHANNEL_EYELIDS, numJoints, jointFrame, blendWeight, removeOriginOffset, true, debugInfo ) ) {
   4365 				hasAnim = true;
   4366 				if ( blendWeight >= 1.0f ) {
   4367 					// fully blended
   4368 					break;
   4369 				}
   4370 			}
   4371 		}
   4372 	}
   4373 
   4374 	// blend the articulated figure pose
   4375 	if ( BlendAFPose( jointFrame ) ) {
   4376 		hasAnim = true;
   4377 	}
   4378 
   4379 	if ( !hasAnim && !jointMods.Num() ) {
   4380 		// no animations were updated
   4381 		return false;
   4382 	}
   4383 
   4384 	// convert the joint quaternions to rotation matrices
   4385 	SIMDProcessor->ConvertJointQuatsToJointMats( joints, jointFrame, numJoints );
   4386 
   4387 	// check if we need to modify the origin
   4388 	if ( jointMods.Num() && ( jointMods[0]->jointnum == 0 ) ) {
   4389 		jointMod = jointMods[0];
   4390 
   4391 		switch( jointMod->transform_axis ) {
   4392 			case JOINTMOD_NONE:
   4393 				break;
   4394 
   4395 			case JOINTMOD_LOCAL:
   4396 				joints[0].SetRotation( jointMod->mat * joints[0].ToMat3() );
   4397 				break;
   4398 			
   4399 			case JOINTMOD_WORLD:
   4400 				joints[0].SetRotation( joints[0].ToMat3() * jointMod->mat );
   4401 				break;
   4402 
   4403 			case JOINTMOD_LOCAL_OVERRIDE:
   4404 			case JOINTMOD_WORLD_OVERRIDE:
   4405 				joints[0].SetRotation( jointMod->mat );
   4406 				break;
   4407 		}
   4408 
   4409 		switch( jointMod->transform_pos ) {
   4410 			case JOINTMOD_NONE:
   4411 				break;
   4412 
   4413 			case JOINTMOD_LOCAL:
   4414 				joints[0].SetTranslation( joints[0].ToVec3() + jointMod->pos );
   4415 				break;
   4416 			
   4417 			case JOINTMOD_LOCAL_OVERRIDE:
   4418 			case JOINTMOD_WORLD:
   4419 			case JOINTMOD_WORLD_OVERRIDE:
   4420 				joints[0].SetTranslation( jointMod->pos );
   4421 				break;
   4422 		}
   4423 		j = 1;
   4424 	} else {
   4425 		j = 0;
   4426 	}
   4427 
   4428 	// add in the model offset
   4429 	joints[0].SetTranslation( joints[0].ToVec3() + modelDef->GetVisualOffset() );
   4430 
   4431 	// pointer to joint info
   4432 	jointParent = modelDef->JointParents();
   4433 
   4434 	// add in any joint modifications
   4435 	for( i = 1; j < jointMods.Num(); j++, i++ ) {
   4436 		jointMod = jointMods[j];
   4437 
   4438 		// transform any joints preceding the joint modifier
   4439 		SIMDProcessor->TransformJoints( joints, jointParent, i, jointMod->jointnum - 1 );
   4440 		i = jointMod->jointnum;
   4441 
   4442 		parentNum = jointParent[i];
   4443 
   4444 		// modify the axis
   4445 		switch( jointMod->transform_axis ) {
   4446 			case JOINTMOD_NONE:
   4447 				joints[i].SetRotation( joints[i].ToMat3() * joints[ parentNum ].ToMat3() );
   4448 				break;
   4449 
   4450 			case JOINTMOD_LOCAL:
   4451 				joints[i].SetRotation( jointMod->mat * ( joints[i].ToMat3() * joints[parentNum].ToMat3() ) );
   4452 				break;
   4453 			
   4454 			case JOINTMOD_LOCAL_OVERRIDE:
   4455 				joints[i].SetRotation( jointMod->mat * joints[parentNum].ToMat3() );
   4456 				break;
   4457 
   4458 			case JOINTMOD_WORLD:
   4459 				joints[i].SetRotation( ( joints[i].ToMat3() * joints[parentNum].ToMat3() ) * jointMod->mat );
   4460 				break;
   4461 
   4462 			case JOINTMOD_WORLD_OVERRIDE:
   4463 				joints[i].SetRotation( jointMod->mat );
   4464 				break;
   4465 		}
   4466 
   4467 		// modify the position
   4468 		switch( jointMod->transform_pos ) {
   4469 			case JOINTMOD_NONE:
   4470 				joints[i].SetTranslation( joints[parentNum].ToVec3() + joints[i].ToVec3() * joints[parentNum].ToMat3() );
   4471 				break;
   4472 
   4473 			case JOINTMOD_LOCAL:
   4474 				joints[i].SetTranslation( joints[parentNum].ToVec3() + ( joints[i].ToVec3() + jointMod->pos ) * joints[parentNum].ToMat3() );
   4475 				break;
   4476 			
   4477 			case JOINTMOD_LOCAL_OVERRIDE:
   4478 				joints[i].SetTranslation( joints[parentNum].ToVec3() + jointMod->pos * joints[parentNum].ToMat3() );
   4479 				break;
   4480 
   4481 			case JOINTMOD_WORLD:
   4482 				joints[i].SetTranslation( joints[parentNum].ToVec3() + joints[i].ToVec3() * joints[parentNum].ToMat3() + jointMod->pos );
   4483 				break;
   4484 
   4485 			case JOINTMOD_WORLD_OVERRIDE:
   4486 				joints[i].SetTranslation( jointMod->pos );
   4487 				break;
   4488 		}
   4489 	}
   4490 
   4491 	// transform the rest of the hierarchy
   4492 	SIMDProcessor->TransformJoints( joints, jointParent, i, numJoints - 1 );
   4493 
   4494 	return true;
   4495 }
   4496 
   4497 /*
   4498 =====================
   4499 idAnimator::ForceUpdate
   4500 =====================
   4501 */
   4502 void idAnimator::ForceUpdate() {
   4503 	lastTransformTime = -1;
   4504 	forceUpdate = true;
   4505 }
   4506 
   4507 /*
   4508 =====================
   4509 idAnimator::ClearForceUpdate
   4510 =====================
   4511 */
   4512 void idAnimator::ClearForceUpdate() {
   4513 	forceUpdate = false;
   4514 }
   4515 
   4516 /*
   4517 =====================
   4518 idAnimator::GetJointTransform>	gamex86.dll!idAnimator::ForceUpdate()  Line 4268	C++
   4519 
   4520 =====================
   4521 */
   4522 bool idAnimator::GetJointTransform( jointHandle_t jointHandle, int currentTime, idVec3 &offset, idMat3 &axis ) {
   4523 	if ( !modelDef || ( jointHandle < 0 ) || ( jointHandle >= modelDef->NumJoints() ) ) {
   4524 		return false;
   4525 	}
   4526 
   4527 	CreateFrame( currentTime, false );
   4528 
   4529 	offset = joints[ jointHandle ].ToVec3();
   4530 	axis = joints[ jointHandle ].ToMat3();
   4531 
   4532 	return true;
   4533 }
   4534 
   4535 /*
   4536 =====================
   4537 idAnimator::GetJointLocalTransform
   4538 =====================
   4539 */
   4540 bool idAnimator::GetJointLocalTransform( jointHandle_t jointHandle, int currentTime, idVec3 &offset, idMat3 &axis ) {
   4541 	if ( !modelDef ) {
   4542 		return false;
   4543 	}
   4544 
   4545 	const idList<jointInfo_t> &modelJoints = modelDef->Joints();
   4546 
   4547 	if ( ( jointHandle < 0 ) || ( jointHandle >= modelJoints.Num() ) ) {
   4548 		return false;
   4549 	}
   4550 
   4551 	// FIXME: overkill
   4552 	CreateFrame( currentTime, false );
   4553 
   4554 	if ( jointHandle > 0 ) {
   4555 		idJointMat m = joints[ jointHandle ];
   4556 		m /= joints[ modelJoints[ jointHandle ].parentNum ];
   4557 		offset = m.ToVec3();
   4558 		axis = m.ToMat3();
   4559 	} else {
   4560 		offset = joints[ jointHandle ].ToVec3();
   4561 		axis = joints[ jointHandle ].ToMat3();
   4562 	}
   4563 
   4564 	return true;
   4565 }
   4566 
   4567 /*
   4568 =====================
   4569 idAnimator::GetJointHandle
   4570 =====================
   4571 */
   4572 jointHandle_t idAnimator::GetJointHandle( const char *name ) const {
   4573 	if ( !modelDef || !modelDef->ModelHandle() ) {
   4574 		return INVALID_JOINT;
   4575 	}
   4576 
   4577 	return modelDef->ModelHandle()->GetJointHandle( name );
   4578 }
   4579 
   4580 /*
   4581 =====================
   4582 idAnimator::GetJointName
   4583 =====================
   4584 */
   4585 const char *idAnimator::GetJointName( jointHandle_t handle ) const {
   4586 	if ( !modelDef || !modelDef->ModelHandle() ) {
   4587 		return "";
   4588 	}
   4589 
   4590 	return modelDef->ModelHandle()->GetJointName( handle );
   4591 }
   4592 
   4593 /*
   4594 =====================
   4595 idAnimator::GetChannelForJoint
   4596 =====================
   4597 */
   4598 int idAnimator::GetChannelForJoint( jointHandle_t joint ) const {
   4599 	if ( !modelDef ) {
   4600 		gameLocal.Error( "idAnimator::GetChannelForJoint: NULL model" );
   4601 		return -1;
   4602 	}
   4603 
   4604 	if ( ( joint < 0 ) || ( joint >= numJoints ) ) {
   4605 		gameLocal.Error( "idAnimator::GetChannelForJoint: invalid joint num (%d)", joint );
   4606 		return -1;
   4607 	}
   4608 
   4609 	return modelDef->GetJoint( joint )->channel;
   4610 }
   4611 
   4612 /*
   4613 =====================
   4614 idAnimator::GetFirstChild
   4615 =====================
   4616 */
   4617 jointHandle_t idAnimator::GetFirstChild( const char *name ) const {
   4618 	return GetFirstChild( GetJointHandle( name ) );
   4619 }
   4620 
   4621 /*
   4622 =====================
   4623 idAnimator::GetFirstChild
   4624 =====================
   4625 */
   4626 jointHandle_t idAnimator::GetFirstChild( jointHandle_t jointnum ) const {
   4627 	int					i;
   4628 	int					num;
   4629 	const jointInfo_t	*joint;
   4630 
   4631 	if ( !modelDef ) {
   4632 		return INVALID_JOINT;
   4633 	}
   4634 
   4635 	num = modelDef->NumJoints();
   4636 	if ( !num ) {
   4637 		return jointnum;
   4638 	}
   4639 	joint = modelDef->GetJoint( 0 );
   4640 	for( i = 0; i < num; i++, joint++ ) {
   4641 		if ( joint->parentNum == jointnum ) {
   4642 			return ( jointHandle_t )joint->num;
   4643 		}
   4644 	}
   4645 	return jointnum;
   4646 }
   4647 
   4648 /*
   4649 =====================
   4650 idAnimator::GetJoints
   4651 =====================
   4652 */
   4653 void idAnimator::GetJoints( int *numJoints, idJointMat **jointsPtr ) {
   4654 	*numJoints = this->numJoints;
   4655 	*jointsPtr = this->joints;
   4656 }
   4657 
   4658 /*
   4659 =====================
   4660 idAnimator::GetAnimFlags
   4661 =====================
   4662 */
   4663 const animFlags_t idAnimator::GetAnimFlags( int animNum ) const {
   4664 	animFlags_t result;
   4665 
   4666 	const idAnim *anim = GetAnim( animNum );
   4667 	if ( anim ) {
   4668 		return anim->GetAnimFlags();
   4669 	}
   4670 
   4671 	memset( &result, 0, sizeof( result ) );
   4672 	return result;
   4673 }
   4674 
   4675 /*
   4676 =====================
   4677 idAnimator::NumFrames
   4678 =====================
   4679 */
   4680 int	idAnimator::NumFrames( int animNum ) const {
   4681 	const idAnim *anim = GetAnim( animNum );
   4682 	if ( anim ) {
   4683 		return anim->NumFrames();
   4684 	} else {
   4685 		return 0;
   4686 	}
   4687 }
   4688 
   4689 /*
   4690 =====================
   4691 idAnimator::NumSyncedAnims
   4692 =====================
   4693 */
   4694 int	idAnimator::NumSyncedAnims( int animNum ) const {
   4695 	const idAnim *anim = GetAnim( animNum );
   4696 	if ( anim ) {
   4697 		return anim->NumAnims();
   4698 	} else {
   4699 		return 0;
   4700 	}
   4701 }
   4702 
   4703 /*
   4704 =====================
   4705 idAnimator::AnimName
   4706 =====================
   4707 */
   4708 const char *idAnimator::AnimName( int animNum ) const {
   4709 	const idAnim *anim = GetAnim( animNum );
   4710 	if ( anim ) {
   4711 		return anim->Name();
   4712 	} else {
   4713 		return "";
   4714 	}
   4715 }
   4716 
   4717 /*
   4718 =====================
   4719 idAnimator::AnimFullName
   4720 =====================
   4721 */
   4722 const char *idAnimator::AnimFullName( int animNum ) const {
   4723 	const idAnim *anim = GetAnim( animNum );
   4724 	if ( anim ) {
   4725 		return anim->FullName();
   4726 	} else {
   4727 		return "";
   4728 	}
   4729 }
   4730 
   4731 /*
   4732 =====================
   4733 idAnimator::AnimLength
   4734 =====================
   4735 */
   4736 int	idAnimator::AnimLength( int animNum ) const {
   4737 	const idAnim *anim = GetAnim( animNum );
   4738 	if ( anim ) {
   4739 		return anim->Length();
   4740 	} else {
   4741 		return 0;
   4742 	}
   4743 }
   4744 
   4745 /*
   4746 =====================
   4747 idAnimator::TotalMovementDelta
   4748 =====================
   4749 */
   4750 const idVec3 &idAnimator::TotalMovementDelta( int animNum ) const {
   4751 	const idAnim *anim = GetAnim( animNum );
   4752 	if ( anim ) {
   4753 		return anim->TotalMovementDelta();
   4754 	} else {
   4755 		return vec3_origin;
   4756 	}
   4757 }
   4758 
   4759 /***********************************************************************
   4760 
   4761 	Util functions
   4762 
   4763 ***********************************************************************/
   4764 
   4765 /*
   4766 =====================
   4767 ANIM_GetModelDefFromEntityDef
   4768 =====================
   4769 */
   4770 const idDeclModelDef *ANIM_GetModelDefFromEntityDef( const idDict *args ) {
   4771 	const idDeclModelDef *modelDef;
   4772 
   4773 	idStr name = args->GetString( "model" );
   4774 	modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, name, false ) );
   4775 	if ( modelDef != NULL && modelDef->ModelHandle() ) {
   4776 		return modelDef;
   4777 	}
   4778 
   4779 	return NULL;
   4780 }
   4781 
   4782 /*
   4783 =====================
   4784 idGameEdit::ANIM_GetModelFromEntityDef
   4785 =====================
   4786 */
   4787 idRenderModel *idGameEdit::ANIM_GetModelFromEntityDef( const idDict *args ) {
   4788 	idRenderModel *model;
   4789 	const idDeclModelDef *modelDef;
   4790 
   4791 	model = NULL;
   4792 
   4793 	idStr name = args->GetString( "model" );
   4794 	modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, name, false ) );
   4795 	if ( modelDef != NULL ) {
   4796 		model = modelDef->ModelHandle();
   4797 	}
   4798 
   4799 	if ( model == NULL ) {
   4800 		model = renderModelManager->FindModel( name );
   4801 	}
   4802 
   4803 	if ( model != NULL && model->IsDefaultModel() ) {
   4804 		return NULL;
   4805 	}
   4806 
   4807 	return model;
   4808 }
   4809 
   4810 /*
   4811 =====================
   4812 idGameEdit::ANIM_GetModelFromEntityDef
   4813 =====================
   4814 */
   4815 idRenderModel *idGameEdit::ANIM_GetModelFromEntityDef( const char *classname ) {
   4816 	const idDict *args;
   4817 
   4818 	args = gameLocal.FindEntityDefDict( classname, false );
   4819 	if ( !args ) {
   4820 		return NULL;
   4821 	}
   4822 
   4823 	return ANIM_GetModelFromEntityDef( args );
   4824 }
   4825 
   4826 /*
   4827 =====================
   4828 idGameEdit::ANIM_GetModelOffsetFromEntityDef
   4829 =====================
   4830 */
   4831 const idVec3 &idGameEdit::ANIM_GetModelOffsetFromEntityDef( const char *classname ) {
   4832 	const idDict *args;
   4833 	const idDeclModelDef *modelDef;
   4834 
   4835 	args = gameLocal.FindEntityDefDict( classname, false );
   4836 	if ( !args ) {
   4837 		return vec3_origin;
   4838 	}
   4839 
   4840 	modelDef = ANIM_GetModelDefFromEntityDef( args );
   4841 	if ( !modelDef ) {
   4842 		return vec3_origin;
   4843 	}
   4844 
   4845 	return modelDef->GetVisualOffset();
   4846 }
   4847 
   4848 /*
   4849 =====================
   4850 idGameEdit::ANIM_GetModelFromName
   4851 =====================
   4852 */
   4853 idRenderModel *idGameEdit::ANIM_GetModelFromName( const char *modelName ) {
   4854 	const idDeclModelDef *modelDef;
   4855 	idRenderModel *model;
   4856 
   4857 	model = NULL;
   4858 	modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelName, false ) );
   4859 	if ( modelDef ) {
   4860 		model = modelDef->ModelHandle();
   4861 	}
   4862 	if ( !model ) {
   4863 		model = renderModelManager->FindModel( modelName );
   4864 	}
   4865 	return model;
   4866 }
   4867 
   4868 /*
   4869 =====================
   4870 idGameEdit::ANIM_GetAnimFromEntityDef
   4871 =====================
   4872 */
   4873 const idMD5Anim *idGameEdit::ANIM_GetAnimFromEntityDef( const char *classname, const char *animname ) {
   4874 	const idDict *args;
   4875 	const idMD5Anim *md5anim;
   4876 	const idAnim *anim;
   4877 	int	animNum;
   4878 	const char	*modelname;
   4879 	const idDeclModelDef *modelDef;
   4880 
   4881 	args = gameLocal.FindEntityDefDict( classname, false );
   4882 	if ( !args ) {
   4883 		return NULL;
   4884 	}
   4885 
   4886 	md5anim = NULL;
   4887 	modelname = args->GetString( "model" );
   4888 	modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelname, false ) );
   4889 	if ( modelDef ) {
   4890 		animNum = modelDef->GetAnim( animname );
   4891 		if ( animNum ) {
   4892 			anim = modelDef->GetAnim( animNum );
   4893 			if ( anim ) {
   4894 				md5anim = anim->MD5Anim( 0 );
   4895 			}
   4896 		}
   4897 	}
   4898 	return md5anim;
   4899 }
   4900 
   4901 /*
   4902 =====================
   4903 idGameEdit::ANIM_GetNumAnimsFromEntityDef
   4904 =====================
   4905 */
   4906 int idGameEdit::ANIM_GetNumAnimsFromEntityDef( const idDict *args ) {
   4907 	const char *modelname;
   4908 	const idDeclModelDef *modelDef;
   4909 
   4910 	modelname = args->GetString( "model" );
   4911 	modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelname, false ) );
   4912 	if ( modelDef ) {
   4913 		return modelDef->NumAnims();
   4914 	}
   4915 	return 0;
   4916 }
   4917 
   4918 /*
   4919 =====================
   4920 idGameEdit::ANIM_GetAnimNameFromEntityDef
   4921 =====================
   4922 */
   4923 const char *idGameEdit::ANIM_GetAnimNameFromEntityDef( const idDict *args, int animNum ) {
   4924 	const char *modelname;
   4925 	const idDeclModelDef *modelDef;
   4926 
   4927 	modelname = args->GetString( "model" );
   4928 	modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, modelname, false ) );
   4929 	if ( modelDef ) {
   4930 		const idAnim* anim = modelDef->GetAnim( animNum );
   4931 		if ( anim ) {
   4932 			return anim->FullName();
   4933 		}
   4934 	}
   4935 	return "";
   4936 }
   4937 
   4938 /*
   4939 =====================
   4940 idGameEdit::ANIM_GetAnim
   4941 =====================
   4942 */
   4943 const idMD5Anim *idGameEdit::ANIM_GetAnim( const char *fileName ) {
   4944 	return animationLib.GetAnim( fileName );
   4945 }
   4946 
   4947 /*
   4948 =====================
   4949 idGameEdit::ANIM_GetLength
   4950 =====================
   4951 */
   4952 int	idGameEdit::ANIM_GetLength( const idMD5Anim *anim ) {
   4953 	if ( !anim ) {
   4954 		return 0;
   4955 	}
   4956 	return anim->Length();
   4957 }
   4958 
   4959 /*
   4960 =====================
   4961 idGameEdit::ANIM_GetNumFrames
   4962 =====================
   4963 */
   4964 int idGameEdit::ANIM_GetNumFrames( const idMD5Anim *anim ) {
   4965 	if ( !anim ) {
   4966 		return 0;
   4967 	}
   4968 	return anim->NumFrames();
   4969 }
   4970 
   4971 /*
   4972 =====================
   4973 idGameEdit::ANIM_CreateAnimFrame
   4974 =====================
   4975 */
   4976 void idGameEdit::ANIM_CreateAnimFrame( const idRenderModel *model, const idMD5Anim *anim, int numJoints, idJointMat *joints, int time, const idVec3 &offset, bool remove_origin_offset ) {
   4977 	int					i;
   4978 	frameBlend_t		frame;
   4979 	const idMD5Joint	*md5joints;
   4980 	int					*index;
   4981 
   4982 	if ( !model || model->IsDefaultModel() || !anim ) {
   4983 		return;
   4984 	}
   4985 
   4986 	if ( numJoints != model->NumJoints() ) {
   4987 		gameLocal.Error( "ANIM_CreateAnimFrame: different # of joints in renderEntity_t than in model (%s)", model->Name() );
   4988 	}
   4989 
   4990 	if ( !model->NumJoints() ) {
   4991 		// FIXME: Print out a warning?
   4992 		return;
   4993 	}
   4994 
   4995 	if ( !joints ) {
   4996 		gameLocal.Error( "ANIM_CreateAnimFrame: NULL joint frame pointer on model (%s)", model->Name() );
   4997 	}
   4998 
   4999 	if ( numJoints != anim->NumJoints() ) {
   5000 		gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", model->Name(), anim->Name() );
   5001 		for( i = 0; i < numJoints; i++ ) {
   5002 			joints[i].SetRotation( mat3_identity );
   5003 			joints[i].SetTranslation( offset );
   5004 		}
   5005 		return;
   5006 	}
   5007 
   5008 	// create index for all joints
   5009 	index = ( int * )_alloca16( numJoints * sizeof( int ) );
   5010 	for ( i = 0; i < numJoints; i++ ) {
   5011 		index[i] = i;
   5012 	}
   5013 
   5014 	// create the frame
   5015 	anim->ConvertTimeToFrame( time, 1, frame );
   5016 	idJointQuat *jointFrame = ( idJointQuat * )_alloca16( numJoints * sizeof( *jointFrame ) );
   5017 	anim->GetInterpolatedFrame( frame, jointFrame, index, numJoints );
   5018 
   5019 	// convert joint quaternions to joint matrices
   5020 	SIMDProcessor->ConvertJointQuatsToJointMats( joints, jointFrame, numJoints );
   5021 
   5022 	// first joint is always root of entire hierarchy
   5023 	if ( remove_origin_offset ) {
   5024 		joints[0].SetTranslation( offset );
   5025 	} else {
   5026 		joints[0].SetTranslation( joints[0].ToVec3() + offset );
   5027 	}
   5028 
   5029 	// transform the children
   5030 	md5joints = model->GetJoints();
   5031 	for( i = 1; i < numJoints; i++ ) {
   5032 		joints[i] *= joints[ md5joints[i].parent - md5joints ];
   5033 	}
   5034 }
   5035 
   5036 /*
   5037 =====================
   5038 idGameEdit::ANIM_CreateMeshForAnim
   5039 =====================
   5040 */
   5041 idRenderModel *idGameEdit::ANIM_CreateMeshForAnim( idRenderModel *model, const char *classname, const char *animname, int frame, bool remove_origin_offset ) {
   5042 	renderEntity_t			ent;
   5043 	const idDict			*args;
   5044 	const char				*temp;
   5045 	idRenderModel			*newmodel;
   5046 	const idMD5Anim 		*md5anim;
   5047 	idStr					filename;
   5048 	idStr					extension;
   5049 	const idAnim			*anim;
   5050 	int						animNum;
   5051 	idVec3					offset;
   5052 	const idDeclModelDef	*modelDef;
   5053 
   5054 	if ( !model || model->IsDefaultModel() ) {
   5055 		return NULL;
   5056 	}
   5057 
   5058 	args = gameLocal.FindEntityDefDict( classname, false );
   5059 	if ( !args ) {
   5060 		return NULL;
   5061 	}
   5062 
   5063 	memset( &ent, 0, sizeof( ent ) );
   5064 
   5065 	ent.bounds.Clear();
   5066 	ent.suppressSurfaceInViewID = 0;
   5067 
   5068 	modelDef = ANIM_GetModelDefFromEntityDef( args );
   5069 	if ( modelDef ) {
   5070 		animNum = modelDef->GetAnim( animname );
   5071 		if ( !animNum ) {
   5072 			return NULL;
   5073 		}
   5074 		anim = modelDef->GetAnim( animNum );
   5075 		if ( !anim ) {
   5076 			return NULL;
   5077 		}
   5078 		md5anim = anim->MD5Anim( 0 );
   5079 		ent.customSkin = modelDef->GetDefaultSkin();
   5080 		offset = modelDef->GetVisualOffset();
   5081 	} else {
   5082 		filename = animname;
   5083 		filename.ExtractFileExtension( extension );
   5084 		if ( !extension.Length() ) {
   5085 			animname = args->GetString( va( "anim %s", animname ) );
   5086 		}
   5087 
   5088 		md5anim = animationLib.GetAnim( animname );
   5089 		offset.Zero();
   5090 	}
   5091 
   5092 	if ( !md5anim ) {
   5093 		return NULL;
   5094 	}
   5095 
   5096 	temp = args->GetString( "skin", "" );
   5097 	if ( temp[ 0 ] ) {
   5098 		ent.customSkin = declManager->FindSkin( temp );
   5099 	}
   5100 
   5101 	ent.numJoints = model->NumJoints();
   5102 	ent.joints = ( idJointMat * )Mem_Alloc16( SIMD_ROUND_JOINTS( ent.numJoints ) * sizeof( *ent.joints ), TAG_JOINTMAT );
   5103 
   5104 	ANIM_CreateAnimFrame( model, md5anim, ent.numJoints, ent.joints, FRAME2MS( frame ), offset, remove_origin_offset );
   5105 
   5106 	SIMD_INIT_LAST_JOINT( ent.joints, ent.numJoints );
   5107 
   5108 	newmodel = model->InstantiateDynamicModel( &ent, NULL, NULL );
   5109 
   5110 	Mem_Free16( ent.joints );
   5111 	ent.joints = NULL;
   5112 
   5113 	return newmodel;
   5114 }