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