mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
608 lines
13 KiB
C++
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 );
|
|
}
|
|
|