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