Files
DoomRTX/neo/prey/Prey/prey_animator.cpp
T
2026-05-05 15:01:01 -07:00

608 lines
13 KiB
C++

#include "precompiled.h"
#pragma hdrstop
#include "prey_local.h"
/*
============
hhAnimator::hhAnimator
============
*/
hhAnimator::hhAnimator() {
lastCycleRotate = 0;
} //. hhAnimator::hhAnimator()
/*
=====================
hhAnimator::CycleAnimRandom
=====================
*/
void hhAnimator::CycleAnim( int channelNum, int anim, int currentTime, int blendTime, const idEventDef *pEvent ) {
//! NLANOTE - Add ability to skip cycling
idAnimator::CycleAnim( channelNum, anim, currentTime, blendTime );
if ( !anim ) { return; }
hhAnim *animPtr = (hhAnim *)GetAnim( anim );
//HUMANHEAD rww - crash here, can't repro. putting extra logic in for non-gold builds to check.
#if !GOLD
if (!animPtr) {
assert(0);
common->Warning("hhAnimator::CycleAnim anim index %i returned NULL for GetAnim.", anim);
return;
}
#endif
//HUMANHEAD END
// If the anim was an exact match to the requested name, if so, just return
if ( animPtr->exactMatch ) {
return;
}
// Determine if there are multiple anims to play. If not, then just return.
int numAnimVariants = GetNumAnimVariants( anim );
if ( numAnimVariants < 2 ) {
return;
}
// The CycleAnim could have started at a random spot in the anim, take this into account, and add only the time left in the anim
int lengthLeft;
lengthLeft = animPtr->Length() - channels[ channelNum ][0].AnimTime( currentTime );
channels[ channelNum ][0].rotateTime = currentTime + lengthLeft;
channels[ channelNum ][0].rotateEvent = pEvent;
entity->PostEventMS( &EV_CheckCycleRotate, lengthLeft );
} //. BlendCycleRandom( int, idAnim *, float, int, int )
/*
==============
hhAnimator::GetNumAnimVariants
==============
*/
int hhAnimator::GetNumAnimVariants( int anim ) {
const char *name = GetAnim( anim )->Name();
int len, numAnims, maxAnims;
// nla - Copied from idDeclModelDef::GetAnim( const char * )
len = strlen( name );
if ( len && idStr::CharIsNumeric( name[ len - 1 ] ) ) {
// find a specific animation
return( 1 );
}
// find all animations with same name
numAnims = 0;
maxAnims = NumAnims();
for( int i = 1; i < maxAnims; i++ ) {
if ( !strcmp( GetAnim( i )->Name(), name ) ) {
numAnims++;
}
}
return( numAnims );
} //. hhAnimator::GetNumAnimVariants( int )
/*
=====================
hhAnimator::CheckCycleRotate
Check the blend animations to see if we need to switch any animations
=====================
*/
void hhAnimator::CheckCycleRotate() {
hhAnimBlend *blend;
int anim;
int i;
const idEventDef *event;
if ( gameLocal.time <= lastCycleRotate ) {
return;
}
lastCycleRotate = gameLocal.time;
// Cycle through the channels
for( i = 0; i < ANIM_NumAnimChannels; i++ ) {
// Only cycle out the first/main anim of the channel. The rest ar being phased out and don't matter
blend = channels[ i ];
if ( !blend->IsDone( gameLocal.time ) ) {
if ( blend->Anim() && ( blend->rotateTime > 0 ) &&
( blend->rotateTime <= gameLocal.time ) ) { // HUMANHEAD JRM
anim = GetAnim( blend->Anim()->Name() );
event = blend->rotateEvent;
// gameLocal.Printf( "%s Cycling Anim %s\n", entity->name.c_str(), GetAnim( anim )->FullName() );
// blendWeight.Init( currentTime, blendTime, weight, newweight );
int remainingBlend = ( blend->blendStartTime + blend->blendDuration ) -
gameLocal.time;
if ( remainingBlend < 0 ) { remainingBlend = 0; }
blend->CycleAnim( modelDef, anim, gameLocal.time, remainingBlend );
entity->BecomeActive( TH_ANIMATE );
blend->rotateTime = gameLocal.time + GetAnim( anim )->Length();
blend->rotateEvent = event;
entity->PostEventMS( &EV_CheckCycleRotate, GetAnim( anim )->Length() );
// Process any change events
if ( event ) {
entity->ProcessEvent( event );
}
}
}
}
} //. CheckCycleRotate
/*
====================
hhAnimator::CheckThaw
HUMANHEAD nla
====================
*/
void hhAnimator::CheckThaw( ) {
hhAnimBlend *blend;
int i, j;
// Cycle through the channels
blend = channels[0];
for( i = 0; i < ANIM_NumAnimChannels; i++ ) {
for( j = 0; j < ANIM_MaxAnimsPerChannel; j++, blend++ ) {
if ( blend->Anim() ) {
blend->ThawIfTime( gameLocal.time );
}
}
}
} //. CheckThaw( )
/*
=====================
hhAnimator::CheckTween
Check if the current animation should be tweened to from the previous anim
Inputs: channel - The channel to check the current anim with
Outputs: The time in ms for the tween. 0 if non given.
HUMANHEAD nla
=====================
*/
int hhAnimator::CheckTween( int channelNum ) {
idStr toAnimName;
idStr fromAnimName = "";
int timeMS = 0;
int tempInt;
// Ensure we have a valid channel number
if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) {
gameLocal.Error( "idAnimator::CheckTween : channel out of range" );
}
// If no valid anim playing, then nothing to tween. :)
if ( channels[ channelNum ][ 0 ].Anim() == NULL ) {
return( 0 );
}
else { // Set the to anim name
toAnimName = channels[ channelNum ][ 0 ].AnimName();
}
// If a valid anim was playing, get its name
if ( channels[ channelNum ][ 1 ].Anim() != NULL ) {
fromAnimName = channels[ channelNum ][ 1 ].AnimName();
}
// We aren't really tweening, head out. = )
if ( toAnimName == fromAnimName ) {
// gameLocal.Printf("Earlying out!");
return( 0 );
}
// See if a default anim tween time is given
if ( entity->spawnArgs.GetInt( va( "tween2%s",
(const char *) toAnimName ),
"0", tempInt ) ) {
timeMS = tempInt;
// JRM - removed
//gameLocal.Printf( "Got tween2%s of %d\n", (const char *) toAnimName,
// timeMS );
}
// See if a anim to anim tween time is given
if ( fromAnimName.Length() &&
entity->spawnArgs.GetInt( va( "tween%s2%s",
(const char *) fromAnimName,
(const char *) toAnimName ),
"0", tempInt ) ) {
//! Check that they aren't the same anims
timeMS = tempInt;
// JRM - removed
//gameLocal.Printf( "Got tween%s2%s of %d\n", (const char *) fromAnimName,
// (const char *) toAnimName,
// timeMS );
}
// If a valid tween time is valid, use it
if ( timeMS > 0 ) {
// Freeze the current anim
channels[ channelNum ][ 0 ].Freeze( gameLocal.time, GetEntity(), timeMS );
// Setup the new blend times
// NLAMERGE 5 - This really used anymore?
/*
channels[ channelNum ][ 0 ].blendWeight.SetDuration( timeMS );
channels[ channelNum ][ 1 ].blendWeight.SetDuration( timeMS );
*/
}
return( timeMS );
} //. CheckTween( int )
/*
=====================
hhAnimator::IsAnimPlaying
HUMANHEAD nla
=====================
*/
bool hhAnimator::IsAnimPlaying( int channelNum, const idAnim *anim ) {
idAnimBlend * playingAnim;
playingAnim = FindAnim( channelNum, anim );
if ( playingAnim && ! playingAnim->IsDone( gameLocal.time ) ) {
return( true );
}
return( false );
} //. IsAnimPlaying( idAnim * )
/*
=====================
ggAnimator::IsAnimPlaying
HUMANHEAD nla
=====================
*/
bool hhAnimator::IsAnimPlaying( int channelNum, const char* anim ) {
return IsAnimPlaying( channelNum, GetAnim( GetAnim( anim ) ) );
}
/*
=====================
hhAnimator::IsAnimPlaying
HUMANHEAD nla
=====================
*/
bool hhAnimator::IsAnimPlaying( const idAnim *anim ) {
for( int i = 0; i < ANIM_NumAnimChannels; i++ ) {
if ( IsAnimPlaying( i, anim ) ) {
return( true );
}
}
return( false );
}
/*
=====================
hhAnimator::IsAnimPlaying
HUMANHEAD nla
=====================
*/
bool hhAnimator::IsAnimPlaying( const char* anim ) {
return IsAnimPlaying( GetAnim( GetAnim( anim ) ) );
}
/*
=====================
hhAnimator::FindAnim
HUMANHEAD nla
=====================
*/
idAnimBlend *hhAnimator::FindAnim( int channelNum, const idAnim *anim ) {
int i;
if ( ( channelNum < 0 ) || ( channelNum >= ANIM_NumAnimChannels ) ) {
gameLocal.Error( "hhAnimator::FindAnim : channel out of range" );
}
for( i = 0; i < ANIM_MaxAnimsPerChannel; i++ ) {
if ( channels[ channelNum ][ i ].Anim() == anim ) {
return &( channels[ channelNum ][ i ] );
}
}
return NULL;
}
/*
=====================
hhAnimator::GetBlendAnim
HUMANHEAD nla
=====================
*/
const idAnimBlend * hhAnimator::GetBlendAnim( int channelNum, int index ) const {
if ( channelNum < 0 || channelNum >= ANIM_NumAnimChannels ) {
gameLocal.Error("Channel out of range");
}
return ( ( index < 0 ) || ( index >= ANIM_MaxAnimsPerChannel ) ) ? NULL : &(channels[channelNum][ index ]);
}
/*
=====================
hhAnimator::NumBlendAnims
// HUMANHEAD Copied logic from IsAnimating
HUMANHEAD nla
=====================
*/
int hhAnimator::NumBlendAnims( int currentTime ) {
int i, j;
const idAnimBlend *blend;
// HUMANHEAD nla
int num;
num = 0;
// HUMANHEAD END
if ( !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 ) ) {
// HUMANHEAD nla
num++;
// HUMANHEAD END
}
}
}
return num;
}
/*
====================
hhAnimator::CopyAnimations
HUMANHEAD nla
====================
*/
void hhAnimator::CopyAnimations( hhAnimator &source ) {
// Loop through each channel
for ( int chan = 0; chan < ANIM_NumAnimChannels; ++chan ) {
// NLA - Simplifed to the double loop in the Nov 2003 "Big Merge" (c)
// Copy the channel anim info
for ( int anim = 0; anim < ANIM_MaxAnimsPerChannel;
++anim ) {
channels[ chan ][ anim ] = source.channels[ chan ][ anim ];
}
}
}
/*
====================
hhAnimator::CopyPoses
HUMANHEAD nla
====================
*/
void hhAnimator::CopyPoses( hhAnimator &source ) {
int i, num;
// Copy over the joint Mod info
jointMods.DeleteContents( true );
num = source.jointMods.Num();
jointMods.SetNum( num );
for( i = 0; i < num; i++ ) {
jointMods[ i ] = new jointMod_t;
*jointMods[ i ] = *source.jointMods[ i ];
}
// Copy over the frameBounds
frameBounds = source.frameBounds;
// Copy over AF info
AFPoseBlendWeight = source.AFPoseBlendWeight;
num = source.AFPoseJoints.Num();
AFPoseJoints.SetNum( num );
for ( int i = 0; i < num; i++ ) {
AFPoseJoints[ i ] = source.AFPoseJoints[ i ];
}
num = source.AFPoseJointMods.Num();
AFPoseJointMods.SetNum( num );
for ( int i = 0; i < num; i++ ) {
AFPoseJointMods[ i ] = source.AFPoseJointMods[ i ];
}
num = source.AFPoseJointFrame.Num();
AFPoseJointFrame.SetNum( num );
for ( int i = 0; i < num; i++ ) {
AFPoseJointFrame[ i ] = source.AFPoseJointFrame[ i ];
}
AFPoseBounds = source.AFPoseBounds;
AFPoseTime = source.AFPoseTime;
}
/*
====================
hhAnimator::Freeze
HUMANHEAD nla
====================
*/
bool hhAnimator::Freeze( ) {
hhAnimBlend *blend;
blend = &( channels[ 0 ][ 0 ] );
return( blend->Freeze( gameLocal.time, GetEntity() ) );
}
/*
====================
hhAnimator::Thaw
HUMANHEAD nla
====================
*/
bool hhAnimator::Thaw( ) {
hhAnimBlend *blend;
blend = &( channels[ 0 ][ 0 ] );
return( blend->Thaw( gameLocal.time ) );
}
/*
=====================
hhAnimator::IsAnimating
=====================
*/
bool hhAnimator::IsAnimating( int currentTime ) const {
int i, j;
const hhAnimBlend *blend;
// HUMANHEAD nla
int numFrozen = 0;
// HUMANHEAD END
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;
}
// HUMANHEAD nla
if ( blend->IsFrozen() ) { numFrozen++; }
// HUMANHEAD END
}
}
// HUMANHEAD nla
if ( numFrozen > 1 ) { return( true ); }
// HUMANHEAD END
return false;
}
/*
=====================
hhAnimator::FrameHasChanged
=====================
*/
bool hhAnimator::FrameHasChanged( int currentTime ) const {
int i, j;
const hhAnimBlend *blend;
// HUMANHEAD nla - Used to allow for tween (2 frozen) anims to work
int numFrozen = 0;
// HUMANHEAD END
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;
}
// HUMANHEAD nla
if ( blend->IsFrozen() ) { numFrozen++; }
// HUMANHEAD END
}
}
// HUMANHEAD nla
if ( numFrozen > 1 ) { return( true ); }
// HUMANHEAD END
if ( forceUpdate && IsAnimating( currentTime ) ) {
return true;
}
return false;
}
//================
//hhAnimator::Save
//================
void hhAnimator::Save( idSaveGame *savefile ) const {
// Not a subclass of idClass, so this is necessary -mdl
idAnimator::Save( savefile );
savefile->WriteInt( lastCycleRotate );
}
//================
//hhAnimator::Restore
//================
void hhAnimator::Restore( idRestoreGame *savefile ) {
// Not a subclass of idClass, so this is necessary -mdl
idAnimator::Restore( savefile );
savefile->ReadInt( lastCycleRotate );
}