// RAVEN BEGIN // bdube: note that this file is no longer merged with Doom3 updates // // MERGE_DATE 09/30/2004 #include "../../idlib/precompiled.h" #pragma hdrstop #include "../Game_local.h" #include "../../mpgame/Projectile.h" #include "../ai/AI.h" static const char *channelNames[ ANIM_NumAnimChannels ] = { "all", "torso", "legs", "head", "eyelids" }; /*********************************************************************** idAnim ***********************************************************************/ /* ===================== idAnim::idAnim ===================== */ idAnim::idAnim() { modelDef = NULL; // RAVEN BEGIN // bdube: added speed rate = 1.0f; // RAVEN END 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; // RAVEN BEGIN // bdube: copy speed info rate = anim->rate; // RAVEN END memset( anims, 0, sizeof( anims ) ); for( i = 0; i < numAnims; i++ ) { anims[ i ] = anim->anims[ i ]; anims[ i ]->IncreaseRefs(); } frameLookup.SetNum( anim->frameLookup.Num() ); // RAVEN BEGIN // JSinger: Changed to call optimized memcpy SIMDProcessor->Memcpy( frameLookup.Ptr(), anim->frameLookup.Ptr(), frameLookup.MemoryUsed() ); // RAVEN END 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 ); } // RAVEN BEGIN // bdube: copy joint information if ( anim->frameCommands[ i ].joint ) { frameCommands[ i ].joint = new idStr ( *anim->frameCommands[i].joint ); } if ( anim->frameCommands[ i ].joint2 ) { frameCommands[ i ].joint2 = new idStr ( *anim->frameCommands[i].joint2 ); } // abahr: if ( anim->frameCommands[ i ].parmList ) { frameCommands[ i ].parmList = new idList( *anim->frameCommands[i].parmList ); } // RAVEN END } } /* ===================== 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; // RAVEN BEGIN // bdube: joint support delete frameCommands[i].joint; delete frameCommands[i].joint2; // abahr: SAFE_DELETE_PTR( frameCommands[i].parmList ); // RAVEN END } } /* ===================== 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; // RAVEN BEGIN // bdube: joints as their own string delete frameCommands[i].joint; delete frameCommands[i].joint2; // abahr: SAFE_DELETE_PTR( frameCommands[i].parmList ); // RAVEN END } 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 == NULL || 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, const idList& frames, idLexer &src, const idDict *def ) { int i; int index; idStr text; idStr funcname; frameCommand_t fc; idToken token; 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 ); } // RAVEN BEGIN // abahr: else if( token == "eventArgs" ) { src.ParseRestOfLine( token ); if( token.Length() <= 0 ) { return "Unexpected end of line"; } fc.type = FC_EVENTFUNCTION_ARGS; fc.parmList = new idList(); token.Split( *fc.parmList, ' ' ); fc.event = idEventDef::FindEvent( (*fc.parmList)[0] ); if( !fc.event ) { SAFE_DELETE_PTR( fc.parmList ); return va( "Event '%s' not found", (*fc.parmList)[0] ); } fc.parmList->RemoveIndex( 0 ); } // RAVEN END 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" ) { // RAVEN BEGIN // bdube: use Raven effect system fc.type = FC_FX; // Get the effect name if ( !src.ReadTokenOnLine( &token ) ) { return va( "missing effect name" ); } // Effect is indirect if it starts with fx_ if ( !idStr::Icmpn ( token, "fx_", 3 ) ) { fc.string = new idStr ( token ); } else { fc.effect = ( const idDecl * )declManager->FindEffect( token ); } // Joint specified? if ( src.ReadTokenOnLine ( &token ) ) { fc.joint = new idStr ( token ); } // End joint specified? if ( src.ReadTokenOnLine ( &token ) ) { fc.joint2 = new idStr ( token ); } // RAVEN END } else if ( token == "trigger" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_TRIGGER; fc.string = new idStr( token ); // RAVEN BEGIN // bdube: not using /* } else if ( token == "triggerSmokeParticle" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line"; } fc.type = FC_TRIGGER_SMOKE_PARTICLE; fc.string = new idStr( token ); */ // RAVEN END } 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 == "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 == "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 == "enableBlinking" ) { fc.type = FC_ENABLE_BLINKING; } else if ( token == "disableBlinking" ) { fc.type = FC_DISABLE_BLINKING; } else if ( token == "enableAutoBlink" ) { fc.type = FC_ENABLE_AUTOBLINK; } else if ( token == "disableAutoBlink" ) { fc.type = FC_DISABLE_AUTOBLINK; } 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 ); } // RAVEN BEGIN // bdube: added script commands } else if ( token == "ai_enablePain" ) { fc.type = FC_AI_ENABLE_PAIN; } else if ( token == "ai_disablePain" ) { fc.type = FC_AI_DISABLE_PAIN; } else if ( token == "ai_enableDamage" ) { fc.type = FC_AI_ENABLE_DAMAGE; } else if ( token == "ai_disableDamage" ) { fc.type = FC_AI_DISABLE_DAMAGE; } else if ( token == "ai_lockEnemyOrigin" ) { fc.type = FC_AI_LOCKENEMYORIGIN; } else if ( token == "ai_attack" ) { fc.type = FC_AI_ATTACK; // Name of attack if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line while looking for attack Name"; } fc.string = new idStr( token ); // Joint to attack from if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line while looking for attack joint"; } fc.joint = new idStr( token ); } else if ( token == "ai_attack_melee" ) { if( !src.ReadTokenOnLine( &token ) ) { return "Unexpected end of line while looking for melee attack name"; } fc.type = FC_AI_ATTACK_MELEE; fc.string = new idStr( token ); } else if ( token == "guievent" ) { fc.type = FC_GUIEVENT; if( src.ReadTokenOnLine( &token ) ) { fc.string = new idStr( token ); } } else if ( token == "speak" ) { fc.type = FC_AI_SPEAK; if( src.ReadTokenOnLine( &token ) ) { fc.string = new idStr( token ); } } else if ( token == "speak_random" ) { fc.type = FC_AI_SPEAK_RANDOM; if( src.ReadTokenOnLine( &token ) ) { fc.string = new idStr( token ); } //MCG - added attachment frame commands } else if ( token == "attachment_hide" ) { fc.type = FC_ACT_ATTACH_HIDE; if( src.ReadTokenOnLine( &token ) ) { fc.string = new idStr( token ); } } else if ( token == "attachment_show" ) { fc.type = FC_ACT_ATTACH_SHOW; if( src.ReadTokenOnLine( &token ) ) { fc.string = new idStr( token ); } // RAVEN END } 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; } } // RAVEN BEGIN // bdube: support multiple frames for ( int ii = 0; ii < frames.Num(); ii ++ ) { int framenum = frames[ii]; // mekberg: error out of frame command is out of range. // -1 because we don't want commands on the loop frame. // If the anim doesn't loop they won't get handled. if ( ( framenum < 1 ) || ( framenum > anims[ 0 ]->NumFrames() -1 ) ) { gameLocal.Error("Frame command out of range: %d on anim '%s'. Max %d.", framenum, anims[ 0 ]->Name(), anims[ 0 ]->NumFrames() -1 ); } // Duplicate the frame info if ( ii != 0 ) { if ( fc.string ) { fc.string = new idStr ( fc.string->c_str() ); } if ( fc.joint ) { fc.joint = new idStr ( fc.joint->c_str() ); } if ( fc.joint2 ) { fc.joint2 = new idStr ( fc.joint2->c_str() ); } if ( fc.parmList ) { fc.parmList = new idList( *fc.parmList ); } } // frame numbers are 1 based in .def files, but 0 based internally framenum--; // RAVEN END // 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++; // RAVEN BEGIN // bdube: loop frame commands } // RAVEN END // return with no error return NULL; } // RAVEN BEGIN // bdube: added for debugging struct frameCommandInfo_t frameCommandInfo[FC_COUNT] = { { "call", false }, { "object_call", false }, { "event", false }, { "eventArgs", false }, { "sound", true }, { "sound_voice", true }, { "sound_voice2", true }, { "sound_body", true }, { "sound_body2", true }, { "sound_body3", true }, { "sound_weapon", true }, { "sound_item", true }, { "sound_global", true }, { "sound_chatter", true }, { "skin", true }, { "trigger", false }, { "direct_damage", false }, { "muzzle_flash", false }, { "footstep", false }, { "leftfoot", false }, { "rightfoot", false }, { "enableEyeFocus", false }, { "disableEyeFocus", false }, { "effect", true }, { "disable_gravity", false }, { "enable_gravity", false }, { "jump", false }, { "enableClip", false }, { "disableClip", false }, { "enableWalkIK", false }, { "disableWalkIK", false }, { "enableLegIK", false }, { "disableLegID", false }, { "recordDemo", false }, { "aviGame", false }, { "guievent", false }, { "ai_enablePain", false }, { "ai_disablePain", false }, { "ai_enableDamage", false }, { "ai_disableDamage", false }, { "ai_lockEnemyOrigin", false }, { "ai_attack", false }, { "ai_attack_melee", false }, { "speak", true }, }; // RAVEN END // RAVEN BEGIN // bdube: added frame command methods /* ===================== idAnim::CallFrameCommandSound ===================== */ void idAnim::CallFrameCommandSound( const frameCommand_t& command, idEntity* ent, const s_channelType channel ) const { int flags = 0; if ( channel == ( FC_SOUND_GLOBAL - FC_SOUND ) ) { flags = SSF_PRIVATE_SOUND; } if ( command.string ) { // ignore frame command jump sounds // we trigger jump sounds from the player physics and explicitely send them over the network if ( command.string->Icmp( "snd_jump" ) == 0 ) { return; } ent->StartSound( command.string->c_str(), channel, flags, false, NULL ); } else { ent->StartSoundShader( command.soundShader, channel, flags, false, NULL ); } } // RAVEN END /* ===================== 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++ ]; // RAVEN BEGIN // bdube: frame command debugging if ( g_showFrameCmds.GetBool() ) { idStr shortName; shortName = name; shortName.StripPath(); shortName.StripFileExtension ( ); gameLocal.Printf ( "framecmd: anim=%s frame=%d cmd=%s parm=%s\n", shortName.c_str(), frame + 1, frameCommandInfo[command.type].name, command.string?command.string->c_str():"???" ); } if ( ( gameLocal.editors & EDITOR_MODVIEW ) && !frameCommandInfo[command.type].modview ) { continue; } // RAVEN END switch( command.type ) { case FC_SCRIPTFUNCTION: { gameLocal.CallFrameCommand( ent, command.function ); break; } // RAVEN BEGIN // bdube: rewrote case FC_SCRIPTFUNCTIONOBJECT: { ent->ProcessEvent ( &EV_CallFunction, command.string->c_str() ); break; } // RAVEN END case FC_EVENTFUNCTION: { const idEventDef *ev = idEventDef::FindEvent( command.string->c_str() ); ent->ProcessEvent( ev ); break; } // RAVEN BEGIN // abahr: case FC_EVENTFUNCTION_ARGS: { assert( command.event ); ent->ProcessEvent( command.event, (int)command.parmList ); break; } // bdube: support indirection and simplify case FC_SOUND: case FC_SOUND_VOICE: case FC_SOUND_VOICE2: case FC_SOUND_BODY: case FC_SOUND_BODY2: case FC_SOUND_BODY3: case FC_SOUND_WEAPON: case FC_SOUND_ITEM: case FC_SOUND_GLOBAL: case FC_SOUND_CHATTER: CallFrameCommandSound ( command, ent, (const s_channelType)(command.type - FC_SOUND) ); break; // RAVEN END case FC_FX: { if ( gameLocal.localClientNum == -1 ) { // early ret on dedicated server break; } // RAVEN BEGIN // bdube: use raven effect system rvClientEffect* cent; if ( command.string ) { if ( command.joint ) { cent = ent->PlayEffect( command.string->c_str(), ent->GetAnimator()->GetJointHandle ( *command.joint ) ); } else { cent = gameLocal.PlayEffect( ent->spawnArgs, command.string->c_str(), ent->GetRenderEntity()->origin, ent->GetRenderEntity()->axis ); } } else { if ( command.joint ) { cent = ent->PlayEffect( command.effect, ent->GetAnimator()->GetJointHandle ( *command.joint ), vec3_zero, mat3_identity ); } else { cent = gameLocal.PlayEffect( command.effect, ent->GetRenderEntity()->origin, ent->GetRenderEntity()->axis ); } } // End origin bone specified? if ( cent && command.joint2 && ent->IsType( idAnimatedEntity::GetClassType() ) ) { cent->SetEndOrigin( ent->GetAnimator()->GetJointHandle( *command.joint2 ) ); } // Error print should the effect fail to play if ( !cent ) { idStr error = "Failed to play effect"; if( command.string ) { error += va( " \'%s\'", command.string->c_str() ); } if ( command.effect ) { error += va( " \'%s\'", command.effect->GetName() ); } if( command.joint ) { error += va( " on bone \'%s\'", command.joint->c_str() ); } common->Warning( error.c_str() ); } // RAVEN END 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_DIRECTDAMAGE: { ent->ProcessEvent( &AI_DirectDamage, command.string->c_str() ); break; } case FC_MUZZLEFLASH: { // RAVEN BEGIN // nmckenzie: We're not using this. // ent->ProcessEvent( &AI_MuzzleFlash, command.string->c_str() ); // RAVEN END 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_ENABLE_BLINKING: { ent->ProcessEvent( &AI_EnableBlink ); break; } case FC_DISABLE_BLINKING: { ent->ProcessEvent( &AI_DisableBlink ); break; } case FC_ENABLE_AUTOBLINK: { ent->ProcessEvent( &AI_EnableAutoBlink ); break; } case FC_DISABLE_AUTOBLINK: { ent->ProcessEvent( &AI_DisableAutoBlink ); 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; } case FC_AI_ENABLE_PAIN: ent->ProcessEvent ( &AI_EnablePain ); break; case FC_AI_DISABLE_PAIN: ent->ProcessEvent ( &AI_DisablePain ); break; case FC_AI_ENABLE_DAMAGE: ent->ProcessEvent ( &AI_EnableDamage ); break; case FC_AI_LOCKENEMYORIGIN: ent->ProcessEvent ( &AI_LockEnemyOrigin ); break; case FC_AI_ATTACK: ent->ProcessEvent ( &AI_Attack, command.string->c_str(), command.joint->c_str() ); break; case FC_AI_DISABLE_DAMAGE: ent->ProcessEvent ( &AI_DisableDamage ); break; case FC_AI_SPEAK: ent->ProcessEvent( &AI_Speak, command.string->c_str() ); break; case FC_AI_SPEAK_RANDOM: ent->ProcessEvent( &AI_SpeakRandom, command.string->c_str() ); break; case FC_ACT_ATTACH_HIDE: if ( ent->IsType(idActor::GetClassType()) ) { static_cast(ent)->HideAttachment( command.string->c_str() ); } break; case FC_ACT_ATTACH_SHOW: if ( ent->IsType(idActor::GetClassType()) ) { static_cast(ent)->ShowAttachment( command.string->c_str() ); } break; case FC_AI_ATTACK_MELEE: ent->ProcessEvent( &AI_AttackMelee, command.string->c_str() ); 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( animNum ); savefile->WriteBool( allowMove ); savefile->WriteBool( allowFrameCommands ); savefile->WriteBool( useFrameBlend ); } /* ===================== 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( 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 ); savefile->ReadBool( useFrameBlend ); } /* ===================== idAnimBlend::Reset ===================== */ void idAnimBlend::Reset( const idDeclModelDef *_modelDef ) { modelDef = _modelDef; cycle = 1; starttime = 0; endtime = 0; timeOffset = 0; rate = 1.0f; allowMove = true; allowFrameCommands = true; animNum = 0; memset( animWeights, 0, sizeof( animWeights ) ); blendStartValue = 0.0f; blendEndValue = 0.0f; blendStartTime = 0; blendDuration = 0; useFrameBlend = false; memset( &frameBlend, 0, sizeof( frameBlend ) ); } /* ===================== 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, const frameBlend_t & _frameBlend ) { 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() ) { // RAVEN BEGIN // scork: reports the actual joint # mismatch as well gameLocal.Warning( "Model '%s' has different # of joints (%d) than anim '%s' (%d)", modelDef->GetModelName(), modelDef->Joints().Num(), md5anim->Name(), md5anim->NumJoints() ); // RAVEN END return; } frameBlend = _frameBlend; animNum = _animNum; useFrameBlend = true; // a frame of 0 means it's not a single frame blend, so we set it to frame + 1 if ( frameBlend.frame1 < 0 ) { frameBlend.frame1 = 0; } else if ( frameBlend.frame1 >= _anim->NumFrames() ) { frameBlend.frame1 = _anim->NumFrames() - 1; } if ( frameBlend.frame2 < 0 ) { frameBlend.frame2 = 0; } else if ( frameBlend.frame2 >= _anim->NumFrames() ) { frameBlend.frame2 = _anim->NumFrames() - 1; } } /* ===================== idAnimBlend::CycleAnim ===================== */ void idAnimBlend::CycleAnim( const idDeclModelDef *modelDef, int _animNum, int currentTime, int blendTime, float _rate ) { 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() ) { // RAVEN BEGIN // scork: reports the actual joint # mismatch as well gameLocal.Warning( "Model '%s' has different # of joints (%d) than anim '%s' (%d)", modelDef->GetModelName(), modelDef->Joints().Num(), md5anim->Name(), md5anim->NumJoints() ); // RAVEN END 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; // RAVEN BEGIN // bdube: configurable playback rate _rate *= _anim->GetPlaybackRate ( ); if ( _rate != 1.0f ) { SetPlaybackRate ( currentTime, _rate ); } // RAVEN END } /* ===================== idAnimBlend::PlayAnim ===================== */ void idAnimBlend::PlayAnim( const idDeclModelDef *modelDef, int _animNum, int currentTime, int blendTime, float _rate ) { 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() ) { // RAVEN BEGIN // scork: reports the actual joint # mismatch as well gameLocal.Warning( "Model '%s' has different # of joints (%d) than anim '%s' (%d)", modelDef->GetModelName(), modelDef->Joints().Num(), md5anim->Name(), md5anim->NumJoints() ); // RAVEN END 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; // RAVEN BEGIN // bdube: configurable playback rate _rate *= _anim->GetPlaybackRate ( ); if ( _rate != 1.0f ) { SetPlaybackRate ( currentTime, _rate ); } // RAVEN END } /* ===================== 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 ( !useFrameBlend && ( 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 ( ( useFrameBlend || ( 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; // RAVEN BEGIN // jnewquist: Xenon compiler bug generated bad code. Used count instead of cycle for test. if ( count < 0 ) { // RAVEN END cycle = -1; endtime = -1; // RAVEN BEGIN // jnewquist: Xenon compiler bug generated bad code. Used count instead of cycle for test. } else if ( count == 0 ) { // RAVEN END 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::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 ( useFrameBlend ) { return FRAME2MS( frameBlend.frame1 ); } // 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 ( useFrameBlend ) { return frameBlend.frame1; } 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 || useFrameBlend || ( ( 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 = {0}; 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 ( useFrameBlend ) { md5anim->GetInterpolatedFrame( frameBlend, 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 ( !useFrameBlend ) { 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 ( useFrameBlend ) { md5anim->GetInterpolatedFrame( frameBlend, 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 ( useFrameBlend ) { gameLocal.Printf( " %s: '%s', %d, %.2f%%\n", channelNames[ channel ], anim->FullName(), frameBlend.frame1, 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 ( useFrameBlend || ( ( 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 ( useFrameBlend || !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 ( useFrameBlend || !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(); } // RAVEN BEGIN // jsinger: I have to track this for binary decl support #ifdef RV_BINARYDECLS mNumChannels=0; #endif // RAVEN END } /* ===================== idDeclModelDef::~idDeclModelDef ===================== */ idDeclModelDef::~idDeclModelDef() { FreeData(); } /* ================= idDeclModelDef::Size ================= */ // RAVEN BEGIN // jscott: made more accurate size_t idDeclModelDef::Size( void ) const { int i; size_t size; size = sizeof( idDeclModelDef ); size += joints.Allocated(); size += jointParents.Allocated(); size += anims.Allocated(); for( i = 0; i < ANIM_NumAnimChannels; i++ ) { size += channelJoints[i].Allocated(); } return( size ); } // RAVEN END /* ===================== 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() ); // RAVEN BEGIN // JSinger: Changed to call optimized memcpy SIMDProcessor->Memcpy( joints.Ptr(), decl->joints.Ptr(), decl->joints.Num() * sizeof( joints[0] ) ); // RAVEN END jointParents.SetNum( decl->jointParents.Num() ); // RAVEN BEGIN // JSinger: Changed to call optimized memcpy SIMDProcessor->Memcpy( jointParents.Ptr(), decl->jointParents.Ptr(), decl->jointParents.Num() * sizeof( jointParents[0] ) ); // RAVEN END 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) //RAVEN BEGIN //amccarthy: Added allocation tag list = (idJointMat *) Mem_Alloc16( num * sizeof( list[0] ), MA_ANIM ); //RAVEN END 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 ( !idStr::Cmp( 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 // RAVEN BEGIN // jsinger: animationLib changed to a pointer md5anim = animationLib->GetAnim( token ); // RAVEN END 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; // RAVEN BEGIN // bdube: added speed } else if ( token == "sync_cycle" ) { flags.sync_cycle = true; } else if ( token == "rate" ) { anim->SetPlaybackRate ( src.ParseFloat ( ) ); } else if ( token == "ai_no_look" ) { flags.ai_no_look = true; } else if ( token == "ai_look_head_only" ) { flags.ai_look_head_only = true; // RAVEN END } 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 // RAVEN BEGIN // bdube: Support a list of frame numbers // int framenum; const char *err; idList frameList; do { // RAVEN END // 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() ); } // RAVEN BEGIN // bdube: multiple frames frameList.Append ( token.GetIntValue() ); } while ( src.CheckTokenString ( "," ) ); // RAVEN END // put the command on the specified frame of the animation // RAVEN BEGIN // bdube: Support a list of frame numbers err = anim->AddFrameCommand( this, frameList, src, NULL ); // RAVEN END 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, bool noCaching ) { 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; // RAVEN BEGIN // bdube: attachments idList attachJoints; // RAVEN END TIME_THIS_SCOPE( __FUNCLINE__); 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 ) { gameLocal.Warning( "Unknown model definition '%s'", token2.c_str() ); } else if ( copy->GetState() == DS_DEFAULTED ) { gameLocal.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 ( !stricmp( 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 ); // RAVEN BEGIN // jsinger: I have to track this for binary decl support #ifdef RV_BINARYDECLS mNumChannels++; #endif // RAVEN END } 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::Validate ===================== */ bool idDeclModelDef::Validate( const char *psText, int iTextLength, idStr &strReportTo ) const { idDeclModelDef *pSelf = (idDeclModelDef*) declManager->AllocateDecl( DECL_MODELDEF ); bool bOk = pSelf->Parse( psText, iTextLength, false ); pSelf->FreeData(); delete pSelf->base; delete pSelf; return bOk; } /* ===================== 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 ( !idStr::Cmp( 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 ( !idStr::Cmp( anims[ i ]->FullName(), name ) ) { return i + 1; } } // didn't find it return 0; } /* ===================== 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 ( !idStr::Cmp( 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 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 channelJoints[ channel ].Ptr(); } /* ===================== idDeclModelDef::GetVisualOffset ===================== */ const idVec3 &idDeclModelDef::GetVisualOffset( void ) const { return offset; } // RAVEN BEGIN // jsinger: allow exporting of this decl type in a preparsed form #ifdef RV_BINARYDECLS void idDeclModelDef::Write( SerialOutputStream &stream ) const { WriteValue(offset[0], stream); WriteValue(offset[1], stream); WriteValue(offset[2], stream); WriteValue(joints.Num(), stream); for(int i=0; iName()), stream); WriteValue(anims.Num(), stream); for(int i=0; iWrite(stream); } if(skin) { WriteValue(idStr(skin->GetName()), stream); } else { WriteValue(idStr(""), stream); } } void idDeclModelDef::AddReferences() const { } idDeclModelDef::idDeclModelDef( SerialInputStream &stream ) { offset[0] = stream.ReadFloatValue(); offset[1] = stream.ReadFloatValue(); offset[2] = stream.ReadFloatValue(); int numJoints = stream.ReadIntValue(); joints.AssureSize(numJoints); for(int i=0; iFindModel( modelName.c_str() ); } int numAnims = stream.ReadIntValue(); anims.AssureSize(numAnims); for(int i=0; iFindSkin(skinName.c_str()); } else { skin = NULL; } } #endif // RAVEN END /*********************************************************************** idAnimator ***********************************************************************/ /* ===================== idAnimator::idAnimator ===================== */ idAnimator::idAnimator() { int i, j; modelDef = NULL; entity = NULL; numJoints = 0; joints = NULL; lastTransformTime = -1; stoppedAnimatingUpdate = false; removeOriginOffset = false; forceUpdate = false; // RAVEN BEGIN // jshepard: rateMultiplier = 1; // RAVEN END frameBounds.Clear(); AFPoseJoints.SetGranularity( 1 ); AFPoseJointMods.SetGranularity( 1 ); AFPoseJointFrame = NULL; AFPoseJointFrameSize = 0; 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() + AFPoseJointFrameSize * sizeof( AFPoseJointFrame[0] ) + 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->Write( jointMods[ i ], sizeof( *jointMods[ i ] ) ); } savefile->WriteInt( numJoints ); savefile->Write( joints, numJoints * sizeof( joints[0] ) ); savefile->WriteInt( lastTransformTime ); savefile->WriteBool( stoppedAnimatingUpdate ); savefile->WriteBool( forceUpdate ); savefile->WriteBounds( frameBounds ); savefile->WriteFloat( AFPoseBlendWeight ); savefile->WriteInt( AFPoseJoints.Num() ); savefile->Write( AFPoseJoints.Ptr(), AFPoseJoints.MemoryUsed() ); savefile->WriteInt( AFPoseJointMods.Num() ); savefile->Write( AFPoseJointMods.Ptr(), AFPoseJointMods.MemoryUsed() ); savefile->WriteInt( AFPoseJointFrameSize ); savefile->Write( AFPoseJointFrame, AFPoseJointFrameSize * sizeof( AFPoseJointFrame[0] ) ); 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->Read( jointMods[ i ], sizeof( *jointMods[ i ] ) ); } savefile->ReadInt( numJoints ); //RAVEN BEGIN //amccarthy: Added allocation tag joints = (idJointMat *) Mem_Alloc16( numJoints * sizeof( joints[0] ), MA_ANIM ); //RAVEN END savefile->Read( joints, numJoints * sizeof( joints[0] ) ); savefile->ReadInt( lastTransformTime ); savefile->ReadBool( stoppedAnimatingUpdate ); savefile->ReadBool( forceUpdate ); savefile->ReadBounds( frameBounds ); savefile->ReadFloat( AFPoseBlendWeight ); savefile->ReadInt( num ); AFPoseJoints.SetGranularity( 1 ); AFPoseJoints.SetNum( num ); savefile->Read( AFPoseJoints.Ptr(), AFPoseJoints.MemoryUsed() ); savefile->ReadInt( num ); AFPoseJointMods.SetGranularity( 1 ); AFPoseJointMods.SetNum( num ); savefile->Read( AFPoseJointMods.Ptr(), AFPoseJointMods.MemoryUsed() ); savefile->ReadInt( AFPoseJointFrameSize ); AFPoseJointFrame = (idJointQuat *)Mem_Alloc16( AFPoseJointFrameSize * sizeof( AFPoseJointFrame[0] ), MA_ANIM ); savefile->Read( AFPoseJointFrame, AFPoseJointFrameSize * sizeof( AFPoseJointFrame[0] ) ); 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; Mem_Free16( AFPoseJointFrame ); AFPoseJointFrame = NULL; AFPoseJointFrameSize = 0; 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(); // RAVEN BEGIN // bdube: make sure models dont get purged if ( modelDef->ModelHandle() ) { modelDef->ModelHandle()->SetLevelLoadReferenced ( true ); } // RAVEN END 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, const frameBlend_t & frameBlend ) { if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) { gameLocal.Error( "idAnimator::SetFrame : channel out of range" ); } if ( !modelDef || !modelDef->GetAnim( animNum ) ) { return; } PushAnims( channelNum, gameLocal.time, 0 ); channels[ channelNum ][ 0 ].SetFrame( modelDef, animNum, frameBlend ); 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, rateMultiplier ); if ( entity ) { entity->BecomeActive( TH_ANIMATE ); } // If the old animation and new animation both sync cycles then do so now idAnimBlend* oldChannel = &channels[channelNum][1]; idAnimBlend* newChannel = &channels[channelNum][0]; if ( oldChannel->AnimNum ( ) && newChannel->AnimNum ( ) ) { if ( oldChannel->Anim ( )->GetAnimFlags ( ).sync_cycle && newChannel->Anim ( )->GetAnimFlags ( ).sync_cycle ) { // Calculate the percentage through the animation the old channel was float f = (float)oldChannel->GetFrameNumber ( currentTime ) / (float)oldChannel->NumFrames ( ); // Move the new channel back in time to start it mid cycle newChannel->SetStartTime ( gameLocal.time - f * (newChannel->Length ( ) / newChannel->GetPlaybackRate()) ); } } } /* ===================== 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, rateMultiplier ); 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 ); } } // RAVEN BEGIN /* ===================== idAnimator::FindJointMod ===================== */ jointMod_t* idAnimator::FindExistingJointMod( jointHandle_t jointnum, int *index ) { jointMod_t* jointMod; int i; 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 ( index ) { *index = i; } return jointMod; } // bdube: added methods /* ===================== idAnimator::FindJointMod ===================== */ jointMod_t* idAnimator::FindJointMod ( jointHandle_t jointnum ) { jointMod_t* jointMod; int i; jointMod = FindExistingJointMod( jointnum, &i ); if ( !jointMod ) { jointMod = new jointMod_t; jointMod->jointnum = jointnum; jointMod->mat.Identity(); jointMod->transform_axis = JOINTMOD_NONE; jointMod->transform_pos = JOINTMOD_NONE; jointMod->angularVelocity.Init ( 0, 0, 0, 0, idAngles(0,0,0), idAngles(0,0,0) ); jointMod->lastTime = 0; jointMods.Insert( jointMod, i ); } return jointMod; } // abahr: /* ===================== idAnimator::SetPlaybackRate ===================== */ void idAnimator::SetPlaybackRate( const char* animName, float rate ) { SetPlaybackRate( GetAnim(animName), rate ); } /* ===================== idAnimator::SetPlaybackRate ===================== */ void idAnimator::SetPlaybackRate( int animHandle, float rate ) { idAnim* anim = const_cast( GetAnim(animHandle) ); if( anim ) { anim->SetPlaybackRate( rate ); } } // RAVEN END /* ===================== idAnimator::SetJointPos ===================== */ void idAnimator::SetJointPos( jointHandle_t jointnum, jointModTransform_t transform_type, const idVec3 &pos ) { // RAVEN BEGIN // bdube: moved old code to FindJointMod /* int i; */ // jointMod_t *jointMod; if ( !modelDef || !modelDef->ModelHandle() || ( jointnum < 0 ) || ( jointnum >= numJoints ) ) { return; } // RAVEN BEGIN // bdube: moved old code to FindJointMod jointMod = FindJointMod ( jointnum ); /* 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 ); } */ // RAVEN END 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 ) { jointMod_t *jointMod; if ( !modelDef || !modelDef->ModelHandle() || ( jointnum < 0 ) || ( jointnum >= numJoints ) ) { return; } jointMod = FindJointMod( jointnum ); jointMod->mat = mat; jointMod->transform_axis = transform_type; if ( entity ) { entity->BecomeActive( TH_ANIMATE ); } ForceUpdate(); } // RAVEN BEGIN // twhitaker: just added this for uniformity /* ===================== idAnimator::GetJointAxis ===================== */ void idAnimator::GetJointAxis( jointHandle_t jointnum, idMat3 &mat ) { if ( !modelDef || !modelDef->ModelHandle() || ( jointnum < 0 ) || ( jointnum >= numJoints ) ) { return; } mat = FindJointMod ( jointnum )->mat; } // RAVEN END /* ===================== idAnimator::CollapseJoint Add a joint modification for the given joint to collapse it to another joint ===================== */ void idAnimator::CollapseJoint ( jointHandle_t jointnum, jointHandle_t collapseTo ) { jointMod_t *jointMod; if ( !modelDef || !modelDef->ModelHandle() || ( jointnum < 0 ) || ( jointnum >= numJoints ) ) { return; } jointMod = FindJointMod ( jointnum ); jointMod->transform_pos = JOINTMOD_COLLAPSE; jointMod->transform_axis = JOINTMOD_WORLD_OVERRIDE; jointMod->mat.Zero ( ); jointMod->collapseJoint = collapseTo; } /* ===================== idAnimator::CollapseJoints Add a collapse joint modification for each joint listed in "jointnames" to the given joint "coppaseJoint" ===================== */ void idAnimator::CollapseJoints ( const char *jointnames, jointHandle_t collpaseJoint ) { idList jointList; int i; GetJointList( jointnames, jointList ); for ( i = 0; i < jointList.Num(); i ++ ) { CollapseJoint ( jointList[i], collpaseJoint ); } } // RAVEN BEGIN // bdube: added more programmer controlled joint methods /* ===================== idAnimator::AimJointAt ===================== */ void idAnimator::AimJointAt ( jointHandle_t jointnum, const idVec3& pos, const int currentTime ) { jointMod_t *jointMod; jointMod = FindJointMod ( jointnum ); idVec3 offset; idVec3 origin; idMat3 axis; idVec3 dir; idVec3 localDir; GetJointTransform ( jointnum, currentTime, offset, axis ); origin = entity->GetPhysics()->GetOrigin() + offset * entity->GetPhysics()->GetAxis(); dir = pos - origin; dir.NormalizeFast ( ); entity->GetPhysics()->GetAxis().ProjectVector ( dir, localDir ); jointMod->transform_axis = JOINTMOD_WORLD_OVERRIDE; jointMod->mat = localDir.ToMat3(); entity->BecomeActive( TH_ANIMATE ); ForceUpdate(); } /* ===================== idAnimator::SetJointAngularVelocity ===================== */ void idAnimator::SetJointAngularVelocity ( jointHandle_t jointnum, const idAngles& vel, const int currentTime, const int blendTime ) { jointMod_t *jointMod; jointMod = FindJointMod ( jointnum ); if ( !jointMod->lastTime ) { jointMod->lastTime = currentTime; jointMod->mat = idAngles(0,0,0).ToMat3(); } jointMod->transform_axis = JOINTMOD_LOCAL; if ( blendTime <= 0 ) { jointMod->angularVelocity.Init ( currentTime, 0, 0, 1, jointMod->angularVelocity.GetCurrentValue(currentTime), vel ); } else { jointMod->angularVelocity.Init ( currentTime, blendTime/2, blendTime/2, blendTime, jointMod->angularVelocity.GetCurrentValue(currentTime), vel ); } entity->BecomeActive( TH_ANIMATE ); ForceUpdate(); } /* ===================== idAnimator::GetJointAngularVelocity ===================== */ idAngles idAnimator::GetJointAngularVelocity ( jointHandle_t jointnum, const int currentTime ) { jointMod_t *jointMod = FindJointMod ( jointnum ); return jointMod->angularVelocity.GetCurrentValue( currentTime ); } /* ===================== idAnimator::GetJointAngularVelocity ===================== */ void idAnimator::ClearJointAngularVelocity ( jointHandle_t jointnum ) { jointMod_t *jointMod = FindJointMod ( jointnum ); jointMod->angularVelocity.Init( 0, 0, 0, 0, jointMod->angularVelocity.GetStartValue() - jointMod->angularVelocity.GetStartValue(), jointMod->angularVelocity.GetStartValue() - jointMod->angularVelocity.GetStartValue() ); } // RAVEN END /* ===================== 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( blend->starttime + 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( blend->starttime + 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() ); // RAVEN BEGIN // jscott: HACK! The bounds only get the range of the skeleton, so add in the distance between the highest bone and the top of the head bounds[1][2] += 4.0f; // RAVEN END 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 ); if ( AFPoseJointFrame == NULL || AFPoseJointFrameSize != modelDef->Joints().Num() ) { Mem_Free16( AFPoseJointFrame ); AFPoseJointFrame = (idJointQuat *) Mem_Alloc16( modelDef->Joints().Num() * sizeof( AFPoseJointFrame[0] ), MA_ANIM ); AFPoseJointFrameSize = modelDef->Joints().Num(); } } /* ===================== 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() ) { // RAVEN BEGIN // scork: reports the actual joint # mismatch as well gameLocal.Warning( "Model '%s' has different # of joints (%d) than anim '%s' (%d)", md5->Name(), numJoints, md5anim->Name(), md5anim->NumJoints() ); // RAVEN END 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, 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, 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 ); } } } // RAVEN BEGIN // rjohnson: added flag to ignore AF when checking for animation /* ===================== idAnimator::IsAnimating ===================== */ bool idAnimator::IsAnimating( int currentTime, bool IgnoreAF ) const { int i, j; const idAnimBlend *blend; if ( !modelDef || !modelDef->ModelHandle() ) { return false; } // if animating with an articulated figure if ( !IgnoreAF && 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; } // RAVEN END /* ===================== 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; // RAVEN BEGIN // bdube: removed const jointMod_t * jointMod; // RAVEN END const idJointQuat * defaultPose; static idCVar r_showSkel( "r_showSkel", "0", CVAR_RENDERER | CVAR_INTEGER, "draw the skeleton when model animates, 1 = draw model with skeleton, 2 = draw skeleton only, 3 = draw joints only", 0, 3, idCmdSystem::ArgCompletion_Integer<0,3> ); if ( gameLocal.inCinematic && gameLocal.skipCinematic ) { return false; } if ( !modelDef || !modelDef->ModelHandle() ) { return false; } // RAVEN BEGIN // rjohnson: always check the time transform except force. Even when skeleton is on if ( !force ) { if ( lastTransformTime == currentTime ) { return false; } } if ( !force && !r_showSkel.GetInteger() ) { if ( lastTransformTime != -1 && !stoppedAnimatingUpdate && !IsAnimating( currentTime ) ) { return false; } } #if 0 if ( !gameLocal.isLastPredictFrame ) { return false; } #endif // RAVEN END 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; } 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]; // RAVEN BEGIN // bdube: angular velocity on joints if ( jointMod->angularVelocity.GetStartTime ( ) ) { idAngles angles; angles = jointMod->angularVelocity.GetCurrentValue ( currentTime ) * MS2SEC(currentTime-jointMod->lastTime); angles.Normalize360 ( ); jointMod->mat *= angles.ToMat3 ( ); } // RAVEN END // 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; case JOINTMOD_COLLAPSE: joints[i].SetTranslation( joints[jointMod->collapseJoint].ToVec3() ); break; } // RAVEN BEGIN // bdube: more joint control jointMod->lastTime = currentTime; // RAVEN END } // 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 ===================== */ bool idAnimator::GetJointTransform( jointHandle_t jointHandle, int currentTime, idVec3 &offset, idMat3 &axis ) { if ( !modelDef || ( jointHandle < 0 ) || ( jointHandle >= modelDef->NumJoints() ) ) { return false; } if ( g_perfTest_noJointTransform.GetBool() ) { offset = entity->GetPhysics()->GetCenterMass()-entity->GetPhysics()->GetOrigin(); axis = entity->GetRenderEntity()->axis; return true; } 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; } if ( g_perfTest_noJointTransform.GetBool() ) { offset = entity->GetPhysics()->GetCenterMass()-entity->GetPhysics()->GetOrigin(); axis = entity->GetRenderEntity()->axis; return true; } // FIXME: overkill CreateFrame( currentTime, false ); if ( jointHandle > 0 ) { idJointMat m = joints[ jointHandle ]; m /= joints[ modelJoints[ jointHandle ].parentNum ]; offset = m.ToVec3(); axis = m.ToMat3(); } else { offset = joints[ jointHandle ].ToVec3(); axis = joints[ jointHandle ].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::IsBlending Returns true if the given channel number is currently blending more than one animation ===================== */ bool idAnimator::IsBlending ( int channel, int currentTime ) const { if ( ( channel < 0 ) || ( channel >= ANIM_NumAnimChannels ) ) { gameLocal.Error( "idAnimator::GetAnimCount : channel (%d) out of range", channel ); } int i; for( i = 1; i < ANIM_MaxAnimsPerChannel; i++ ) { if ( !channels[ channel ][ i ].IsDone ( currentTime ) ) { return true; } } return false; } /* ===================== 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 ""; } } // RAVEN BEGIN // rjohnson: more output for animators /* ===================== idAnimator::AnimMD5Name ===================== */ const char *idAnimator::AnimMD5Name( int animNum, int index ) const { const idAnim *anim = GetAnim( animNum ); if ( anim ) { const idMD5Anim *md5Anim = anim->MD5Anim( index ); if ( md5Anim ) { return md5Anim->Name(); } else { return ""; } } else { return ""; } } // RAVEN END /* ===================== 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; } } // RAVEN BEGIN // nrausch: added func /* ===================== idAnimator::GetNearestJoint ===================== */ jointHandle_t idAnimator::GetNearestJoint( const idVec3 &start, const idVec3 &end, int currentTime, jointHandle_t *jointList, int cnt ) { int numJoints = 0; idJointMat *joints = 0; GetJoints( &numJoints, &joints ); jointHandle_t closestJoint = INVALID_JOINT; float closest = 0.0f; idVec3 a = end - start; a.Normalize(); for ( int i = 0; i < cnt; ++i ) { // If we specified a null jointlist, just run through each joint jointHandle_t jointNum = (jointHandle_t)i; if ( jointList ) { jointNum = jointList[i]; } if ( jointNum != INVALID_JOINT ) { idVec3 jointPos; idMat3 jointMat; GetJointTransform( jointNum, currentTime, jointPos, jointMat ); idVec3 b = jointPos - start; b.Normalize(); float newDot = DotProduct(a, b); if ( newDot > closest ) { closest = newDot; closestJoint = jointNum; } } } return closestJoint; } // RAVEN END /*********************************************************************** 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; } // RAVEN BEGIN // bdube: added /* ===================== idGameEdit::ANIM_GetAnimFromEntity ===================== */ // scork: const-qualified 'ent' const idMD5Anim *idGameEdit::ANIM_GetAnimFromEntity( const idEntity* ent, int animNum ) { const idAnim * anim; anim = ent->GetAnimator ( )->GetAnim ( animNum ); if ( !anim ) { return NULL; } return anim->MD5Anim(0); } /* ===================== idGameEdit::ANIM_GetAnimPlaybackRateFromEntity ===================== */ float idGameEdit::ANIM_GetAnimPlaybackRateFromEntity ( idEntity* ent, int animNum ) { const idAnim * anim; anim = ent->GetAnimator ( )->GetAnim ( animNum ); if ( !anim ) { return 1.0f; } return anim->GetPlaybackRate(); } /* ===================== idGameEdit::ANIM_GetAnimNameFromEntity ===================== */ // scork: const-qualified 'ent' const char* idGameEdit::ANIM_GetAnimNameFromEntity ( const idEntity* ent, int animNum ) { const idAnim * anim; anim = ent->GetAnimator ( )->GetAnim ( animNum ); if ( !anim ) { return ""; } return anim->FullName(); } // RAVEN END /* ===================== 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 ) { // RAVEN BEGIN // jsinger: animationLib changed to a pointer return animationLib->GetAnim( fileName ); // RAVEN END } /* ===================== 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(); } // RAVEN BEGIN // bdube: added /* ===================== idGameEdit::ANIM_GetFilename ===================== */ const char * idGameEdit::ANIM_GetFilename( const idMD5Anim* anim ) { return anim->Name(); } /* ===================== idGameEdit::ANIM_ConvertFrameToTime ===================== */ int idGameEdit::ANIM_ConvertFrameToTime ( const idMD5Anim* anim, int frame ) { frameBlend_t fb; fb.frame1 = frame; fb.frame2 = frame; return anim->ConvertFrameToTime ( fb ); } /* ===================== idGameEdit::ANIM_ConvertTimeToFrame ===================== */ int idGameEdit::ANIM_ConvertTimeToFrame ( const idMD5Anim* anim, int time ) { frameBlend_t fb; anim->ConvertTimeToFrame ( time, 0, fb ); return fb.frame1; } // RAVEN END /* ===================== 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 (%d) than in model (%s)(%d)", model->NumJoints(), model->Name(), numJoints ); } 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() ) { // RAVEN BEGIN // scork: reports the actual joint # mismatch as well gameLocal.Warning( "Model '%s' has different # of joints (%d) than anim '%s' (%d)", model->Name(), numJoints, anim->Name(), anim->NumJoints() ); // RAVEN END 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 ) ); } // RAVEN BEGIN // jsinger: animationLib changed to a pointer md5anim = animationLib->GetAnim( animname ); // RAVEN END offset.Zero(); } if ( !md5anim ) { return NULL; } temp = args->GetString( "skin", "" ); if ( temp[ 0 ] ) { ent.customSkin = declManager->FindSkin( temp ); } ent.numJoints = model->NumJoints(); //RAVEN BEGIN //amccarthy: Added allocation tag ent.joints = ( idJointMat * )Mem_Alloc16( ent.numJoints * sizeof( *ent.joints ), MA_ANIM ); //RAVEN END ANIM_CreateAnimFrame( model, md5anim, ent.numJoints, ent.joints, FRAME2MS( frame ), offset, remove_origin_offset ); newmodel = model->InstantiateDynamicModel( &ent, NULL, NULL, ~SURF_COLLISION ); Mem_Free16( ent.joints ); ent.joints = NULL; return newmodel; } // RAVEN BEGIN // jsinger: allow for serialization/deserialization of binary decls #ifdef RV_BINARYDECLS idAnim::idAnim( idDeclModelDef const *def, SerialInputStream &stream ) { modelDef = def; numAnims = stream.ReadIntValue(); for(int i=0; i(); for(int i=0; iAppend(stream.ReadStringValue()); } } else { com.parmList = NULL; } } flags.prevent_idle_override = stream.ReadBoolValue(); flags.random_cycle_start = stream.ReadBoolValue(); flags.ai_no_turn = stream.ReadBoolValue(); flags.anim_turn = stream.ReadBoolValue(); flags.sync_cycle = stream.ReadBoolValue(); flags.ai_no_look = stream.ReadBoolValue(); flags.ai_look_head_only = stream.ReadBoolValue(); rate = stream.ReadFloatValue(); } void idAnim::Write( SerialOutputStream &stream ) const { stream.Write(numAnims); for(int i=0; iName()); } stream.Write(name); stream.Write(realname); stream.Write(frameLookup.Num()); for(int i=0; iNum()); for(int j=0; jNum(); j++) { stream.Write((*frameCommands[i].parmList)[j]); } } } stream.Write((bool)flags.prevent_idle_override); stream.Write((bool)flags.random_cycle_start); stream.Write((bool)flags.ai_no_turn); stream.Write((bool)flags.anim_turn); stream.Write((bool)flags.sync_cycle); stream.Write((bool)flags.ai_no_look); stream.Write((bool)flags.ai_look_head_only); stream.Write(rate); } #endif // RAVEN END