/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 Source Code. If not, see . 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. 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. =========================================================================== */ #include "sys/platform.h" #include "idlib/containers/BinSearch.h" #include "idlib/geometry/JointTransform.h" #include "idlib/math/Quat.h" #include "renderer/ModelManager.h" #include "gamesys/SysCvar.h" #include "ai/AI.h" #include "Entity.h" #include "Fx.h" #include "Game_local.h" #include "anim/Anim.h" static const char *channelNames[ ANIM_NumAnimChannels ] = { "all", "torso", "legs", "head", "eyelids" }; /*********************************************************************** idAnim ***********************************************************************/ /* ===================== idAnim::idAnim ===================== */ idAnim::idAnim() { modelDef = NULL; numAnims = 0; memset( anims, 0, sizeof( anims ) ); memset( &flags, 0, sizeof( flags ) ); } /* ===================== idAnim::idAnim ===================== */ idAnim::idAnim( const idDeclModelDef *modelDef, const idAnim *anim ) { int i; this->modelDef = modelDef; numAnims = anim->numAnims; name = anim->name; realname = anim->realname; flags = anim->flags; memset( anims, 0, sizeof( anims ) ); for( i = 0; i < numAnims; i++ ) { anims[ i ] = anim->anims[ i ]; anims[ i ]->IncreaseRefs(); } frameLookup.SetNum( anim->frameLookup.Num() ); memcpy( frameLookup.Ptr(), anim->frameLookup.Ptr(), frameLookup.MemoryUsed() ); frameCommands.SetNum( anim->frameCommands.Num() ); for( i = 0; i < frameCommands.Num(); i++ ) { frameCommands[ i ] = anim->frameCommands[ i ]; if ( anim->frameCommands[ i ].string ) { frameCommands[ i ].string = new idStr( *anim->frameCommands[ i ].string ); } } } /* ===================== idAnim::~idAnim ===================== */ idAnim::~idAnim() { int i; for( i = 0; i < numAnims; i++ ) { anims[ i ]->DecreaseRefs(); } for( i = 0; i < frameCommands.Num(); i++ ) { delete frameCommands[ i ].string; } } /* ===================== idAnim::SetAnim ===================== */ void idAnim::SetAnim( const idDeclModelDef *modelDef, const char *sourcename, const char *animname, int num, const idMD5Anim *md5anims[ ANIM_MaxSyncedAnims ] ) { int i; this->modelDef = modelDef; for( i = 0; i < numAnims; i++ ) { anims[ i ]->DecreaseRefs(); anims[ i ] = NULL; } assert( ( num > 0 ) && ( num <= ANIM_MaxSyncedAnims ) ); numAnims = num; realname = sourcename; name = animname; for( i = 0; i < num; i++ ) { anims[ i ] = md5anims[ i ]; anims[ i ]->IncreaseRefs(); } memset( &flags, 0, sizeof( flags ) ); for( i = 0; i < frameCommands.Num(); i++ ) { delete frameCommands[ i ].string; } frameLookup.Clear(); frameCommands.Clear(); } /* ===================== idAnim::Name ===================== */ const char *idAnim::Name( void ) const { return name; } /* ===================== idAnim::FullName ===================== */ const char *idAnim::FullName( void ) const { return realname; } /* ===================== idAnim::MD5Anim index 0 will never be NULL. Any anim >= NumAnims will return NULL. ===================== */ const idMD5Anim *idAnim::MD5Anim( int num ) const { if ( anims[0] == NULL ) { return NULL; } return anims[ num ]; } /* ===================== idAnim::ModelDef ===================== */ const idDeclModelDef *idAnim::ModelDef( void ) const { return modelDef; } /* ===================== idAnim::Length ===================== */ int idAnim::Length( void ) const { if ( !anims[ 0 ] ) { return 0; } return anims[ 0 ]->Length(); } /* ===================== idAnim::NumFrames ===================== */ int idAnim::NumFrames( void ) const { if ( !anims[ 0 ] ) { return 0; } return anims[ 0 ]->NumFrames(); } /* ===================== idAnim::NumAnims ===================== */ int idAnim::NumAnims( void ) const { return numAnims; } /* ===================== idAnim::TotalMovementDelta ===================== */ const idVec3 &idAnim::TotalMovementDelta( void ) const { if ( !anims[ 0 ] ) { return vec3_zero; } return anims[ 0 ]->TotalMovementDelta(); } /* ===================== idAnim::GetOrigin ===================== */ bool idAnim::GetOrigin( idVec3 &offset, int animNum, int currentTime, int cyclecount ) const { if ( !anims[ animNum ] ) { offset.Zero(); return false; } anims[ animNum ]->GetOrigin( offset, currentTime, cyclecount ); return true; } /* ===================== idAnim::GetOriginRotation ===================== */ bool idAnim::GetOriginRotation( idQuat &rotation, int animNum, int currentTime, int cyclecount ) const { if ( !anims[ animNum ] ) { rotation.Set( 0.0f, 0.0f, 0.0f, 1.0f ); return false; } anims[ animNum ]->GetOriginRotation( rotation, currentTime, cyclecount ); return true; } /* ===================== idAnim::GetBounds ===================== */ ID_INLINE bool idAnim::GetBounds( idBounds &bounds, int animNum, int currentTime, int cyclecount ) const { if ( !anims[ animNum ] ) { return false; } anims[ animNum ]->GetBounds( bounds, currentTime, cyclecount ); return true; } /* ===================== idAnim::AddFrameCommand Returns NULL if no error. ===================== */ const char *idAnim::AddFrameCommand( const idDeclModelDef *modelDef, int framenum, idLexer &src, const idDict *def ) { int i; int index; idStr text; idStr funcname; frameCommand_t fc; idToken token; const jointInfo_t *jointInfo; // make sure we're within bounds if ( ( framenum < 1 ) || ( framenum > anims[ 0 ]->NumFrames() ) ) { return va( "Frame %d out of range", framenum ); } // frame numbers are 1 based in .def files, but 0 based internally framenum--; memset( &fc, 0, sizeof( fc ) ); if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } if ( token == "call" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SCRIPTFUNCTION; fc.function = gameLocal.program.FindFunction( token ); if ( !fc.function ) { return va( "Function '%s' not found", token.c_str() ); } } else if ( token == "object_call" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SCRIPTFUNCTIONOBJECT; fc.string = new idStr( token ); } else if ( token == "event" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_EVENTFUNCTION; const idEventDef *ev = idEventDef::FindEvent( token ); if ( !ev ) { return va( "Event '%s' not found", token.c_str() ); } if ( ev->GetNumArgs() != 0 ) { return va( "Event '%s' has arguments", token.c_str() ); } fc.string = new idStr( token ); } else if ( token == "sound" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SOUND; if ( !token.Cmpn( "snd_", 4 ) ) { fc.string = new idStr( token ); } else { fc.soundShader = declManager->FindSound( token ); if ( fc.soundShader->GetState() == DS_DEFAULTED ) { gameLocal.Warning( "Sound '%s' not found", token.c_str() ); } } } else if ( token == "sound_voice" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SOUND_VOICE; if ( !token.Cmpn( "snd_", 4 ) ) { fc.string = new idStr( token ); } else { fc.soundShader = declManager->FindSound( token ); if ( fc.soundShader->GetState() == DS_DEFAULTED ) { gameLocal.Warning( "Sound '%s' not found", token.c_str() ); } } } else if ( token == "sound_voice2" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SOUND_VOICE2; if ( !token.Cmpn( "snd_", 4 ) ) { fc.string = new idStr( token ); } else { fc.soundShader = declManager->FindSound( token ); if ( fc.soundShader->GetState() == DS_DEFAULTED ) { gameLocal.Warning( "Sound '%s' not found", token.c_str() ); } } } else if ( token == "sound_body" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SOUND_BODY; if ( !token.Cmpn( "snd_", 4 ) ) { fc.string = new idStr( token ); } else { fc.soundShader = declManager->FindSound( token ); if ( fc.soundShader->GetState() == DS_DEFAULTED ) { gameLocal.Warning( "Sound '%s' not found", token.c_str() ); } } } else if ( token == "sound_body2" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SOUND_BODY2; if ( !token.Cmpn( "snd_", 4 ) ) { fc.string = new idStr( token ); } else { fc.soundShader = declManager->FindSound( token ); if ( fc.soundShader->GetState() == DS_DEFAULTED ) { gameLocal.Warning( "Sound '%s' not found", token.c_str() ); } } } else if ( token == "sound_body3" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SOUND_BODY3; if ( !token.Cmpn( "snd_", 4 ) ) { fc.string = new idStr( token ); } else { fc.soundShader = declManager->FindSound( token ); if ( fc.soundShader->GetState() == DS_DEFAULTED ) { gameLocal.Warning( "Sound '%s' not found", token.c_str() ); } } } else if ( token == "sound_weapon" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SOUND_WEAPON; if ( !token.Cmpn( "snd_", 4 ) ) { fc.string = new idStr( token ); } else { fc.soundShader = declManager->FindSound( token ); if ( fc.soundShader->GetState() == DS_DEFAULTED ) { gameLocal.Warning( "Sound '%s' not found", token.c_str() ); } } } else if ( token == "sound_global" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SOUND_GLOBAL; if ( !token.Cmpn( "snd_", 4 ) ) { fc.string = new idStr( token ); } else { fc.soundShader = declManager->FindSound( token ); if ( fc.soundShader->GetState() == DS_DEFAULTED ) { gameLocal.Warning( "Sound '%s' not found", token.c_str() ); } } } else if ( token == "sound_item" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SOUND_ITEM; if ( !token.Cmpn( "snd_", 4 ) ) { fc.string = new idStr( token ); } else { fc.soundShader = declManager->FindSound( token ); if ( fc.soundShader->GetState() == DS_DEFAULTED ) { gameLocal.Warning( "Sound '%s' not found", token.c_str() ); } } } else if ( token == "sound_chatter" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SOUND_CHATTER; if ( !token.Cmpn( "snd_", 4 ) ) { fc.string = new idStr( token ); } else { fc.soundShader = declManager->FindSound( token ); if ( fc.soundShader->GetState() == DS_DEFAULTED ) { gameLocal.Warning( "Sound '%s' not found", token.c_str() ); } } } else if ( token == "skin" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_SKIN; if ( token == "none" ) { fc.skin = NULL; } else { fc.skin = declManager->FindSkin( token ); if ( !fc.skin ) { return va( "Skin '%s' not found", token.c_str() ); } } } else if ( token == "fx" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_FX; if ( !declManager->FindType( DECL_FX, token.c_str() ) ) { return va( "fx '%s' not found", token.c_str() ); } fc.string = new idStr( token ); } else if ( token == "trigger" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_TRIGGER; fc.string = new idStr( token ); } else if ( token == "triggerSmokeParticle" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_TRIGGER_SMOKE_PARTICLE; fc.string = new idStr( token ); } else if ( token == "melee" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_MELEE; if ( !gameLocal.FindEntityDef( token.c_str(), false ) ) { return va( "Unknown entityDef '%s'", token.c_str() ); } fc.string = new idStr( token ); } else if ( token == "direct_damage" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_DIRECTDAMAGE; if ( !gameLocal.FindEntityDef( token.c_str(), false ) ) { return va( "Unknown entityDef '%s'", token.c_str() ); } fc.string = new idStr( token ); } else if ( token == "attack_begin" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_BEGINATTACK; if ( !gameLocal.FindEntityDef( token.c_str(), false ) ) { return va( "Unknown entityDef '%s'", token.c_str() ); } fc.string = new idStr( token ); } else if ( token == "attack_end" ) { fc.type = FC_ENDATTACK; } else if ( token == "muzzle_flash" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } if ( ( token != "" ) && !modelDef->FindJoint( token ) ) { return va( "Joint '%s' not found", token.c_str() ); } fc.type = FC_MUZZLEFLASH; fc.string = new idStr( token ); } else if ( token == "muzzle_flash" ) { fc.type = FC_MUZZLEFLASH; fc.string = new idStr( "" ); } else if ( token == "create_missile" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } if ( !modelDef->FindJoint( token ) ) { return va( "Joint '%s' not found", token.c_str() ); } fc.type = FC_CREATEMISSILE; fc.string = new idStr( token ); } else if ( token == "launch_missile" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } if ( !modelDef->FindJoint( token ) ) { return va( "Joint '%s' not found", token.c_str() ); } fc.type = FC_LAUNCHMISSILE; fc.string = new idStr( token ); } else if ( token == "fire_missile_at_target" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } jointInfo = modelDef->FindJoint( token ); if ( !jointInfo ) { return va( "Joint '%s' not found", token.c_str() ); } if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_FIREMISSILEATTARGET; fc.string = new idStr( token ); fc.index = jointInfo->num; } else if ( token == "footstep" ) { fc.type = FC_FOOTSTEP; } else if ( token == "leftfoot" ) { fc.type = FC_LEFTFOOT; } else if ( token == "rightfoot" ) { fc.type = FC_RIGHTFOOT; } else if ( token == "enableEyeFocus" ) { fc.type = FC_ENABLE_EYE_FOCUS; } else if ( token == "disableEyeFocus" ) { fc.type = FC_DISABLE_EYE_FOCUS; } else if ( token == "disableGravity" ) { fc.type = FC_DISABLE_GRAVITY; } else if ( token == "enableGravity" ) { fc.type = FC_ENABLE_GRAVITY; } else if ( token == "jump" ) { fc.type = FC_JUMP; } else if ( token == "enableClip" ) { fc.type = FC_ENABLE_CLIP; } else if ( token == "disableClip" ) { fc.type = FC_DISABLE_CLIP; } else if ( token == "enableWalkIK" ) { fc.type = FC_ENABLE_WALK_IK; } else if ( token == "disableWalkIK" ) { fc.type = FC_DISABLE_WALK_IK; } else if ( token == "enableLegIK" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_ENABLE_LEG_IK; fc.index = atoi( token ); } else if ( token == "disableLegIK" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_DISABLE_LEG_IK; fc.index = atoi( token ); } else if ( token == "recordDemo" ) { fc.type = FC_RECORDDEMO; if( src.ReadTokenOnLine( &token ) ) { fc.string = new idStr( token ); } } else if ( token == "aviGame" ) { fc.type = FC_AVIGAME; if( src.ReadTokenOnLine( &token ) ) { fc.string = new idStr( token ); } } else { return va( "Unknown command '%s'", token.c_str() ); } // check if we've initialized the frame loopup table if ( !frameLookup.Num() ) { // we haven't, so allocate the table and initialize it frameLookup.SetGranularity( 1 ); frameLookup.SetNum( anims[ 0 ]->NumFrames() ); for( i = 0; i < frameLookup.Num(); i++ ) { frameLookup[ i ].num = 0; frameLookup[ i ].firstCommand = 0; } } // allocate space for a new command frameCommands.Alloc(); // calculate the index of the new command index = frameLookup[ framenum ].firstCommand + frameLookup[ framenum ].num; // move all commands from our index onward up one to give us space for our new command for( i = frameCommands.Num() - 1; i > index; i-- ) { frameCommands[ i ] = frameCommands[ i - 1 ]; } // fix the indices of any later frames to account for the inserted command for( i = framenum + 1; i < frameLookup.Num(); i++ ) { frameLookup[ i ].firstCommand++; } // store the new command frameCommands[ index ] = fc; // increase the number of commands on this frame frameLookup[ framenum ].num++; // return with no error return NULL; } /* ===================== idAnim::CallFrameCommands ===================== */ void idAnim::CallFrameCommands( idEntity *ent, int from, int to ) const { int index; int end; int frame; int numframes; numframes = anims[ 0 ]->NumFrames(); frame = from; while( frame != to ) { frame++; if ( frame >= numframes ) { frame = 0; } index = frameLookup[ frame ].firstCommand; end = index + frameLookup[ frame ].num; while( index < end ) { const frameCommand_t &command = frameCommands[ index++ ]; switch( command.type ) { case FC_SCRIPTFUNCTION: { gameLocal.CallFrameCommand( ent, command.function ); break; } case FC_SCRIPTFUNCTIONOBJECT: { gameLocal.CallObjectFrameCommand( ent, command.string->c_str() ); break; } case FC_EVENTFUNCTION: { const idEventDef *ev = idEventDef::FindEvent( command.string->c_str() ); ent->ProcessEvent( ev ); break; } case FC_SOUND: { if ( !command.soundShader ) { if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_ANY, 0, false, NULL ) ) { gameLocal.Warning( "Framecommand 'sound' on entity '%s', anim '%s', frame %d: Could not find sound '%s'", ent->name.c_str(), FullName(), frame + 1, command.string->c_str() ); } } else { ent->StartSoundShader( command.soundShader, SND_CHANNEL_ANY, 0, false, NULL ); } break; } case FC_SOUND_VOICE: { if ( !command.soundShader ) { if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_VOICE, 0, false, NULL ) ) { gameLocal.Warning( "Framecommand 'sound_voice' on entity '%s', anim '%s', frame %d: Could not find sound '%s'", ent->name.c_str(), FullName(), frame + 1, command.string->c_str() ); } } else { ent->StartSoundShader( command.soundShader, SND_CHANNEL_VOICE, 0, false, NULL ); } break; } case FC_SOUND_VOICE2: { if ( !command.soundShader ) { if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_VOICE2, 0, false, NULL ) ) { gameLocal.Warning( "Framecommand 'sound_voice2' on entity '%s', anim '%s', frame %d: Could not find sound '%s'", ent->name.c_str(), FullName(), frame + 1, command.string->c_str() ); } } else { ent->StartSoundShader( command.soundShader, SND_CHANNEL_VOICE2, 0, false, NULL ); } break; } case FC_SOUND_BODY: { if ( !command.soundShader ) { if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_BODY, 0, false, NULL ) ) { gameLocal.Warning( "Framecommand 'sound_body' on entity '%s', anim '%s', frame %d: Could not find sound '%s'", ent->name.c_str(), FullName(), frame + 1, command.string->c_str() ); } } else { ent->StartSoundShader( command.soundShader, SND_CHANNEL_BODY, 0, false, NULL ); } break; } case FC_SOUND_BODY2: { if ( !command.soundShader ) { if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_BODY2, 0, false, NULL ) ) { gameLocal.Warning( "Framecommand 'sound_body2' on entity '%s', anim '%s', frame %d: Could not find sound '%s'", ent->name.c_str(), FullName(), frame + 1, command.string->c_str() ); } } else { ent->StartSoundShader( command.soundShader, SND_CHANNEL_BODY2, 0, false, NULL ); } break; } case FC_SOUND_BODY3: { if ( !command.soundShader ) { if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_BODY3, 0, false, NULL ) ) { gameLocal.Warning( "Framecommand 'sound_body3' on entity '%s', anim '%s', frame %d: Could not find sound '%s'", ent->name.c_str(), FullName(), frame + 1, command.string->c_str() ); } } else { ent->StartSoundShader( command.soundShader, SND_CHANNEL_BODY3, 0, false, NULL ); } break; } case FC_SOUND_WEAPON: { if ( !command.soundShader ) { if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_WEAPON, 0, false, NULL ) ) { gameLocal.Warning( "Framecommand 'sound_weapon' on entity '%s', anim '%s', frame %d: Could not find sound '%s'", ent->name.c_str(), FullName(), frame + 1, command.string->c_str() ); } } else { ent->StartSoundShader( command.soundShader, SND_CHANNEL_WEAPON, 0, false, NULL ); } break; } case FC_SOUND_GLOBAL: { if ( !command.soundShader ) { if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL ) ) { gameLocal.Warning( "Framecommand 'sound_global' on entity '%s', anim '%s', frame %d: Could not find sound '%s'", ent->name.c_str(), FullName(), frame + 1, command.string->c_str() ); } } else { ent->StartSoundShader( command.soundShader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL ); } break; } case FC_SOUND_ITEM: { if ( !command.soundShader ) { if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_ITEM, 0, false, NULL ) ) { gameLocal.Warning( "Framecommand 'sound_item' on entity '%s', anim '%s', frame %d: Could not find sound '%s'", ent->name.c_str(), FullName(), frame + 1, command.string->c_str() ); } } else { ent->StartSoundShader( command.soundShader, SND_CHANNEL_ITEM, 0, false, NULL ); } break; } case FC_SOUND_CHATTER: { if ( ent->CanPlayChatterSounds() ) { if ( !command.soundShader ) { if ( !ent->StartSound( command.string->c_str(), SND_CHANNEL_VOICE, 0, false, NULL ) ) { gameLocal.Warning( "Framecommand 'sound_chatter' on entity '%s', anim '%s', frame %d: Could not find sound '%s'", ent->name.c_str(), FullName(), frame + 1, command.string->c_str() ); } } else { ent->StartSoundShader( command.soundShader, SND_CHANNEL_VOICE, 0, false, NULL ); } } break; } case FC_FX: { idEntityFx::StartFx( command.string->c_str(), NULL, NULL, ent, true ); break; } case FC_SKIN: { ent->SetSkin( command.skin ); break; } case FC_TRIGGER: { idEntity *target; target = gameLocal.FindEntity( command.string->c_str() ); if ( target ) { target->Signal( SIG_TRIGGER ); target->ProcessEvent( &EV_Activate, ent ); target->TriggerGuis(); } else { gameLocal.Warning( "Framecommand 'trigger' on entity '%s', anim '%s', frame %d: Could not find entity '%s'", ent->name.c_str(), FullName(), frame + 1, command.string->c_str() ); } break; } case FC_TRIGGER_SMOKE_PARTICLE: { ent->ProcessEvent( &AI_TriggerParticles, command.string->c_str() ); break; } case FC_MELEE: { ent->ProcessEvent( &AI_AttackMelee, command.string->c_str() ); break; } case FC_DIRECTDAMAGE: { ent->ProcessEvent( &AI_DirectDamage, command.string->c_str() ); break; } case FC_BEGINATTACK: { ent->ProcessEvent( &AI_BeginAttack, command.string->c_str() ); break; } case FC_ENDATTACK: { ent->ProcessEvent( &AI_EndAttack ); break; } case FC_MUZZLEFLASH: { ent->ProcessEvent( &AI_MuzzleFlash, command.string->c_str() ); break; } case FC_CREATEMISSILE: { ent->ProcessEvent( &AI_CreateMissile, command.string->c_str() ); break; } case FC_LAUNCHMISSILE: { ent->ProcessEvent( &AI_AttackMissile, command.string->c_str() ); break; } case FC_FIREMISSILEATTARGET: { ent->ProcessEvent( &AI_FireMissileAtTarget, modelDef->GetJointName( command.index ), command.string->c_str() ); break; } case FC_FOOTSTEP : { ent->ProcessEvent( &EV_Footstep ); break; } case FC_LEFTFOOT: { ent->ProcessEvent( &EV_FootstepLeft ); break; } case FC_RIGHTFOOT: { ent->ProcessEvent( &EV_FootstepRight ); break; } case FC_ENABLE_EYE_FOCUS: { ent->ProcessEvent( &AI_EnableEyeFocus ); break; } case FC_DISABLE_EYE_FOCUS: { ent->ProcessEvent( &AI_DisableEyeFocus ); break; } case FC_DISABLE_GRAVITY: { ent->ProcessEvent( &AI_DisableGravity ); break; } case FC_ENABLE_GRAVITY: { ent->ProcessEvent( &AI_EnableGravity ); break; } case FC_JUMP: { ent->ProcessEvent( &AI_JumpFrame ); break; } case FC_ENABLE_CLIP: { ent->ProcessEvent( &AI_EnableClip ); break; } case FC_DISABLE_CLIP: { ent->ProcessEvent( &AI_DisableClip ); break; } case FC_ENABLE_WALK_IK: { ent->ProcessEvent( &EV_EnableWalkIK ); break; } case FC_DISABLE_WALK_IK: { ent->ProcessEvent( &EV_DisableWalkIK ); break; } case FC_ENABLE_LEG_IK: { ent->ProcessEvent( &EV_EnableLegIK, command.index ); break; } case FC_DISABLE_LEG_IK: { ent->ProcessEvent( &EV_DisableLegIK, command.index ); break; } case FC_RECORDDEMO: { if ( command.string ) { cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "recordDemo %s", command.string->c_str() ) ); } else { cmdSystem->BufferCommandText( CMD_EXEC_NOW, "stoprecording" ); } break; } case FC_AVIGAME: { if ( command.string ) { cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "aviGame %s", command.string->c_str() ) ); } else { cmdSystem->BufferCommandText( CMD_EXEC_NOW, "aviGame" ); } break; } } } } } /* ===================== idAnim::FindFrameForFrameCommand ===================== */ int idAnim::FindFrameForFrameCommand( frameCommandType_t framecommand, const frameCommand_t **command ) const { int frame; int index; int numframes; int end; if ( !frameCommands.Num() ) { return -1; } numframes = anims[ 0 ]->NumFrames(); for( frame = 0; frame < numframes; frame++ ) { end = frameLookup[ frame ].firstCommand + frameLookup[ frame ].num; for( index = frameLookup[ frame ].firstCommand; index < end; index++ ) { if ( frameCommands[ index ].type == framecommand ) { if ( command ) { *command = &frameCommands[ index ]; } return frame; } } } if ( command ) { *command = NULL; } return -1; } /* ===================== idAnim::HasFrameCommands ===================== */ bool idAnim::HasFrameCommands( void ) const { if ( !frameCommands.Num() ) { return false; } return true; } /* ===================== idAnim::SetAnimFlags ===================== */ void idAnim::SetAnimFlags( const animFlags_t &animflags ) { flags = animflags; } /* ===================== idAnim::GetAnimFlags ===================== */ const animFlags_t &idAnim::GetAnimFlags( void ) const { return flags; } /*********************************************************************** idAnimBlend ***********************************************************************/ /* ===================== idAnimBlend::idAnimBlend ===================== */ idAnimBlend::idAnimBlend( void ) { Reset( NULL ); } /* ===================== idAnimBlend::Save archives object for save game file ===================== */ void idAnimBlend::Save( idSaveGame *savefile ) const { int i; savefile->WriteInt( starttime ); savefile->WriteInt( endtime ); savefile->WriteInt( timeOffset ); savefile->WriteFloat( rate ); savefile->WriteInt( blendStartTime ); savefile->WriteInt( blendDuration ); savefile->WriteFloat( blendStartValue ); savefile->WriteFloat( blendEndValue ); for( i = 0; i < ANIM_MaxSyncedAnims; i++ ) { savefile->WriteFloat( animWeights[ i ] ); } savefile->WriteShort( cycle ); savefile->WriteShort( frame ); savefile->WriteShort( animNum ); savefile->WriteBool( allowMove ); savefile->WriteBool( allowFrameCommands ); } /* ===================== idAnimBlend::Restore unarchives object from save game file ===================== */ void idAnimBlend::Restore( idRestoreGame *savefile, const idDeclModelDef *modelDef ) { int i; this->modelDef = modelDef; savefile->ReadInt( starttime ); savefile->ReadInt( endtime ); savefile->ReadInt( timeOffset ); savefile->ReadFloat( rate ); savefile->ReadInt( blendStartTime ); savefile->ReadInt( blendDuration ); savefile->ReadFloat( blendStartValue ); savefile->ReadFloat( blendEndValue ); for( i = 0; i < ANIM_MaxSyncedAnims; i++ ) { savefile->ReadFloat( animWeights[ i ] ); } savefile->ReadShort( cycle ); savefile->ReadShort( frame ); savefile->ReadShort( animNum ); if ( !modelDef ) { animNum = 0; } else if ( ( animNum < 0 ) || ( animNum > modelDef->NumAnims() ) ) { gameLocal.Warning( "Anim number %d out of range for model '%s' during save game", animNum, modelDef->GetModelName() ); animNum = 0; } savefile->ReadBool( allowMove ); savefile->ReadBool( allowFrameCommands ); } /* ===================== idAnimBlend::Reset ===================== */ void idAnimBlend::Reset( const idDeclModelDef *_modelDef ) { modelDef = _modelDef; cycle = 1; starttime = 0; endtime = 0; timeOffset = 0; rate = 1.0f; frame = 0; allowMove = true; allowFrameCommands = true; animNum = 0; memset( animWeights, 0, sizeof( animWeights ) ); blendStartValue = 0.0f; blendEndValue = 0.0f; blendStartTime = 0; blendDuration = 0; } /* ===================== idAnimBlend::FullName ===================== */ const char *idAnimBlend::AnimFullName( void ) const { const idAnim *anim = Anim(); if ( !anim ) { return ""; } return anim->FullName(); } /* ===================== idAnimBlend::AnimName ===================== */ const char *idAnimBlend::AnimName( void ) const { const idAnim *anim = Anim(); if ( !anim ) { return ""; } return anim->Name(); } /* ===================== idAnimBlend::NumFrames ===================== */ int idAnimBlend::NumFrames( void ) const { const idAnim *anim = Anim(); if ( !anim ) { return 0; } return anim->NumFrames(); } /* ===================== idAnimBlend::Length ===================== */ int idAnimBlend::Length( void ) const { const idAnim *anim = Anim(); if ( !anim ) { return 0; } return anim->Length(); } /* ===================== idAnimBlend::GetWeight ===================== */ float idAnimBlend::GetWeight( int currentTime ) const { int timeDelta; float frac; float w; timeDelta = currentTime - blendStartTime; if ( timeDelta <= 0 ) { w = blendStartValue; } else if ( timeDelta >= blendDuration ) { w = blendEndValue; } else { frac = ( float )timeDelta / ( float )blendDuration; w = blendStartValue + ( blendEndValue - blendStartValue ) * frac; } return w; } /* ===================== idAnimBlend::GetFinalWeight ===================== */ float idAnimBlend::GetFinalWeight( void ) const { return blendEndValue; } /* ===================== idAnimBlend::SetWeight ===================== */ void idAnimBlend::SetWeight( float newweight, int currentTime, int blendTime ) { blendStartValue = GetWeight( currentTime ); blendEndValue = newweight; blendStartTime = currentTime - 1; blendDuration = blendTime; if ( !newweight ) { endtime = currentTime + blendTime; } } /* ===================== idAnimBlend::NumSyncedAnims ===================== */ int idAnimBlend::NumSyncedAnims( void ) const { const idAnim *anim = Anim(); if ( !anim ) { return 0; } return anim->NumAnims(); } /* ===================== idAnimBlend::SetSyncedAnimWeight ===================== */ bool idAnimBlend::SetSyncedAnimWeight( int num, float weight ) { const idAnim *anim = Anim(); if ( !anim ) { return false; } if ( ( num < 0 ) || ( num > anim->NumAnims() ) ) { return false; } animWeights[ num ] = weight; return true; } /* ===================== idAnimBlend::SetFrame ===================== */ void idAnimBlend::SetFrame( const idDeclModelDef *modelDef, int _animNum, int _frame, int currentTime, int blendTime ) { Reset( modelDef ); if ( !modelDef ) { return; } const idAnim *_anim = modelDef->GetAnim( _animNum ); if ( !_anim ) { return; } const idMD5Anim *md5anim = _anim->MD5Anim( 0 ); if ( modelDef->Joints().Num() != md5anim->NumJoints() ) { gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", modelDef->GetModelName(), md5anim->Name() ); return; } animNum = _animNum; starttime = currentTime; endtime = -1; cycle = -1; animWeights[ 0 ] = 1.0f; frame = _frame; // a frame of 0 means it's not a single frame blend, so we set it to frame + 1 if ( frame <= 0 ) { frame = 1; } else if ( frame > _anim->NumFrames() ) { frame = _anim->NumFrames(); } // set up blend blendEndValue = 1.0f; blendStartTime = currentTime - 1; blendDuration = blendTime; blendStartValue = 0.0f; } /* ===================== idAnimBlend::CycleAnim ===================== */ void idAnimBlend::CycleAnim( const idDeclModelDef *modelDef, int _animNum, int currentTime, int blendTime ) { Reset( modelDef ); if ( !modelDef ) { return; } const idAnim *_anim = modelDef->GetAnim( _animNum ); if ( !_anim ) { return; } const idMD5Anim *md5anim = _anim->MD5Anim( 0 ); if ( modelDef->Joints().Num() != md5anim->NumJoints() ) { gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", modelDef->GetModelName(), md5anim->Name() ); return; } animNum = _animNum; animWeights[ 0 ] = 1.0f; endtime = -1; cycle = -1; if ( _anim->GetAnimFlags().random_cycle_start ) { // start the animation at a random time so that characters don't walk in sync starttime = currentTime - gameLocal.random.RandomFloat() * _anim->Length(); } else { starttime = currentTime; } // set up blend blendEndValue = 1.0f; blendStartTime = currentTime - 1; blendDuration = blendTime; blendStartValue = 0.0f; } /* ===================== idAnimBlend::PlayAnim ===================== */ void idAnimBlend::PlayAnim( const idDeclModelDef *modelDef, int _animNum, int currentTime, int blendTime ) { Reset( modelDef ); if ( !modelDef ) { return; } const idAnim *_anim = modelDef->GetAnim( _animNum ); if ( !_anim ) { return; } const idMD5Anim *md5anim = _anim->MD5Anim( 0 ); if ( modelDef->Joints().Num() != md5anim->NumJoints() ) { gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", modelDef->GetModelName(), md5anim->Name() ); return; } animNum = _animNum; starttime = currentTime; endtime = starttime + _anim->Length(); cycle = 1; animWeights[ 0 ] = 1.0f; // set up blend blendEndValue = 1.0f; blendStartTime = currentTime - 1; blendDuration = blendTime; blendStartValue = 0.0f; } /* ===================== idAnimBlend::Clear ===================== */ void idAnimBlend::Clear( int currentTime, int clearTime ) { if ( !clearTime ) { Reset( modelDef ); } else { SetWeight( 0.0f, currentTime, clearTime ); } } /* ===================== idAnimBlend::IsDone ===================== */ bool idAnimBlend::IsDone( int currentTime ) const { if ( !frame && ( endtime > 0 ) && ( currentTime >= endtime ) ) { return true; } if ( ( blendEndValue <= 0.0f ) && ( currentTime >= ( blendStartTime + blendDuration ) ) ) { return true; } return false; } /* ===================== idAnimBlend::FrameHasChanged ===================== */ bool idAnimBlend::FrameHasChanged( int currentTime ) const { // if we don't have an anim, no change if ( !animNum ) { return false; } // if anim is done playing, no change if ( ( endtime > 0 ) && ( currentTime > endtime ) ) { return false; } // if our blend weight changes, we need to update if ( ( currentTime < ( blendStartTime + blendDuration ) && ( blendStartValue != blendEndValue ) ) ) { return true; } // if we're a single frame anim and this isn't the frame we started on, we don't need to update if ( ( frame || ( NumFrames() == 1 ) ) && ( currentTime != starttime ) ) { return false; } return true; } /* ===================== idAnimBlend::GetCycleCount ===================== */ int idAnimBlend::GetCycleCount( void ) const { return cycle; } /* ===================== idAnimBlend::SetCycleCount ===================== */ void idAnimBlend::SetCycleCount( int count ) { const idAnim *anim = Anim(); if ( !anim ) { cycle = -1; endtime = 0; } else { cycle = count; if ( cycle < 0 ) { cycle = -1; endtime = -1; } else if ( cycle == 0 ) { cycle = 1; // most of the time we're running at the original frame rate, so avoid the int-to-float-to-int conversion if ( rate == 1.0f ) { endtime = starttime - timeOffset + anim->Length(); } else if ( rate != 0.0f ) { endtime = starttime - timeOffset + anim->Length() / rate; } else { endtime = -1; } } else { // most of the time we're running at the original frame rate, so avoid the int-to-float-to-int conversion if ( rate == 1.0f ) { endtime = starttime - timeOffset + anim->Length() * cycle; } else if ( rate != 0.0f ) { endtime = starttime - timeOffset + ( anim->Length() * cycle ) / rate; } else { endtime = -1; } } } } /* ===================== idAnimBlend::SetPlaybackRate ===================== */ void idAnimBlend::SetPlaybackRate( int currentTime, float newRate ) { int animTime; if ( rate == newRate ) { return; } animTime = AnimTime( currentTime ); if ( newRate == 1.0f ) { timeOffset = animTime - ( currentTime - starttime ); } else { timeOffset = animTime - ( currentTime - starttime ) * newRate; } rate = newRate; // update the anim endtime SetCycleCount( cycle ); } /* ===================== idAnimBlend::GetPlaybackRate ===================== */ float idAnimBlend::GetPlaybackRate( void ) const { return rate; } /* ===================== idAnimBlend::SetStartTime ===================== */ void idAnimBlend::SetStartTime( int _startTime ) { starttime = _startTime; // update the anim endtime SetCycleCount( cycle ); } /* ===================== idAnimBlend::GetStartTime ===================== */ int idAnimBlend::GetStartTime( void ) const { if ( !animNum ) { return 0; } return starttime; } /* ===================== idAnimBlend::GetEndTime ===================== */ int idAnimBlend::GetEndTime( void ) const { if ( !animNum ) { return 0; } return endtime; } /* ===================== idAnimBlend::PlayLength ===================== */ int idAnimBlend::PlayLength( void ) const { if ( !animNum ) { return 0; } if ( endtime < 0 ) { return -1; } return endtime - starttime + timeOffset; } /* ===================== idAnimBlend::AllowMovement ===================== */ void idAnimBlend::AllowMovement( bool allow ) { allowMove = allow; } /* ===================== idAnimBlend::AllowFrameCommands ===================== */ void idAnimBlend::AllowFrameCommands( bool allow ) { allowFrameCommands = allow; } /* ===================== idAnimBlend::Anim ===================== */ const idAnim *idAnimBlend::Anim( void ) const { if ( !modelDef ) { return NULL; } const idAnim *anim = modelDef->GetAnim( animNum ); return anim; } /* ===================== idAnimBlend::AnimNum ===================== */ int idAnimBlend::AnimNum( void ) const { return animNum; } /* ===================== idAnimBlend::AnimTime ===================== */ int idAnimBlend::AnimTime( int currentTime ) const { int time; int length; const idAnim *anim = Anim(); if ( anim ) { if ( frame ) { return FRAME2MS( frame - 1 ); } // most of the time we're running at the original frame rate, so avoid the int-to-float-to-int conversion if ( rate == 1.0f ) { time = currentTime - starttime + timeOffset; } else { time = static_cast( ( currentTime - starttime ) * rate ) + timeOffset; } // given enough time, we can easily wrap time around in our frame calculations, so // keep cycling animations' time within the length of the anim. length = anim->Length(); if ( ( cycle < 0 ) && ( length > 0 ) ) { time %= length; // time will wrap after 24 days (oh no!), resulting in negative results for the %. // adding the length gives us the proper result. if ( time < 0 ) { time += length; } } return time; } else { return 0; } } /* ===================== idAnimBlend::GetFrameNumber ===================== */ int idAnimBlend::GetFrameNumber( int currentTime ) const { const idMD5Anim *md5anim; frameBlend_t frameinfo; int animTime; const idAnim *anim = Anim(); if ( !anim ) { return 1; } if ( frame ) { return frame; } md5anim = anim->MD5Anim( 0 ); animTime = AnimTime( currentTime ); md5anim->ConvertTimeToFrame( animTime, cycle, frameinfo ); return frameinfo.frame1 + 1; } /* ===================== idAnimBlend::CallFrameCommands ===================== */ void idAnimBlend::CallFrameCommands( idEntity *ent, int fromtime, int totime ) const { const idMD5Anim *md5anim; frameBlend_t frame1; frameBlend_t frame2; int fromFrameTime; int toFrameTime; if ( !allowFrameCommands || !ent || frame || ( ( endtime > 0 ) && ( fromtime > endtime ) ) ) { return; } const idAnim *anim = Anim(); if ( !anim || !anim->HasFrameCommands() ) { return; } if ( totime <= starttime ) { // don't play until next frame or we'll play commands twice. // this happens on the player sometimes. return; } fromFrameTime = AnimTime( fromtime ); toFrameTime = AnimTime( totime ); if ( toFrameTime < fromFrameTime ) { toFrameTime += anim->Length(); } md5anim = anim->MD5Anim( 0 ); md5anim->ConvertTimeToFrame( fromFrameTime, cycle, frame1 ); md5anim->ConvertTimeToFrame( toFrameTime, cycle, frame2 ); if ( fromFrameTime <= 0 ) { // make sure first frame is called anim->CallFrameCommands( ent, -1, frame2.frame1 ); } else { anim->CallFrameCommands( ent, frame1.frame1, frame2.frame1 ); } } /* ===================== idAnimBlend::BlendAnim ===================== */ bool idAnimBlend::BlendAnim( int currentTime, int channel, int numJoints, idJointQuat *blendFrame, float &blendWeight, bool removeOriginOffset, bool overrideBlend, bool printInfo ) const { int i; float lerp; float mixWeight; const idMD5Anim *md5anim; idJointQuat *ptr; frameBlend_t frametime; idJointQuat *jointFrame; idJointQuat *mixFrame; int numAnims; int time; const idAnim *anim = Anim(); if ( !anim ) { return false; } float weight = GetWeight( currentTime ); if ( blendWeight > 0.0f ) { if ( ( endtime >= 0 ) && ( currentTime >= endtime ) ) { return false; } if ( !weight ) { return false; } if ( overrideBlend ) { blendWeight = 1.0f - weight; } } if ( ( channel == ANIMCHANNEL_ALL ) && !blendWeight ) { // we don't need a temporary buffer, so just store it directly in the blend frame jointFrame = blendFrame; } else { // allocate a temporary buffer to copy the joints from jointFrame = ( idJointQuat * )_alloca16( numJoints * sizeof( *jointFrame ) ); } time = AnimTime( currentTime ); numAnims = anim->NumAnims(); if ( numAnims == 1 ) { md5anim = anim->MD5Anim( 0 ); if ( frame ) { md5anim->GetSingleFrame( frame - 1, jointFrame, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) ); } else { md5anim->ConvertTimeToFrame( time, cycle, frametime ); md5anim->GetInterpolatedFrame( frametime, jointFrame, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) ); } } else { // // need to mix the multipoint anim together first // // allocate a temporary buffer to copy the joints to mixFrame = ( idJointQuat * )_alloca16( numJoints * sizeof( *jointFrame ) ); if ( !frame ) { anim->MD5Anim( 0 )->ConvertTimeToFrame( time, cycle, frametime ); } ptr = jointFrame; mixWeight = 0.0f; for( i = 0; i < numAnims; i++ ) { if ( animWeights[ i ] > 0.0f ) { mixWeight += animWeights[ i ]; lerp = animWeights[ i ] / mixWeight; md5anim = anim->MD5Anim( i ); if ( frame ) { md5anim->GetSingleFrame( frame - 1, ptr, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) ); } else { md5anim->GetInterpolatedFrame( frametime, ptr, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) ); } // only blend after the first anim is mixed in if ( ptr != jointFrame ) { SIMDProcessor->BlendJoints( jointFrame, ptr, lerp, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) ); } ptr = mixFrame; } } if ( !mixWeight ) { return false; } } if ( removeOriginOffset ) { if ( allowMove ) { #ifdef VELOCITY_MOVE jointFrame[ 0 ].t.x = 0.0f; #else jointFrame[ 0 ].t.Zero(); #endif } if ( anim->GetAnimFlags().anim_turn ) { jointFrame[ 0 ].q.Set( -0.70710677f, 0.0f, 0.0f, 0.70710677f ); } } if ( !blendWeight ) { blendWeight = weight; if ( channel != ANIMCHANNEL_ALL ) { const int *index = modelDef->GetChannelJoints( channel ); const int num = modelDef->NumJointsOnChannel( channel ); for( i = 0; i < num; i++ ) { int j = index[i]; blendFrame[j].t = jointFrame[j].t; blendFrame[j].q = jointFrame[j].q; } } } else { blendWeight += weight; lerp = weight / blendWeight; SIMDProcessor->BlendJoints( blendFrame, jointFrame, lerp, modelDef->GetChannelJoints( channel ), modelDef->NumJointsOnChannel( channel ) ); } if ( printInfo ) { if ( frame ) { gameLocal.Printf( " %s: '%s', %d, %.2f%%\n", channelNames[ channel ], anim->FullName(), frame, weight * 100.0f ); } else { gameLocal.Printf( " %s: '%s', %.3f, %.2f%%\n", channelNames[ channel ], anim->FullName(), ( float )frametime.frame1 + frametime.backlerp, weight * 100.0f ); } } return true; } /* ===================== idAnimBlend::BlendOrigin ===================== */ void idAnimBlend::BlendOrigin( int currentTime, idVec3 &blendPos, float &blendWeight, bool removeOriginOffset ) const { float lerp; idVec3 animpos; idVec3 pos; int time; int num; int i; if ( frame || ( ( endtime > 0 ) && ( currentTime > endtime ) ) ) { return; } const idAnim *anim = Anim(); if ( !anim ) { return; } if ( allowMove && removeOriginOffset ) { return; } float weight = GetWeight( currentTime ); if ( !weight ) { return; } time = AnimTime( currentTime ); pos.Zero(); num = anim->NumAnims(); for( i = 0; i < num; i++ ) { anim->GetOrigin( animpos, i, time, cycle ); pos += animpos * animWeights[ i ]; } if ( !blendWeight ) { blendPos = pos; blendWeight = weight; } else { lerp = weight / ( blendWeight + weight ); blendPos += lerp * ( pos - blendPos ); blendWeight += weight; } } /* ===================== idAnimBlend::BlendDelta ===================== */ void idAnimBlend::BlendDelta( int fromtime, int totime, idVec3 &blendDelta, float &blendWeight ) const { idVec3 pos1; idVec3 pos2; idVec3 animpos; idVec3 delta; int time1; int time2; float lerp; int num; int i; if ( frame || !allowMove || ( ( endtime > 0 ) && ( fromtime > endtime ) ) ) { return; } const idAnim *anim = Anim(); if ( !anim ) { return; } float weight = GetWeight( totime ); if ( !weight ) { return; } time1 = AnimTime( fromtime ); time2 = AnimTime( totime ); if ( time2 < time1 ) { time2 += anim->Length(); } num = anim->NumAnims(); pos1.Zero(); pos2.Zero(); for( i = 0; i < num; i++ ) { anim->GetOrigin( animpos, i, time1, cycle ); pos1 += animpos * animWeights[ i ]; anim->GetOrigin( animpos, i, time2, cycle ); pos2 += animpos * animWeights[ i ]; } delta = pos2 - pos1; if ( !blendWeight ) { blendDelta = delta; blendWeight = weight; } else { lerp = weight / ( blendWeight + weight ); blendDelta += lerp * ( delta - blendDelta ); blendWeight += weight; } } /* ===================== idAnimBlend::BlendDeltaRotation ===================== */ void idAnimBlend::BlendDeltaRotation( int fromtime, int totime, idQuat &blendDelta, float &blendWeight ) const { idQuat q1; idQuat q2; idQuat q3; int time1; int time2; float lerp; float mixWeight; int num; int i; if ( frame || !allowMove || ( ( endtime > 0 ) && ( fromtime > endtime ) ) ) { return; } const idAnim *anim = Anim(); if ( !anim || !anim->GetAnimFlags().anim_turn ) { return; } float weight = GetWeight( totime ); if ( !weight ) { return; } time1 = AnimTime( fromtime ); time2 = AnimTime( totime ); if ( time2 < time1 ) { time2 += anim->Length(); } q1.Set( 0.0f, 0.0f, 0.0f, 1.0f ); q2.Set( 0.0f, 0.0f, 0.0f, 1.0f ); mixWeight = 0.0f; num = anim->NumAnims(); for( i = 0; i < num; i++ ) { if ( animWeights[ i ] > 0.0f ) { mixWeight += animWeights[ i ]; if ( animWeights[ i ] == mixWeight ) { anim->GetOriginRotation( q1, i, time1, cycle ); anim->GetOriginRotation( q2, i, time2, cycle ); } else { lerp = animWeights[ i ] / mixWeight; anim->GetOriginRotation( q3, i, time1, cycle ); q1.Slerp( q1, q3, lerp ); anim->GetOriginRotation( q3, i, time2, cycle ); q2.Slerp( q1, q3, lerp ); } } } q3 = q1.Inverse() * q2; if ( !blendWeight ) { blendDelta = q3; blendWeight = weight; } else { lerp = weight / ( blendWeight + weight ); blendDelta.Slerp( blendDelta, q3, lerp ); blendWeight += weight; } } /* ===================== idAnimBlend::AddBounds ===================== */ bool idAnimBlend::AddBounds( int currentTime, idBounds &bounds, bool removeOriginOffset ) const { int i; int num; idBounds b; int time; idVec3 pos; bool addorigin; if ( ( endtime > 0 ) && ( currentTime > endtime ) ) { return false; } const idAnim *anim = Anim(); if ( !anim ) { return false; } float weight = GetWeight( currentTime ); if ( !weight ) { return false; } time = AnimTime( currentTime ); num = anim->NumAnims(); addorigin = !allowMove || !removeOriginOffset; for( i = 0; i < num; i++ ) { if ( anim->GetBounds( b, i, time, cycle ) ) { if ( addorigin ) { anim->GetOrigin( pos, i, time, cycle ); b.TranslateSelf( pos ); } bounds.AddBounds( b ); } } return true; } /*********************************************************************** idDeclModelDef ***********************************************************************/ /* ===================== idDeclModelDef::idDeclModelDef ===================== */ idDeclModelDef::idDeclModelDef() { modelHandle = NULL; skin = NULL; offset.Zero(); for ( int i = 0; i < ANIM_NumAnimChannels; i++ ) { channelJoints[i].Clear(); } } /* ===================== idDeclModelDef::~idDeclModelDef ===================== */ idDeclModelDef::~idDeclModelDef() { FreeData(); } /* ================= idDeclModelDef::Size ================= */ size_t idDeclModelDef::Size( void ) const { return sizeof( idDeclModelDef ); } /* ===================== idDeclModelDef::CopyDecl ===================== */ void idDeclModelDef::CopyDecl( const idDeclModelDef *decl ) { int i; FreeData(); offset = decl->offset; modelHandle = decl->modelHandle; skin = decl->skin; anims.SetNum( decl->anims.Num() ); for( i = 0; i < anims.Num(); i++ ) { anims[ i ] = new idAnim( this, decl->anims[ i ] ); } joints.SetNum( decl->joints.Num() ); memcpy( joints.Ptr(), decl->joints.Ptr(), decl->joints.Num() * sizeof( joints[0] ) ); jointParents.SetNum( decl->jointParents.Num() ); memcpy( jointParents.Ptr(), decl->jointParents.Ptr(), decl->jointParents.Num() * sizeof( jointParents[0] ) ); for ( i = 0; i < ANIM_NumAnimChannels; i++ ) { channelJoints[i] = decl->channelJoints[i]; } } /* ===================== idDeclModelDef::FreeData ===================== */ void idDeclModelDef::FreeData( void ) { anims.DeleteContents( true ); joints.Clear(); jointParents.Clear(); modelHandle = NULL; skin = NULL; offset.Zero(); for ( int i = 0; i < ANIM_NumAnimChannels; i++ ) { channelJoints[i].Clear(); } } /* ================ idDeclModelDef::DefaultDefinition ================ */ const char *idDeclModelDef::DefaultDefinition( void ) const { return "{ }"; } /* ==================== idDeclModelDef::FindJoint ==================== */ const jointInfo_t *idDeclModelDef::FindJoint( const char *name ) const { int i; const idMD5Joint *joint; if ( !modelHandle ) { return NULL; } joint = modelHandle->GetJoints(); for( i = 0; i < joints.Num(); i++, joint++ ) { if ( !joint->name.Icmp( name ) ) { return &joints[ i ]; } } return NULL; } /* ===================== idDeclModelDef::ModelHandle ===================== */ idRenderModel *idDeclModelDef::ModelHandle( void ) const { return ( idRenderModel * )modelHandle; } /* ===================== idDeclModelDef::GetJointList ===================== */ void idDeclModelDef::GetJointList( const char *jointnames, idList &jointList ) const { const char *pos; idStr jointname; const jointInfo_t *joint; const jointInfo_t *child; int i; int num; bool getChildren; bool subtract; if ( !modelHandle ) { return; } jointList.Clear(); num = modelHandle->NumJoints(); // scan through list of joints and add each to the joint list pos = jointnames; while( *pos ) { // skip over whitespace while( ( *pos != 0 ) && isspace( *pos ) ) { pos++; } if ( !*pos ) { // no more names break; } // copy joint name jointname = ""; if ( *pos == '-' ) { subtract = true; pos++; } else { subtract = false; } if ( *pos == '*' ) { getChildren = true; pos++; } else { getChildren = false; } while( ( *pos != 0 ) && !isspace( *pos ) ) { jointname += *pos; pos++; } joint = FindJoint( jointname ); if ( !joint ) { gameLocal.Warning( "Unknown joint '%s' in '%s' for model '%s'", jointname.c_str(), jointnames, GetName() ); continue; } if ( !subtract ) { jointList.AddUnique( joint->num ); } else { jointList.Remove( joint->num ); } if ( getChildren ) { // include all joint's children child = joint + 1; for( i = joint->num + 1; i < num; i++, child++ ) { // all children of the joint should follow it in the list. // once we reach a joint without a parent or with a parent // who is earlier in the list than the specified joint, then // we've gone through all it's children. if ( child->parentNum < joint->num ) { break; } if ( !subtract ) { jointList.AddUnique( child->num ); } else { jointList.Remove( child->num ); } } } } } /* ===================== idDeclModelDef::Touch ===================== */ void idDeclModelDef::Touch( void ) const { if ( modelHandle ) { renderModelManager->FindModel( modelHandle->Name() ); } } /* ===================== idDeclModelDef::GetDefaultSkin ===================== */ const idDeclSkin *idDeclModelDef::GetDefaultSkin( void ) const { return skin; } /* ===================== idDeclModelDef::GetDefaultPose ===================== */ const idJointQuat *idDeclModelDef::GetDefaultPose( void ) const { return modelHandle->GetDefaultPose(); } /* ===================== idDeclModelDef::SetupJoints ===================== */ void idDeclModelDef::SetupJoints( int *numJoints, idJointMat **jointList, idBounds &frameBounds, bool removeOriginOffset ) const { int num; const idJointQuat *pose; idJointMat *list; if ( !modelHandle || modelHandle->IsDefaultModel() ) { Mem_Free16( (*jointList) ); (*jointList) = NULL; frameBounds.Clear(); return; } // get the number of joints num = modelHandle->NumJoints(); if ( !num ) { gameLocal.Error( "model '%s' has no joints", modelHandle->Name() ); } // set up initial pose for model (with no pose, model is just a jumbled mess) list = (idJointMat *) Mem_Alloc16( num * sizeof( list[0] ) ); pose = GetDefaultPose(); // convert the joint quaternions to joint matrices SIMDProcessor->ConvertJointQuatsToJointMats( list, pose, joints.Num() ); // check if we offset the model by the origin joint if ( removeOriginOffset ) { #ifdef VELOCITY_MOVE list[ 0 ].SetTranslation( idVec3( offset.x, offset.y + pose[0].t.y, offset.z + pose[0].t.z ) ); #else list[ 0 ].SetTranslation( offset ); #endif } else { list[ 0 ].SetTranslation( pose[0].t + offset ); } // transform the joint hierarchy SIMDProcessor->TransformJoints( list, jointParents.Ptr(), 1, joints.Num() - 1 ); *numJoints = num; *jointList = list; // get the bounds of the default pose frameBounds = modelHandle->Bounds( NULL ); } /* ===================== idDeclModelDef::ParseAnim ===================== */ bool idDeclModelDef::ParseAnim( idLexer &src, int numDefaultAnims ) { int i; int len; idAnim *anim; const idMD5Anim *md5anims[ ANIM_MaxSyncedAnims ]; const idMD5Anim *md5anim; idStr alias; idToken realname; idToken token; int numAnims; animFlags_t flags; numAnims = 0; memset( md5anims, 0, sizeof( md5anims ) ); if( !src.ReadToken( &realname ) ) { src.Warning( "Unexpected end of file" ); MakeDefault(); return false; } alias = realname; for( i = 0; i < anims.Num(); i++ ) { if ( !strcmp( anims[ i ]->FullName(), realname ) ) { break; } } if ( ( i < anims.Num() ) && ( i >= numDefaultAnims ) ) { src.Warning( "Duplicate anim '%s'", realname.c_str() ); MakeDefault(); return false; } if ( i < numDefaultAnims ) { anim = anims[ i ]; } else { // create the alias associated with this animation anim = new idAnim(); anims.Append( anim ); } // random anims end with a number. find the numeric suffix of the animation. len = alias.Length(); for( i = len - 1; i > 0; i-- ) { if ( !isdigit( alias[ i ] ) ) { break; } } // check for zero length name, or a purely numeric name if ( i <= 0 ) { src.Warning( "Invalid animation name '%s'", alias.c_str() ); MakeDefault(); return false; } // remove the numeric suffix alias.CapLength( i + 1 ); // parse the anims from the string do { if( !src.ReadToken( &token ) ) { src.Warning( "Unexpected end of file" ); MakeDefault(); return false; } // lookup the animation md5anim = animationLib.GetAnim( token ); if ( !md5anim ) { src.Warning( "Couldn't load anim '%s'", token.c_str() ); MakeDefault(); return false; } md5anim->CheckModelHierarchy( modelHandle ); if ( numAnims > 0 ) { // make sure it's the same length as the other anims if ( md5anim->Length() != md5anims[ 0 ]->Length() ) { src.Warning( "Anim '%s' does not match length of anim '%s'", md5anim->Name(), md5anims[ 0 ]->Name() ); MakeDefault(); return false; } } if ( numAnims >= ANIM_MaxSyncedAnims ) { src.Warning( "Exceeded max synced anims (%d)", ANIM_MaxSyncedAnims ); MakeDefault(); return false; } // add it to our list md5anims[ numAnims ] = md5anim; numAnims++; } while ( src.CheckTokenString( "," ) ); if ( !numAnims ) { src.Warning( "No animation specified" ); MakeDefault(); return false; } anim->SetAnim( this, realname, alias, numAnims, md5anims ); memset( &flags, 0, sizeof( flags ) ); // parse any frame commands or animflags if ( src.CheckTokenString( "{" ) ) { while( 1 ) { if( !src.ReadToken( &token ) ) { src.Warning( "Unexpected end of file" ); MakeDefault(); return false; } if ( token == "}" ) { break; }else if ( token == "prevent_idle_override" ) { flags.prevent_idle_override = true; } else if ( token == "random_cycle_start" ) { flags.random_cycle_start = true; } else if ( token == "ai_no_turn" ) { flags.ai_no_turn = true; } else if ( token == "anim_turn" ) { flags.anim_turn = true; } else if ( token == "frame" ) { // create a frame command int framenum; const char *err; // make sure we don't have any line breaks while reading the frame command so the error line # will be correct if ( !src.ReadTokenOnLine( &token ) ) { src.Warning( "Missing frame # after 'frame'" ); MakeDefault(); return false; } if ( token.type == TT_PUNCTUATION && token == "-" ) { src.Warning( "Invalid frame # after 'frame'" ); MakeDefault(); return false; } else if ( token.type != TT_NUMBER || token.subtype == TT_FLOAT ) { src.Error( "expected integer value, found '%s'", token.c_str() ); } // get the frame number framenum = token.GetIntValue(); // put the command on the specified frame of the animation err = anim->AddFrameCommand( this, framenum, src, NULL ); if ( err ) { src.Warning( "%s", err ); MakeDefault(); return false; } } else { src.Warning( "Unknown command '%s'", token.c_str() ); MakeDefault(); return false; } } } // set the flags anim->SetAnimFlags( flags ); return true; } /* ================ idDeclModelDef::Parse ================ */ bool idDeclModelDef::Parse( const char *text, const int textLength ) { int i; int num; idStr filename; idStr extension; const idMD5Joint *md5joint; const idMD5Joint *md5joints; idLexer src; idToken token; idToken token2; idStr jointnames; int channel; jointHandle_t jointnum; idList jointList; int numDefaultAnims; src.LoadMemory( text, textLength, GetFileName(), GetLineNum() ); src.SetFlags( DECL_LEXER_FLAGS ); src.SkipUntilString( "{" ); numDefaultAnims = 0; while( 1 ) { if ( !src.ReadToken( &token ) ) { break; } if ( !token.Icmp( "}" ) ) { break; } if ( token == "inherit" ) { if( !src.ReadToken( &token2 ) ) { src.Warning( "Unexpected end of file" ); MakeDefault(); return false; } const idDeclModelDef *copy = static_cast( declManager->FindType( DECL_MODELDEF, token2, false ) ); if ( !copy ) { common->Warning( "Unknown model definition '%s'", token2.c_str() ); } else if ( copy->GetState() == DS_DEFAULTED ) { common->Warning( "inherited model definition '%s' defaulted", token2.c_str() ); MakeDefault(); return false; } else { CopyDecl( copy ); numDefaultAnims = anims.Num(); } } else if ( token == "skin" ) { if( !src.ReadToken( &token2 ) ) { src.Warning( "Unexpected end of file" ); MakeDefault(); return false; } skin = declManager->FindSkin( token2 ); if ( !skin ) { src.Warning( "Skin '%s' not found", token2.c_str() ); MakeDefault(); return false; } } else if ( token == "mesh" ) { if( !src.ReadToken( &token2 ) ) { src.Warning( "Unexpected end of file" ); MakeDefault(); return false; } filename = token2; filename.ExtractFileExtension( extension ); if ( extension != MD5_MESH_EXT ) { src.Warning( "Invalid model for MD5 mesh" ); MakeDefault(); return false; } modelHandle = renderModelManager->FindModel( filename ); if ( !modelHandle ) { src.Warning( "Model '%s' not found", filename.c_str() ); MakeDefault(); return false; } if ( modelHandle->IsDefaultModel() ) { src.Warning( "Model '%s' defaulted", filename.c_str() ); MakeDefault(); return false; } // get the number of joints num = modelHandle->NumJoints(); if ( !num ) { src.Warning( "Model '%s' has no joints", filename.c_str() ); } // set up the joint hierarchy joints.SetGranularity( 1 ); joints.SetNum( num ); jointParents.SetNum( num ); channelJoints[0].SetNum( num ); md5joints = modelHandle->GetJoints(); md5joint = md5joints; for( i = 0; i < num; i++, md5joint++ ) { joints[i].channel = ANIMCHANNEL_ALL; joints[i].num = static_cast( i ); if ( md5joint->parent ) { joints[i].parentNum = static_cast( md5joint->parent - md5joints ); } else { joints[i].parentNum = INVALID_JOINT; } jointParents[i] = joints[i].parentNum; channelJoints[0][i] = i; } } else if ( token == "remove" ) { // removes any anims whos name matches if( !src.ReadToken( &token2 ) ) { src.Warning( "Unexpected end of file" ); MakeDefault(); return false; } num = 0; for( i = 0; i < anims.Num(); i++ ) { if ( ( token2 == anims[ i ]->Name() ) || ( token2 == anims[ i ]->FullName() ) ) { delete anims[ i ]; anims.RemoveIndex( i ); if ( i >= numDefaultAnims ) { src.Warning( "Anim '%s' was not inherited. Anim should be removed from the model def.", token2.c_str() ); MakeDefault(); return false; } i--; numDefaultAnims--; num++; continue; } } if ( !num ) { src.Warning( "Couldn't find anim '%s' to remove", token2.c_str() ); MakeDefault(); return false; } } else if ( token == "anim" ) { if ( !modelHandle ) { src.Warning( "Must specify mesh before defining anims" ); MakeDefault(); return false; } if ( !ParseAnim( src, numDefaultAnims ) ) { MakeDefault(); return false; } } else if ( token == "offset" ) { if ( !src.Parse1DMatrix( 3, offset.ToFloatPtr() ) ) { src.Warning( "Expected vector following 'offset'" ); MakeDefault(); return false; } } else if ( token == "channel" ) { if ( !modelHandle ) { src.Warning( "Must specify mesh before defining channels" ); MakeDefault(); return false; } // set the channel for a group of joints if( !src.ReadToken( &token2 ) ) { src.Warning( "Unexpected end of file" ); MakeDefault(); return false; } if ( !src.CheckTokenString( "(" ) ) { src.Warning( "Expected { after '%s'\n", token2.c_str() ); MakeDefault(); return false; } for( i = ANIMCHANNEL_ALL + 1; i < ANIM_NumAnimChannels; i++ ) { if ( !idStr::Icmp( channelNames[ i ], token2 ) ) { break; } } if ( i >= ANIM_NumAnimChannels ) { src.Warning( "Unknown channel '%s'", token2.c_str() ); MakeDefault(); return false; } channel = i; jointnames = ""; while( !src.CheckTokenString( ")" ) ) { if( !src.ReadToken( &token2 ) ) { src.Warning( "Unexpected end of file" ); MakeDefault(); return false; } jointnames += token2; if ( ( token2 != "*" ) && ( token2 != "-" ) ) { jointnames += " "; } } GetJointList( jointnames, jointList ); channelJoints[ channel ].SetNum( jointList.Num() ); for( num = i = 0; i < jointList.Num(); i++ ) { jointnum = jointList[ i ]; if ( joints[ jointnum ].channel != ANIMCHANNEL_ALL ) { src.Warning( "Joint '%s' assigned to multiple channels", modelHandle->GetJointName( jointnum ) ); continue; } joints[ jointnum ].channel = channel; channelJoints[ channel ][ num++ ] = jointnum; } channelJoints[ channel ].SetNum( num ); } else { src.Warning( "unknown token '%s'", token.c_str() ); MakeDefault(); return false; } } // shrink the anim list down to save space anims.SetGranularity( 1 ); anims.SetNum( anims.Num() ); return true; } /* ===================== idDeclModelDef::HasAnim ===================== */ bool idDeclModelDef::HasAnim( const char *name ) const { int i; // find any animations with same name for( i = 0; i < anims.Num(); i++ ) { if ( !strcmp( anims[ i ]->Name(), name ) ) { return true; } } return false; } /* ===================== idDeclModelDef::NumAnims ===================== */ int idDeclModelDef::NumAnims( void ) const { return anims.Num() + 1; } /* ===================== idDeclModelDef::GetSpecificAnim Gets the exact anim for the name, without randomization. ===================== */ int idDeclModelDef::GetSpecificAnim( const char *name ) const { int i; // find a specific animation for( i = 0; i < anims.Num(); i++ ) { if ( !strcmp( anims[ i ]->FullName(), name ) ) { return i + 1; } } // didn't find it return 0; } /* ===================== idDeclModelDef::GetAnim ===================== */ const idAnim *idDeclModelDef::GetAnim( int index ) const { if ( ( index < 1 ) || ( index > anims.Num() ) ) { return NULL; } return anims[ index - 1 ]; } /* ===================== idDeclModelDef::GetAnim ===================== */ int idDeclModelDef::GetAnim( const char *name ) const { int i; int which; const int MAX_ANIMS = 64; int animList[ MAX_ANIMS ]; int numAnims; int len; len = strlen( name ); if ( len && idStr::CharIsNumeric( name[ len - 1 ] ) ) { // find a specific animation return GetSpecificAnim( name ); } // find all animations with same name numAnims = 0; for( i = 0; i < anims.Num(); i++ ) { if ( !strcmp( anims[ i ]->Name(), name ) ) { animList[ numAnims++ ] = i; if ( numAnims >= MAX_ANIMS ) { break; } } } if ( !numAnims ) { return 0; } // get a random anim //FIXME: don't access gameLocal here? which = gameLocal.random.RandomInt( numAnims ); return animList[ which ] + 1; } /* ===================== idDeclModelDef::GetSkin ===================== */ const idDeclSkin *idDeclModelDef::GetSkin( void ) const { return skin; } /* ===================== idDeclModelDef::GetModelName ===================== */ const char *idDeclModelDef::GetModelName( void ) const { if ( modelHandle ) { return modelHandle->Name(); } else { return ""; } } /* ===================== idDeclModelDef::Joints ===================== */ const idList &idDeclModelDef::Joints( void ) const { return joints; } /* ===================== idDeclModelDef::JointParents ===================== */ const int * idDeclModelDef::JointParents( void ) const { return jointParents.Ptr(); } /* ===================== idDeclModelDef::NumJoints ===================== */ int idDeclModelDef::NumJoints( void ) const { return joints.Num(); } /* ===================== idDeclModelDef::GetJoint ===================== */ const jointInfo_t *idDeclModelDef::GetJoint( int jointHandle ) const { if ( ( jointHandle < 0 ) || ( jointHandle > joints.Num() ) ) { gameLocal.Error( "idDeclModelDef::GetJoint : joint handle out of range" ); } return &joints[ jointHandle ]; } /* ==================== idDeclModelDef::GetJointName ==================== */ const char *idDeclModelDef::GetJointName( int jointHandle ) const { const idMD5Joint *joint; if ( !modelHandle ) { return NULL; } if ( ( jointHandle < 0 ) || ( jointHandle > joints.Num() ) ) { gameLocal.Error( "idDeclModelDef::GetJointName : joint handle out of range" ); } joint = modelHandle->GetJoints(); return joint[ jointHandle ].name.c_str(); } /* ===================== idDeclModelDef::NumJointsOnChannel ===================== */ int idDeclModelDef::NumJointsOnChannel( int channel ) const { if ( ( channel < 0 ) || ( channel >= ANIM_NumAnimChannels ) ) { gameLocal.Error( "idDeclModelDef::NumJointsOnChannel : channel out of range" ); return 0; // unreachable, (Error() doesn't return) just to shut up compiler } return channelJoints[ channel ].Num(); } /* ===================== idDeclModelDef::GetChannelJoints ===================== */ const int * idDeclModelDef::GetChannelJoints( int channel ) const { if ( ( channel < 0 ) || ( channel >= ANIM_NumAnimChannels ) ) { gameLocal.Error( "idDeclModelDef::GetChannelJoints : channel out of range" ); return NULL; // unreachable, (Error() doesn't return) just to shut up compiler } return channelJoints[ channel ].Ptr(); } /* ===================== idDeclModelDef::GetVisualOffset ===================== */ const idVec3 &idDeclModelDef::GetVisualOffset( void ) const { return offset; } /*********************************************************************** idAnimator ***********************************************************************/ /* ===================== idAnimator::idAnimator ===================== */ idAnimator::idAnimator() { int i, j; modelDef = NULL; entity = NULL; numJoints = 0; joints = NULL; lastTransformTime = -1; stoppedAnimatingUpdate = false; removeOriginOffset = false; forceUpdate = false; frameBounds.Clear(); AFPoseJoints.SetGranularity( 1 ); AFPoseJointMods.SetGranularity( 1 ); AFPoseJointFrame.SetGranularity( 1 ); ClearAFPose(); for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) { for( j = 0; j < ANIM_MaxAnimsPerChannel; j++ ) { channels[ i ][ j ].Reset( NULL ); } } } /* ===================== idAnimator::~idAnimator ===================== */ idAnimator::~idAnimator() { FreeData(); } /* ===================== idAnimator::Allocated ===================== */ size_t idAnimator::Allocated( void ) const { size_t size; size = jointMods.Allocated() + numJoints * sizeof( joints[0] ) + jointMods.Num() * sizeof( jointMods[ 0 ] ) + AFPoseJointMods.Allocated() + AFPoseJointFrame.Allocated() + AFPoseJoints.Allocated(); return size; } /* ===================== idAnimator::Save archives object for save game file ===================== */ void idAnimator::Save( idSaveGame *savefile ) const { int i; int j; savefile->WriteModelDef( modelDef ); savefile->WriteObject( entity ); savefile->WriteInt( jointMods.Num() ); for( i = 0; i < jointMods.Num(); i++ ) { savefile->WriteInt( jointMods[ i ]->jointnum ); savefile->WriteMat3( jointMods[ i ]->mat ); savefile->WriteVec3( jointMods[ i ]->pos ); savefile->WriteInt( (int&)jointMods[ i ]->transform_pos ); savefile->WriteInt( (int&)jointMods[ i ]->transform_axis ); } savefile->WriteInt( numJoints ); for ( i = 0; i < numJoints; i++ ) { float *data = joints[i].ToFloatPtr(); for ( j = 0; j < 12; j++ ) { savefile->WriteFloat( data[j] ); } } savefile->WriteInt( lastTransformTime ); savefile->WriteBool( stoppedAnimatingUpdate ); savefile->WriteBool( forceUpdate ); savefile->WriteBounds( frameBounds ); savefile->WriteFloat( AFPoseBlendWeight ); savefile->WriteInt( AFPoseJoints.Num() ); for ( i = 0; i < AFPoseJoints.Num(); i++ ) { savefile->WriteInt( AFPoseJoints[i] ); } savefile->WriteInt( AFPoseJointMods.Num() ); for ( i = 0; i < AFPoseJointMods.Num(); i++ ) { savefile->WriteInt( (int&)AFPoseJointMods[i].mod ); savefile->WriteMat3( AFPoseJointMods[i].axis ); savefile->WriteVec3( AFPoseJointMods[i].origin ); } savefile->WriteInt( AFPoseJointFrame.Num() ); for ( i = 0; i < AFPoseJointFrame.Num(); i++ ) { savefile->WriteFloat( AFPoseJointFrame[i].q.x ); savefile->WriteFloat( AFPoseJointFrame[i].q.y ); savefile->WriteFloat( AFPoseJointFrame[i].q.z ); savefile->WriteFloat( AFPoseJointFrame[i].q.w ); savefile->WriteVec3( AFPoseJointFrame[i].t ); } savefile->WriteBounds( AFPoseBounds ); savefile->WriteInt( AFPoseTime ); savefile->WriteBool( removeOriginOffset ); for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) { for( j = 0; j < ANIM_MaxAnimsPerChannel; j++ ) { channels[ i ][ j ].Save( savefile ); } } } /* ===================== idAnimator::Restore unarchives object from save game file ===================== */ void idAnimator::Restore( idRestoreGame *savefile ) { int i; int j; int num; savefile->ReadModelDef( modelDef ); savefile->ReadObject( reinterpret_cast( entity ) ); savefile->ReadInt( num ); jointMods.SetNum( num ); for( i = 0; i < num; i++ ) { jointMods[ i ] = new jointMod_t; savefile->ReadInt( (int&)jointMods[ i ]->jointnum ); savefile->ReadMat3( jointMods[ i ]->mat ); savefile->ReadVec3( jointMods[ i ]->pos ); savefile->ReadInt( (int&)jointMods[ i ]->transform_pos ); savefile->ReadInt( (int&)jointMods[ i ]->transform_axis ); } savefile->ReadInt( numJoints ); joints = (idJointMat *) Mem_Alloc16( numJoints * sizeof( joints[0] ) ); for ( i = 0; i < numJoints; i++ ) { float *data = joints[i].ToFloatPtr(); for ( j = 0; j < 12; j++ ) { savefile->ReadFloat( data[j] ); } } savefile->ReadInt( lastTransformTime ); savefile->ReadBool( stoppedAnimatingUpdate ); savefile->ReadBool( forceUpdate ); savefile->ReadBounds( frameBounds ); savefile->ReadFloat( AFPoseBlendWeight ); savefile->ReadInt( num ); AFPoseJoints.SetGranularity( 1 ); AFPoseJoints.SetNum( num ); for ( i = 0; i < AFPoseJoints.Num(); i++ ) { savefile->ReadInt( AFPoseJoints[i] ); } savefile->ReadInt( num ); AFPoseJointMods.SetGranularity( 1 ); AFPoseJointMods.SetNum( num ); for ( i = 0; i < AFPoseJointMods.Num(); i++ ) { savefile->ReadInt( (int&)AFPoseJointMods[i].mod ); savefile->ReadMat3( AFPoseJointMods[i].axis ); savefile->ReadVec3( AFPoseJointMods[i].origin ); } savefile->ReadInt( num ); AFPoseJointFrame.SetGranularity( 1 ); AFPoseJointFrame.SetNum( num ); for ( i = 0; i < AFPoseJointFrame.Num(); i++ ) { savefile->ReadFloat( AFPoseJointFrame[i].q.x ); savefile->ReadFloat( AFPoseJointFrame[i].q.y ); savefile->ReadFloat( AFPoseJointFrame[i].q.z ); savefile->ReadFloat( AFPoseJointFrame[i].q.w ); savefile->ReadVec3( AFPoseJointFrame[i].t ); } savefile->ReadBounds( AFPoseBounds ); savefile->ReadInt( AFPoseTime ); savefile->ReadBool( removeOriginOffset ); for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) { for( j = 0; j < ANIM_MaxAnimsPerChannel; j++ ) { channels[ i ][ j ].Restore( savefile, modelDef ); } } } /* ===================== idAnimator::FreeData ===================== */ void idAnimator::FreeData( void ) { int i, j; if ( entity ) { entity->BecomeInactive( TH_ANIMATE ); } for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) { for( j = 0; j < ANIM_MaxAnimsPerChannel; j++ ) { channels[ i ][ j ].Reset( NULL ); } } jointMods.DeleteContents( true ); Mem_Free16( joints ); joints = NULL; numJoints = 0; modelDef = NULL; ForceUpdate(); } /* ===================== idAnimator::PushAnims ===================== */ void idAnimator::PushAnims( int channelNum, int currentTime, int blendTime ) { int i; idAnimBlend *channel; channel = channels[ channelNum ]; if ( !channel[ 0 ].GetWeight( currentTime ) || ( channel[ 0 ].starttime == currentTime ) ) { return; } for( i = ANIM_MaxAnimsPerChannel - 1; i > 0; i-- ) { channel[ i ] = channel[ i - 1 ]; } channel[ 0 ].Reset( modelDef ); channel[ 1 ].Clear( currentTime, blendTime ); ForceUpdate(); } /* ===================== idAnimator::SetModel ===================== */ idRenderModel *idAnimator::SetModel( const char *modelname ) { int i, j; FreeData(); // check if we're just clearing the model if ( !modelname || !*modelname ) { return NULL; } modelDef = static_cast( declManager->FindType( DECL_MODELDEF, modelname, false ) ); if ( !modelDef ) { return NULL; } idRenderModel *renderModel = modelDef->ModelHandle(); if ( !renderModel ) { modelDef = NULL; return NULL; } // make sure model hasn't been purged modelDef->Touch(); modelDef->SetupJoints( &numJoints, &joints, frameBounds, removeOriginOffset ); modelDef->ModelHandle()->Reset(); // set the modelDef on all channels for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) { for( j = 0; j < ANIM_MaxAnimsPerChannel; j++ ) { channels[ i ][ j ].Reset( modelDef ); } } return modelDef->ModelHandle(); } /* ===================== idAnimator::Size ===================== */ size_t idAnimator::Size( void ) const { return sizeof( *this ) + Allocated(); } /* ===================== idAnimator::SetEntity ===================== */ void idAnimator::SetEntity( idEntity *ent ) { entity = ent; } /* ===================== idAnimator::GetEntity ===================== */ idEntity *idAnimator::GetEntity( void ) const { return entity; } /* ===================== idAnimator::RemoveOriginOffset ===================== */ void idAnimator::RemoveOriginOffset( bool remove ) { removeOriginOffset = remove; } /* ===================== idAnimator::RemoveOrigin ===================== */ bool idAnimator::RemoveOrigin( void ) const { return removeOriginOffset; } /* ===================== idAnimator::GetJointList ===================== */ void idAnimator::GetJointList( const char *jointnames, idList &jointList ) const { if ( modelDef ) { modelDef->GetJointList( jointnames, jointList ); } } /* ===================== idAnimator::NumAnims ===================== */ int idAnimator::NumAnims( void ) const { if ( !modelDef ) { return 0; } return modelDef->NumAnims(); } /* ===================== idAnimator::GetAnim ===================== */ const idAnim *idAnimator::GetAnim( int index ) const { if ( !modelDef ) { return NULL; } return modelDef->GetAnim( index ); } /* ===================== idAnimator::GetAnim ===================== */ int idAnimator::GetAnim( const char *name ) const { if ( !modelDef ) { return 0; } return modelDef->GetAnim( name ); } /* ===================== idAnimator::HasAnim ===================== */ bool idAnimator::HasAnim( const char *name ) const { if ( !modelDef ) { return false; } return modelDef->HasAnim( name ); } /* ===================== idAnimator::NumJoints ===================== */ int idAnimator::NumJoints( void ) const { return numJoints; } /* ===================== idAnimator::ModelHandle ===================== */ idRenderModel *idAnimator::ModelHandle( void ) const { if ( !modelDef ) { return NULL; } return modelDef->ModelHandle(); } /* ===================== idAnimator::ModelDef ===================== */ const idDeclModelDef *idAnimator::ModelDef( void ) const { return modelDef; } /* ===================== idAnimator::CurrentAnim ===================== */ idAnimBlend *idAnimator::CurrentAnim( int channelNum ) { if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) { gameLocal.Error( "idAnimator::CurrentAnim : channel out of range" ); } return &channels[ channelNum ][ 0 ]; } /* ===================== idAnimator::Clear ===================== */ void idAnimator::Clear( int channelNum, int currentTime, int cleartime ) { int i; idAnimBlend *blend; if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) { gameLocal.Error( "idAnimator::Clear : channel out of range" ); } blend = channels[ channelNum ]; for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) { blend->Clear( currentTime, cleartime ); } ForceUpdate(); } /* ===================== idAnimator::SetFrame ===================== */ void idAnimator::SetFrame( int channelNum, int animNum, int frame, int currentTime, int blendTime ) { if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) { gameLocal.Error( "idAnimator::SetFrame : channel out of range" ); } if ( !modelDef || !modelDef->GetAnim( animNum ) ) { return; } PushAnims( channelNum, currentTime, blendTime ); channels[ channelNum ][ 0 ].SetFrame( modelDef, animNum, frame, currentTime, blendTime ); if ( entity ) { entity->BecomeActive( TH_ANIMATE ); } } /* ===================== idAnimator::CycleAnim ===================== */ void idAnimator::CycleAnim( int channelNum, int animNum, int currentTime, int blendTime ) { if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) { gameLocal.Error( "idAnimator::CycleAnim : channel out of range" ); } if ( !modelDef || !modelDef->GetAnim( animNum ) ) { return; } PushAnims( channelNum, currentTime, blendTime ); channels[ channelNum ][ 0 ].CycleAnim( modelDef, animNum, currentTime, blendTime ); if ( entity ) { entity->BecomeActive( TH_ANIMATE ); } } /* ===================== idAnimator::PlayAnim ===================== */ void idAnimator::PlayAnim( int channelNum, int animNum, int currentTime, int blendTime ) { if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) { gameLocal.Error( "idAnimator::PlayAnim : channel out of range" ); } if ( !modelDef || !modelDef->GetAnim( animNum ) ) { return; } PushAnims( channelNum, currentTime, blendTime ); channels[ channelNum ][ 0 ].PlayAnim( modelDef, animNum, currentTime, blendTime ); if ( entity ) { entity->BecomeActive( TH_ANIMATE ); } } /* ===================== idAnimator::SyncAnimChannels ===================== */ void idAnimator::SyncAnimChannels( int channelNum, int fromChannelNum, int currentTime, int blendTime ) { if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) || ( fromChannelNum < 0 ) || ( fromChannelNum >= ANIM_NumAnimChannels ) ) { gameLocal.Error( "idAnimator::SyncToChannel : channel out of range" ); } idAnimBlend &fromBlend = channels[ fromChannelNum ][ 0 ]; idAnimBlend &toBlend = channels[ channelNum ][ 0 ]; float weight = fromBlend.blendEndValue; if ( ( fromBlend.Anim() != toBlend.Anim() ) || ( fromBlend.GetStartTime() != toBlend.GetStartTime() ) || ( fromBlend.GetEndTime() != toBlend.GetEndTime() ) ) { PushAnims( channelNum, currentTime, blendTime ); toBlend = fromBlend; toBlend.blendStartValue = 0.0f; toBlend.blendEndValue = 0.0f; } toBlend.SetWeight( weight, currentTime - 1, blendTime ); // disable framecommands on the current channel so that commands aren't called twice toBlend.AllowFrameCommands( false ); if ( entity ) { entity->BecomeActive( TH_ANIMATE ); } } /* ===================== idAnimator::SetJointPos ===================== */ void idAnimator::SetJointPos( jointHandle_t jointnum, jointModTransform_t transform_type, const idVec3 &pos ) { int i; jointMod_t *jointMod; if ( !modelDef || !modelDef->ModelHandle() || ( jointnum < 0 ) || ( jointnum >= numJoints ) ) { return; } jointMod = NULL; for( i = 0; i < jointMods.Num(); i++ ) { if ( jointMods[ i ]->jointnum == jointnum ) { jointMod = jointMods[ i ]; break; } else if ( jointMods[ i ]->jointnum > jointnum ) { break; } } if ( !jointMod ) { jointMod = new jointMod_t; jointMod->jointnum = jointnum; jointMod->mat.Identity(); jointMod->transform_axis = JOINTMOD_NONE; jointMods.Insert( jointMod, i ); } jointMod->pos = pos; jointMod->transform_pos = transform_type; if ( entity ) { entity->BecomeActive( TH_ANIMATE ); } ForceUpdate(); } /* ===================== idAnimator::SetJointAxis ===================== */ void idAnimator::SetJointAxis( jointHandle_t jointnum, jointModTransform_t transform_type, const idMat3 &mat ) { int i; jointMod_t *jointMod; if ( !modelDef || !modelDef->ModelHandle() || ( jointnum < 0 ) || ( jointnum >= numJoints ) ) { return; } jointMod = NULL; for( i = 0; i < jointMods.Num(); i++ ) { if ( jointMods[ i ]->jointnum == jointnum ) { jointMod = jointMods[ i ]; break; } else if ( jointMods[ i ]->jointnum > jointnum ) { break; } } if ( !jointMod ) { jointMod = new jointMod_t; jointMod->jointnum = jointnum; jointMod->pos.Zero(); jointMod->transform_pos = JOINTMOD_NONE; jointMods.Insert( jointMod, i ); } jointMod->mat = mat; jointMod->transform_axis = transform_type; if ( entity ) { entity->BecomeActive( TH_ANIMATE ); } ForceUpdate(); } /* ===================== idAnimator::ClearJoint ===================== */ void idAnimator::ClearJoint( jointHandle_t jointnum ) { int i; if ( !modelDef || !modelDef->ModelHandle() || ( jointnum < 0 ) || ( jointnum >= numJoints ) ) { return; } for( i = 0; i < jointMods.Num(); i++ ) { if ( jointMods[ i ]->jointnum == jointnum ) { delete jointMods[ i ]; jointMods.RemoveIndex( i ); ForceUpdate(); break; } else if ( jointMods[ i ]->jointnum > jointnum ) { break; } } } /* ===================== idAnimator::ClearAllJoints ===================== */ void idAnimator::ClearAllJoints( void ) { if ( jointMods.Num() ) { ForceUpdate(); } jointMods.DeleteContents( true ); } /* ===================== idAnimator::ClearAllAnims ===================== */ void idAnimator::ClearAllAnims( int currentTime, int cleartime ) { int i; for( i = 0; i < ANIM_NumAnimChannels; i++ ) { Clear( i, currentTime, cleartime ); } ClearAFPose(); ForceUpdate(); } /* ==================== idAnimator::GetDelta ==================== */ void idAnimator::GetDelta( int fromtime, int totime, idVec3 &delta ) const { int i; const idAnimBlend *blend; float blendWeight; if ( !modelDef || !modelDef->ModelHandle() || ( fromtime == totime ) ) { delta.Zero(); return; } delta.Zero(); blendWeight = 0.0f; blend = channels[ ANIMCHANNEL_ALL ]; for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) { blend->BlendDelta( fromtime, totime, delta, blendWeight ); } if ( modelDef->Joints()[ 0 ].channel ) { blend = channels[ modelDef->Joints()[ 0 ].channel ]; for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) { blend->BlendDelta( fromtime, totime, delta, blendWeight ); } } } /* ==================== idAnimator::GetDeltaRotation ==================== */ bool idAnimator::GetDeltaRotation( int fromtime, int totime, idMat3 &delta ) const { int i; const idAnimBlend *blend; float blendWeight; idQuat q; if ( !modelDef || !modelDef->ModelHandle() || ( fromtime == totime ) ) { delta.Identity(); return false; } q.Set( 0.0f, 0.0f, 0.0f, 1.0f ); blendWeight = 0.0f; blend = channels[ ANIMCHANNEL_ALL ]; for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) { blend->BlendDeltaRotation( fromtime, totime, q, blendWeight ); } if ( modelDef->Joints()[ 0 ].channel ) { blend = channels[ modelDef->Joints()[ 0 ].channel ]; for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) { blend->BlendDeltaRotation( fromtime, totime, q, blendWeight ); } } if ( blendWeight > 0.0f ) { delta = q.ToMat3(); return true; } else { delta.Identity(); return false; } } /* ==================== idAnimator::GetOrigin ==================== */ void idAnimator::GetOrigin( int currentTime, idVec3 &pos ) const { int i; const idAnimBlend *blend; float blendWeight; if ( !modelDef || !modelDef->ModelHandle() ) { pos.Zero(); return; } pos.Zero(); blendWeight = 0.0f; blend = channels[ ANIMCHANNEL_ALL ]; for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) { blend->BlendOrigin( currentTime, pos, blendWeight, removeOriginOffset ); } if ( modelDef->Joints()[ 0 ].channel ) { blend = channels[ modelDef->Joints()[ 0 ].channel ]; for( i = 0; i < ANIM_MaxAnimsPerChannel; i++, blend++ ) { blend->BlendOrigin( currentTime, pos, blendWeight, removeOriginOffset ); } } pos += modelDef->GetVisualOffset(); } /* ==================== idAnimator::GetBounds ==================== */ bool idAnimator::GetBounds( int currentTime, idBounds &bounds ) { int i, j; const idAnimBlend *blend; int count; if ( !modelDef || !modelDef->ModelHandle() ) { return false; } if ( AFPoseJoints.Num() ) { bounds = AFPoseBounds; count = 1; } else { bounds.Clear(); count = 0; } blend = channels[ 0 ]; for( i = ANIMCHANNEL_ALL; i < ANIM_NumAnimChannels; i++ ) { for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) { if ( blend->AddBounds( currentTime, bounds, removeOriginOffset ) ) { count++; } } } if ( !count ) { if ( !frameBounds.IsCleared() ) { bounds = frameBounds; return true; } else { bounds.Zero(); return false; } } bounds.TranslateSelf( modelDef->GetVisualOffset() ); if ( g_debugBounds.GetBool() ) { if ( bounds[1][0] - bounds[0][0] > 2048 || bounds[1][1] - bounds[0][1] > 2048 ) { if ( entity ) { 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] ); } else { gameLocal.Warning( "big frameBounds on model '%s': %f,%f", modelDef->ModelHandle()->Name(), bounds[1][0] - bounds[0][0], bounds[1][1] - bounds[0][1] ); } } } frameBounds = bounds; return true; } /* ===================== idAnimator::InitAFPose ===================== */ void idAnimator::InitAFPose( void ) { if ( !modelDef ) { return; } AFPoseJoints.SetNum( modelDef->Joints().Num(), false ); AFPoseJoints.SetNum( 0, false ); AFPoseJointMods.SetNum( modelDef->Joints().Num(), false ); AFPoseJointFrame.SetNum( modelDef->Joints().Num(), false ); } /* ===================== idAnimator::SetAFPoseJointMod ===================== */ void idAnimator::SetAFPoseJointMod( const jointHandle_t jointNum, const AFJointModType_t mod, const idMat3 &axis, const idVec3 &origin ) { AFPoseJointMods[jointNum].mod = mod; AFPoseJointMods[jointNum].axis = axis; AFPoseJointMods[jointNum].origin = origin; int index = idBinSearch_GreaterEqual( AFPoseJoints.Ptr(), AFPoseJoints.Num(), jointNum ); if ( index >= AFPoseJoints.Num() || jointNum != AFPoseJoints[index] ) { AFPoseJoints.Insert( jointNum, index ); } } /* ===================== idAnimator::FinishAFPose ===================== */ void idAnimator::FinishAFPose( int animNum, const idBounds &bounds, const int time ) { int i, j; int numJoints; int parentNum; int jointMod; int jointNum; const int * jointParent; if ( !modelDef ) { return; } const idAnim *anim = modelDef->GetAnim( animNum ); if ( !anim ) { return; } numJoints = modelDef->Joints().Num(); if ( !numJoints ) { return; } idRenderModel *md5 = modelDef->ModelHandle(); const idMD5Anim *md5anim = anim->MD5Anim( 0 ); if ( numJoints != md5anim->NumJoints() ) { gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", md5->Name(), md5anim->Name() ); return; } idJointQuat *jointFrame = ( idJointQuat * )_alloca16( numJoints * sizeof( *jointFrame ) ); md5anim->GetSingleFrame( 0, jointFrame, modelDef->GetChannelJoints( ANIMCHANNEL_ALL ), modelDef->NumJointsOnChannel( ANIMCHANNEL_ALL ) ); if ( removeOriginOffset ) { #ifdef VELOCITY_MOVE jointFrame[ 0 ].t.x = 0.0f; #else jointFrame[ 0 ].t.Zero(); #endif } idJointMat *joints = ( idJointMat * )_alloca16( numJoints * sizeof( *joints ) ); // convert the joint quaternions to joint matrices SIMDProcessor->ConvertJointQuatsToJointMats( joints, jointFrame, numJoints ); // first joint is always root of entire hierarchy if ( AFPoseJoints.Num() && AFPoseJoints[0] == 0 ) { switch( AFPoseJointMods[0].mod ) { case AF_JOINTMOD_AXIS: { joints[0].SetRotation( AFPoseJointMods[0].axis ); break; } case AF_JOINTMOD_ORIGIN: { joints[0].SetTranslation( AFPoseJointMods[0].origin ); break; } case AF_JOINTMOD_BOTH: { joints[0].SetRotation( AFPoseJointMods[0].axis ); joints[0].SetTranslation( AFPoseJointMods[0].origin ); break; } } j = 1; } else { j = 0; } // pointer to joint info jointParent = modelDef->JointParents(); // transform the child joints for( i = 1; j < AFPoseJoints.Num(); j++, i++ ) { jointMod = AFPoseJoints[j]; // transform any joints preceding the joint modifier SIMDProcessor->TransformJoints( joints, jointParent, i, jointMod - 1 ); i = jointMod; parentNum = jointParent[i]; switch( AFPoseJointMods[jointMod].mod ) { case AF_JOINTMOD_AXIS: { joints[i].SetRotation( AFPoseJointMods[jointMod].axis ); joints[i].SetTranslation( joints[parentNum].ToVec3() + joints[i].ToVec3() * joints[parentNum].ToMat3() ); break; } case AF_JOINTMOD_ORIGIN: { joints[i].SetRotation( joints[i].ToMat3() * joints[parentNum].ToMat3() ); joints[i].SetTranslation( AFPoseJointMods[jointMod].origin ); break; } case AF_JOINTMOD_BOTH: { joints[i].SetRotation( AFPoseJointMods[jointMod].axis ); joints[i].SetTranslation( AFPoseJointMods[jointMod].origin ); break; } } } // transform the rest of the hierarchy SIMDProcessor->TransformJoints( joints, jointParent, i, numJoints - 1 ); // untransform hierarchy SIMDProcessor->UntransformJoints( joints, jointParent, 1, numJoints - 1 ); // convert joint matrices back to joint quaternions SIMDProcessor->ConvertJointMatsToJointQuats( AFPoseJointFrame.Ptr(), joints, numJoints ); // find all modified joints and their parents bool *blendJoints = (bool *) _alloca16( numJoints * sizeof( bool ) ); memset( blendJoints, 0, numJoints * sizeof( bool ) ); // mark all modified joints and their parents for( i = 0; i < AFPoseJoints.Num(); i++ ) { for( jointNum = AFPoseJoints[i]; jointNum != INVALID_JOINT; jointNum = jointParent[jointNum] ) { blendJoints[jointNum] = true; } } // lock all parents of modified joints AFPoseJoints.SetNum( 0, false ); for ( i = 0; i < numJoints; i++ ) { if ( blendJoints[i] ) { AFPoseJoints.Append( i ); } } AFPoseBounds = bounds; AFPoseTime = time; ForceUpdate(); } /* ===================== idAnimator::SetAFPoseBlendWeight ===================== */ void idAnimator::SetAFPoseBlendWeight( float blendWeight ) { AFPoseBlendWeight = blendWeight; } /* ===================== idAnimator::BlendAFPose ===================== */ bool idAnimator::BlendAFPose( idJointQuat *blendFrame ) const { if ( !AFPoseJoints.Num() ) { return false; } SIMDProcessor->BlendJoints( blendFrame, AFPoseJointFrame.Ptr(), AFPoseBlendWeight, AFPoseJoints.Ptr(), AFPoseJoints.Num() ); return true; } /* ===================== idAnimator::ClearAFPose ===================== */ void idAnimator::ClearAFPose( void ) { if ( AFPoseJoints.Num() ) { ForceUpdate(); } AFPoseBlendWeight = 1.0f; AFPoseJoints.SetNum( 0, false ); AFPoseBounds.Clear(); AFPoseTime = 0; } /* ===================== idAnimator::ServiceAnims ===================== */ void idAnimator::ServiceAnims( int fromtime, int totime ) { int i, j; idAnimBlend *blend; if ( !modelDef ) { return; } if ( modelDef->ModelHandle() ) { blend = channels[ 0 ]; for( i = 0; i < ANIM_NumAnimChannels; i++ ) { for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) { blend->CallFrameCommands( entity, fromtime, totime ); } } } if ( !IsAnimating( totime ) ) { stoppedAnimatingUpdate = true; if ( entity ) { entity->BecomeInactive( TH_ANIMATE ); // present one more time with stopped animations so the renderer can properly recreate interactions entity->BecomeActive( TH_UPDATEVISUALS ); } } } /* ===================== idAnimator::IsAnimating ===================== */ bool idAnimator::IsAnimating( int currentTime ) const { int i, j; const idAnimBlend *blend; if ( !modelDef || !modelDef->ModelHandle() ) { return false; } // if animating with an articulated figure if ( AFPoseJoints.Num() && currentTime <= AFPoseTime ) { return true; } blend = channels[ 0 ]; for( i = 0; i < ANIM_NumAnimChannels; i++ ) { for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) { if ( !blend->IsDone( currentTime ) ) { return true; } } } return false; } /* ===================== idAnimator::FrameHasChanged ===================== */ bool idAnimator::FrameHasChanged( int currentTime ) const { int i, j; const idAnimBlend *blend; if ( !modelDef || !modelDef->ModelHandle() ) { return false; } // if animating with an articulated figure if ( AFPoseJoints.Num() && currentTime <= AFPoseTime ) { return true; } blend = channels[ 0 ]; for( i = 0; i < ANIM_NumAnimChannels; i++ ) { for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) { if ( blend->FrameHasChanged( currentTime ) ) { return true; } } } if ( forceUpdate && IsAnimating( currentTime ) ) { return true; } return false; } /* ===================== idAnimator::CreateFrame ===================== */ bool idAnimator::CreateFrame( int currentTime, bool force ) { int i, j; int numJoints; int parentNum; bool hasAnim; bool debugInfo; float baseBlend; float blendWeight; const idAnimBlend * blend; const int * jointParent; const jointMod_t * jointMod; const idJointQuat * defaultPose; static idCVar r_showSkel( "r_showSkel", "0", CVAR_RENDERER | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> ); if ( gameLocal.inCinematic && gameLocal.skipCinematic ) { return false; } if ( !modelDef || !modelDef->ModelHandle() ) { return false; } if ( !force && !r_showSkel.GetInteger() ) { if ( lastTransformTime == currentTime ) { return false; } if ( lastTransformTime != -1 && !stoppedAnimatingUpdate && !IsAnimating( currentTime ) ) { return false; } } lastTransformTime = currentTime; stoppedAnimatingUpdate = false; if ( entity && ( ( g_debugAnim.GetInteger() == entity->entityNumber ) || ( g_debugAnim.GetInteger() == -2 ) ) ) { debugInfo = true; gameLocal.Printf( "---------------\n%d: entity '%s':\n", gameLocal.time, entity->GetName() ); gameLocal.Printf( "model '%s':\n", modelDef->GetModelName() ); } else { debugInfo = false; } // init the joint buffer if ( AFPoseJoints.Num() ) { // initialize with AF pose anim for the case where there are no other animations and no AF pose joint modifications defaultPose = AFPoseJointFrame.Ptr(); } else { defaultPose = modelDef->GetDefaultPose(); } if ( !defaultPose ) { //gameLocal.Warning( "idAnimator::CreateFrame: no defaultPose on '%s'", modelDef->Name() ); return false; } numJoints = modelDef->Joints().Num(); idJointQuat *jointFrame = ( idJointQuat * )_alloca16( numJoints * sizeof( jointFrame[0] ) ); SIMDProcessor->Memcpy( jointFrame, defaultPose, numJoints * sizeof( jointFrame[0] ) ); hasAnim = false; // blend the all channel baseBlend = 0.0f; blend = channels[ ANIMCHANNEL_ALL ]; for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) { if ( blend->BlendAnim( currentTime, ANIMCHANNEL_ALL, numJoints, jointFrame, baseBlend, removeOriginOffset, false, debugInfo ) ) { hasAnim = true; if ( baseBlend >= 1.0f ) { break; } } } // only blend other channels if there's enough space to blend into if ( baseBlend < 1.0f ) { for( i = ANIMCHANNEL_ALL + 1; i < ANIM_NumAnimChannels; i++ ) { if ( !modelDef->NumJointsOnChannel( i ) ) { continue; } if ( i == ANIMCHANNEL_EYELIDS ) { // eyelids blend over any previous anims, so skip it and blend it later continue; } blendWeight = baseBlend; blend = channels[ i ]; for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) { if ( blend->BlendAnim( currentTime, i, numJoints, jointFrame, blendWeight, removeOriginOffset, false, debugInfo ) ) { hasAnim = true; if ( blendWeight >= 1.0f ) { // fully blended break; } } } if ( debugInfo && !AFPoseJoints.Num() && !blendWeight ) { gameLocal.Printf( "%d: %s using default pose in model '%s'\n", gameLocal.time, channelNames[ i ], modelDef->GetModelName() ); } } } // blend in the eyelids if ( modelDef->NumJointsOnChannel( ANIMCHANNEL_EYELIDS ) ) { blend = channels[ ANIMCHANNEL_EYELIDS ]; blendWeight = baseBlend; for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) { if ( blend->BlendAnim( currentTime, ANIMCHANNEL_EYELIDS, numJoints, jointFrame, blendWeight, removeOriginOffset, true, debugInfo ) ) { hasAnim = true; if ( blendWeight >= 1.0f ) { // fully blended break; } } } } // blend the articulated figure pose if ( BlendAFPose( jointFrame ) ) { hasAnim = true; } if ( !hasAnim && !jointMods.Num() ) { // no animations were updated return false; } // convert the joint quaternions to rotation matrices SIMDProcessor->ConvertJointQuatsToJointMats( joints, jointFrame, numJoints ); // check if we need to modify the origin if ( jointMods.Num() && ( jointMods[0]->jointnum == 0 ) ) { jointMod = jointMods[0]; switch( jointMod->transform_axis ) { case JOINTMOD_NONE: break; case JOINTMOD_LOCAL: joints[0].SetRotation( jointMod->mat * joints[0].ToMat3() ); break; case JOINTMOD_WORLD: joints[0].SetRotation( joints[0].ToMat3() * jointMod->mat ); break; case JOINTMOD_LOCAL_OVERRIDE: case JOINTMOD_WORLD_OVERRIDE: joints[0].SetRotation( jointMod->mat ); break; } switch( jointMod->transform_pos ) { case JOINTMOD_NONE: break; case JOINTMOD_LOCAL: joints[0].SetTranslation( joints[0].ToVec3() + jointMod->pos ); break; case JOINTMOD_LOCAL_OVERRIDE: case JOINTMOD_WORLD: case JOINTMOD_WORLD_OVERRIDE: joints[0].SetTranslation( jointMod->pos ); break; } j = 1; } else { j = 0; } // add in the model offset joints[0].SetTranslation( joints[0].ToVec3() + modelDef->GetVisualOffset() ); // pointer to joint info jointParent = modelDef->JointParents(); // add in any joint modifications for( i = 1; j < jointMods.Num(); j++, i++ ) { jointMod = jointMods[j]; // transform any joints preceding the joint modifier SIMDProcessor->TransformJoints( joints, jointParent, i, jointMod->jointnum - 1 ); i = jointMod->jointnum; parentNum = jointParent[i]; // modify the axis switch( jointMod->transform_axis ) { case JOINTMOD_NONE: joints[i].SetRotation( joints[i].ToMat3() * joints[ parentNum ].ToMat3() ); break; case JOINTMOD_LOCAL: joints[i].SetRotation( jointMod->mat * ( joints[i].ToMat3() * joints[parentNum].ToMat3() ) ); break; case JOINTMOD_LOCAL_OVERRIDE: joints[i].SetRotation( jointMod->mat * joints[parentNum].ToMat3() ); break; case JOINTMOD_WORLD: joints[i].SetRotation( ( joints[i].ToMat3() * joints[parentNum].ToMat3() ) * jointMod->mat ); break; case JOINTMOD_WORLD_OVERRIDE: joints[i].SetRotation( jointMod->mat ); break; } // modify the position switch( jointMod->transform_pos ) { case JOINTMOD_NONE: joints[i].SetTranslation( joints[parentNum].ToVec3() + joints[i].ToVec3() * joints[parentNum].ToMat3() ); break; case JOINTMOD_LOCAL: joints[i].SetTranslation( joints[parentNum].ToVec3() + ( joints[i].ToVec3() + jointMod->pos ) * joints[parentNum].ToMat3() ); break; case JOINTMOD_LOCAL_OVERRIDE: joints[i].SetTranslation( joints[parentNum].ToVec3() + jointMod->pos * joints[parentNum].ToMat3() ); break; case JOINTMOD_WORLD: joints[i].SetTranslation( joints[parentNum].ToVec3() + joints[i].ToVec3() * joints[parentNum].ToMat3() + jointMod->pos ); break; case JOINTMOD_WORLD_OVERRIDE: joints[i].SetTranslation( jointMod->pos ); break; } } // transform the rest of the hierarchy SIMDProcessor->TransformJoints( joints, jointParent, i, numJoints - 1 ); return true; } /* ===================== idAnimator::ForceUpdate ===================== */ void idAnimator::ForceUpdate( void ) { lastTransformTime = -1; forceUpdate = true; } /* ===================== idAnimator::ClearForceUpdate ===================== */ void idAnimator::ClearForceUpdate( void ) { forceUpdate = false; } /* ===================== idAnimator::GetJointTransform> gamex86.dll!idAnimator::ForceUpdate() Line 4268 C++ ===================== */ bool idAnimator::GetJointTransform( jointHandle_t jointHandle, int currentTime, idVec3 &offset, idMat3 &axis ) { if ( !modelDef || ( jointHandle < 0 ) || ( jointHandle >= modelDef->NumJoints() ) ) { return false; } CreateFrame( currentTime, false ); offset = joints[ jointHandle ].ToVec3(); axis = joints[ jointHandle ].ToMat3(); return true; } /* ===================== idAnimator::GetJointLocalTransform ===================== */ bool idAnimator::GetJointLocalTransform( jointHandle_t jointHandle, int currentTime, idVec3 &offset, idMat3 &axis ) { if ( !modelDef ) { return false; } const idList &modelJoints = modelDef->Joints(); if ( ( jointHandle < 0 ) || ( jointHandle >= modelJoints.Num() ) ) { return false; } // FIXME: overkill CreateFrame( currentTime, false ); if ( jointHandle == 0 ) { offset = joints[ jointHandle ].ToVec3(); axis = joints[ jointHandle ].ToMat3(); return true; } idJointMat m = joints[ jointHandle ]; m /= joints[ modelJoints[ jointHandle ].parentNum ]; offset = m.ToVec3(); axis = m.ToMat3(); return true; } /* ===================== idAnimator::GetJointHandle ===================== */ jointHandle_t idAnimator::GetJointHandle( const char *name ) const { if ( !modelDef || !modelDef->ModelHandle() ) { return INVALID_JOINT; } return modelDef->ModelHandle()->GetJointHandle( name ); } /* ===================== idAnimator::GetJointName ===================== */ const char *idAnimator::GetJointName( jointHandle_t handle ) const { if ( !modelDef || !modelDef->ModelHandle() ) { return ""; } return modelDef->ModelHandle()->GetJointName( handle ); } /* ===================== idAnimator::GetChannelForJoint ===================== */ int idAnimator::GetChannelForJoint( jointHandle_t joint ) const { if ( !modelDef ) { gameLocal.Error( "idAnimator::GetChannelForJoint: NULL model" ); } if ( ( joint < 0 ) || ( joint >= numJoints ) ) { gameLocal.Error( "idAnimator::GetChannelForJoint: invalid joint num (%d)", joint ); } return modelDef->GetJoint( joint )->channel; } /* ===================== idAnimator::GetFirstChild ===================== */ jointHandle_t idAnimator::GetFirstChild( const char *name ) const { return GetFirstChild( GetJointHandle( name ) ); } /* ===================== idAnimator::GetFirstChild ===================== */ jointHandle_t idAnimator::GetFirstChild( jointHandle_t jointnum ) const { int i; int num; const jointInfo_t *joint; if ( !modelDef ) { return INVALID_JOINT; } num = modelDef->NumJoints(); if ( !num ) { return jointnum; } joint = modelDef->GetJoint( 0 ); for( i = 0; i < num; i++, joint++ ) { if ( joint->parentNum == jointnum ) { return ( jointHandle_t )joint->num; } } return jointnum; } /* ===================== idAnimator::GetJoints ===================== */ void idAnimator::GetJoints( int *numJoints, idJointMat **jointsPtr ) { *numJoints = this->numJoints; *jointsPtr = this->joints; } /* ===================== idAnimator::GetAnimFlags ===================== */ const animFlags_t idAnimator::GetAnimFlags( int animNum ) const { animFlags_t result; const idAnim *anim = GetAnim( animNum ); if ( anim ) { return anim->GetAnimFlags(); } memset( &result, 0, sizeof( result ) ); return result; } /* ===================== idAnimator::NumFrames ===================== */ int idAnimator::NumFrames( int animNum ) const { const idAnim *anim = GetAnim( animNum ); if ( anim ) { return anim->NumFrames(); } else { return 0; } } /* ===================== idAnimator::NumSyncedAnims ===================== */ int idAnimator::NumSyncedAnims( int animNum ) const { const idAnim *anim = GetAnim( animNum ); if ( anim ) { return anim->NumAnims(); } else { return 0; } } /* ===================== idAnimator::AnimName ===================== */ const char *idAnimator::AnimName( int animNum ) const { const idAnim *anim = GetAnim( animNum ); if ( anim ) { return anim->Name(); } else { return ""; } } /* ===================== idAnimator::AnimFullName ===================== */ const char *idAnimator::AnimFullName( int animNum ) const { const idAnim *anim = GetAnim( animNum ); if ( anim ) { return anim->FullName(); } else { return ""; } } /* ===================== idAnimator::AnimLength ===================== */ int idAnimator::AnimLength( int animNum ) const { const idAnim *anim = GetAnim( animNum ); if ( anim ) { return anim->Length(); } else { return 0; } } /* ===================== idAnimator::TotalMovementDelta ===================== */ const idVec3 &idAnimator::TotalMovementDelta( int animNum ) const { const idAnim *anim = GetAnim( animNum ); if ( anim ) { return anim->TotalMovementDelta(); } else { return vec3_origin; } } /*********************************************************************** Util functions ***********************************************************************/ /* ===================== ANIM_GetModelDefFromEntityDef ===================== */ const idDeclModelDef *ANIM_GetModelDefFromEntityDef( const idDict *args ) { const idDeclModelDef *modelDef; idStr name = args->GetString( "model" ); modelDef = static_cast( declManager->FindType( DECL_MODELDEF, name, false ) ); if ( modelDef && modelDef->ModelHandle() ) { return modelDef; } return NULL; } /* ===================== idGameEdit::ANIM_GetModelFromEntityDef ===================== */ idRenderModel *idGameEdit::ANIM_GetModelFromEntityDef( const idDict *args ) { idRenderModel *model; const idDeclModelDef *modelDef; model = NULL; idStr name = args->GetString( "model" ); modelDef = static_cast( declManager->FindType( DECL_MODELDEF, name, false ) ); if ( modelDef ) { model = modelDef->ModelHandle(); } if ( !model ) { model = renderModelManager->FindModel( name ); } if ( model && model->IsDefaultModel() ) { return NULL; } return model; } /* ===================== idGameEdit::ANIM_GetModelFromEntityDef ===================== */ idRenderModel *idGameEdit::ANIM_GetModelFromEntityDef( const char *classname ) { const idDict *args; args = gameLocal.FindEntityDefDict( classname, false ); if ( !args ) { return NULL; } return ANIM_GetModelFromEntityDef( args ); } /* ===================== idGameEdit::ANIM_GetModelOffsetFromEntityDef ===================== */ const idVec3 &idGameEdit::ANIM_GetModelOffsetFromEntityDef( const char *classname ) { const idDict *args; const idDeclModelDef *modelDef; args = gameLocal.FindEntityDefDict( classname, false ); if ( !args ) { return vec3_origin; } modelDef = ANIM_GetModelDefFromEntityDef( args ); if ( !modelDef ) { return vec3_origin; } return modelDef->GetVisualOffset(); } /* ===================== idGameEdit::ANIM_GetModelFromName ===================== */ idRenderModel *idGameEdit::ANIM_GetModelFromName( const char *modelName ) { const idDeclModelDef *modelDef; idRenderModel *model; model = NULL; modelDef = static_cast( declManager->FindType( DECL_MODELDEF, modelName, false ) ); if ( modelDef ) { model = modelDef->ModelHandle(); } if ( !model ) { model = renderModelManager->FindModel( modelName ); } return model; } /* ===================== idGameEdit::ANIM_GetAnimFromEntityDef ===================== */ const idMD5Anim *idGameEdit::ANIM_GetAnimFromEntityDef( const char *classname, const char *animname ) { const idDict *args; const idMD5Anim *md5anim; const idAnim *anim; int animNum; const char *modelname; const idDeclModelDef *modelDef; args = gameLocal.FindEntityDefDict( classname, false ); if ( !args ) { return NULL; } md5anim = NULL; modelname = args->GetString( "model" ); modelDef = static_cast( declManager->FindType( DECL_MODELDEF, modelname, false ) ); if ( modelDef ) { animNum = modelDef->GetAnim( animname ); if ( animNum ) { anim = modelDef->GetAnim( animNum ); if ( anim ) { md5anim = anim->MD5Anim( 0 ); } } } return md5anim; } /* ===================== idGameEdit::ANIM_GetNumAnimsFromEntityDef ===================== */ int idGameEdit::ANIM_GetNumAnimsFromEntityDef( const idDict *args ) { const char *modelname; const idDeclModelDef *modelDef; modelname = args->GetString( "model" ); modelDef = static_cast( declManager->FindType( DECL_MODELDEF, modelname, false ) ); if ( modelDef ) { return modelDef->NumAnims(); } return 0; } /* ===================== idGameEdit::ANIM_GetAnimNameFromEntityDef ===================== */ const char *idGameEdit::ANIM_GetAnimNameFromEntityDef( const idDict *args, int animNum ) { const char *modelname; const idDeclModelDef *modelDef; modelname = args->GetString( "model" ); modelDef = static_cast( declManager->FindType( DECL_MODELDEF, modelname, false ) ); if ( modelDef ) { const idAnim* anim = modelDef->GetAnim( animNum ); if ( anim ) { return anim->FullName(); } } return ""; } /* ===================== idGameEdit::ANIM_GetAnim ===================== */ const idMD5Anim *idGameEdit::ANIM_GetAnim( const char *fileName ) { return animationLib.GetAnim( fileName ); } /* ===================== idGameEdit::ANIM_GetLength ===================== */ int idGameEdit::ANIM_GetLength( const idMD5Anim *anim ) { if ( !anim ) { return 0; } return anim->Length(); } /* ===================== idGameEdit::ANIM_GetNumFrames ===================== */ int idGameEdit::ANIM_GetNumFrames( const idMD5Anim *anim ) { if ( !anim ) { return 0; } return anim->NumFrames(); } /* ===================== idGameEdit::ANIM_CreateAnimFrame ===================== */ void idGameEdit::ANIM_CreateAnimFrame( const idRenderModel *model, const idMD5Anim *anim, int numJoints, idJointMat *joints, int time, const idVec3 &offset, bool remove_origin_offset ) { int i; frameBlend_t frame; const idMD5Joint *md5joints; int *index; if ( !model || model->IsDefaultModel() || !anim ) { return; } if ( numJoints != model->NumJoints() ) { gameLocal.Error( "ANIM_CreateAnimFrame: different # of joints in renderEntity_t than in model (%s)", model->Name() ); } if ( !model->NumJoints() ) { // FIXME: Print out a warning? return; } if ( !joints ) { gameLocal.Error( "ANIM_CreateAnimFrame: NULL joint frame pointer on model (%s)", model->Name() ); } if ( numJoints != anim->NumJoints() ) { gameLocal.Warning( "Model '%s' has different # of joints than anim '%s'", model->Name(), anim->Name() ); for( i = 0; i < numJoints; i++ ) { joints[i].SetRotation( mat3_identity ); joints[i].SetTranslation( offset ); } return; } // create index for all joints index = ( int * )_alloca16( numJoints * sizeof( int ) ); for ( i = 0; i < numJoints; i++ ) { index[i] = i; } // create the frame anim->ConvertTimeToFrame( time, 1, frame ); idJointQuat *jointFrame = ( idJointQuat * )_alloca16( numJoints * sizeof( *jointFrame ) ); anim->GetInterpolatedFrame( frame, jointFrame, index, numJoints ); // convert joint quaternions to joint matrices SIMDProcessor->ConvertJointQuatsToJointMats( joints, jointFrame, numJoints ); // first joint is always root of entire hierarchy if ( remove_origin_offset ) { joints[0].SetTranslation( offset ); } else { joints[0].SetTranslation( joints[0].ToVec3() + offset ); } // transform the children md5joints = model->GetJoints(); for( i = 1; i < numJoints; i++ ) { joints[i] *= joints[ md5joints[i].parent - md5joints ]; } } /* ===================== idGameEdit::ANIM_CreateMeshForAnim ===================== */ idRenderModel *idGameEdit::ANIM_CreateMeshForAnim( idRenderModel *model, const char *classname, const char *animname, int frame, bool remove_origin_offset ) { renderEntity_t ent; const idDict *args; const char *temp; idRenderModel *newmodel; const idMD5Anim *md5anim; idStr filename; idStr extension; const idAnim *anim; int animNum; idVec3 offset; const idDeclModelDef *modelDef; if ( !model || model->IsDefaultModel() ) { return NULL; } args = gameLocal.FindEntityDefDict( classname, false ); if ( !args ) { return NULL; } memset( &ent, 0, sizeof( ent ) ); ent.bounds.Clear(); ent.suppressSurfaceInViewID = 0; modelDef = ANIM_GetModelDefFromEntityDef( args ); if ( modelDef ) { animNum = modelDef->GetAnim( animname ); if ( !animNum ) { return NULL; } anim = modelDef->GetAnim( animNum ); if ( !anim ) { return NULL; } md5anim = anim->MD5Anim( 0 ); ent.customSkin = modelDef->GetDefaultSkin(); offset = modelDef->GetVisualOffset(); } else { filename = animname; filename.ExtractFileExtension( extension ); if ( !extension.Length() ) { animname = args->GetString( va( "anim %s", animname ) ); } md5anim = animationLib.GetAnim( animname ); offset.Zero(); } if ( !md5anim ) { return NULL; } temp = args->GetString( "skin", "" ); if ( temp[ 0 ] ) { ent.customSkin = declManager->FindSkin( temp ); } ent.numJoints = model->NumJoints(); ent.joints = ( idJointMat * )Mem_Alloc16( ent.numJoints * sizeof( *ent.joints ) ); ANIM_CreateAnimFrame( model, md5anim, ent.numJoints, ent.joints, FRAME2MS( frame ), offset, remove_origin_offset ); newmodel = model->InstantiateDynamicModel( &ent, NULL, NULL ); Mem_Free16( ent.joints ); ent.joints = NULL; return newmodel; }