921 lines
23 KiB
C++
921 lines
23 KiB
C++
/*
|
|
** Command & Conquer Renegade(tm)
|
|
** Copyright 2025 Electronic Arts Inc.
|
|
**
|
|
** This program 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.
|
|
**
|
|
** This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
*** Confidential - Westwood Studios ***
|
|
***********************************************************************************************
|
|
* *
|
|
* Project Name : Commando *
|
|
* *
|
|
* $Archive:: /Commando/Code/Combat/animcontrol.cpp $*
|
|
* *
|
|
* $Author:: Byon_g $*
|
|
* *
|
|
* $Modtime:: 1/04/02 10:26a $*
|
|
* *
|
|
* $Revision:: 53 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
#include "animcontrol.h"
|
|
#include "assets.h"
|
|
#include "debug.h"
|
|
#include "rendobj.h"
|
|
#include "chunkio.h"
|
|
#include "saveload.h"
|
|
#include "htree.h"
|
|
|
|
/*
|
|
**
|
|
*/
|
|
enum {
|
|
XXXCHUNKID_LEGS = 910991512,
|
|
XXXCHUNKID_TORSO,
|
|
CHUNKID_CHANNEL,
|
|
CHUNKID_VARIABLES,
|
|
CHUNKID_OLD,
|
|
CHUNKID_NEW,
|
|
CHUNKID_PARENT,
|
|
CHUNKID_CHANNEL1,
|
|
CHUNKID_CHANNEL2,
|
|
|
|
MICROCHUNKID_BLEND_TIMER = 1,
|
|
MICROCHUNKID_BLEND_TOTAL,
|
|
MICROCHUNKID_FRAME,
|
|
XXXMICROCHUNKID_WEIGHT,
|
|
MICROCHUNKID_MODE,
|
|
MICROCHUNKID_ANIMATION_NAME,
|
|
MICROCHUNKID_MODEL_PTR,
|
|
MICROCHUNKID_CHANNEL2_RATIO,
|
|
MICROCHUNKID_TARGET_FRAME,
|
|
MICROCHUNKID_SKELETON,
|
|
};
|
|
|
|
/*
|
|
** AnimChannelClass
|
|
*/
|
|
AnimChannelClass::AnimChannelClass( void ) :
|
|
Animation( NULL ),
|
|
Frame( 0.0f ),
|
|
NumFrames( 1 ),
|
|
TargetFrame( 0.0f ),
|
|
Mode( ANIM_MODE_ONCE )
|
|
{
|
|
}
|
|
|
|
AnimChannelClass::~AnimChannelClass( void )
|
|
{
|
|
if ( Animation ) {
|
|
Animation->Release_Ref();
|
|
Animation = NULL;
|
|
}
|
|
}
|
|
|
|
AnimChannelClass & AnimChannelClass::operator = (const AnimChannelClass & src)
|
|
{
|
|
if (Animation != NULL) {
|
|
Animation->Release_Ref();
|
|
}
|
|
Animation = src.Animation;
|
|
if (Animation != NULL) {
|
|
Animation->Add_Ref();
|
|
}
|
|
Frame = src.Frame;
|
|
NumFrames = src.NumFrames;
|
|
Mode = src.Mode;
|
|
TargetFrame = src.TargetFrame;
|
|
return *this;
|
|
}
|
|
|
|
bool AnimChannelClass::Save( ChunkSaveClass & csave )
|
|
{
|
|
csave.Begin_Chunk( CHUNKID_VARIABLES );
|
|
// save the anim first, because the load will stomp the frame & weight
|
|
if ( Animation ) {
|
|
csave.Begin_Micro_Chunk( MICROCHUNKID_ANIMATION_NAME );
|
|
const char * anim_name = Animation->Get_Name();
|
|
csave.Write( anim_name, strlen( anim_name ) + 1);
|
|
csave.End_Micro_Chunk();
|
|
}
|
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_FRAME, Frame );
|
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MODE, Mode );
|
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TARGET_FRAME, TargetFrame );
|
|
csave.End_Chunk();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AnimChannelClass::Load( ChunkLoadClass &cload )
|
|
{
|
|
while (cload.Open_Chunk()) {
|
|
switch(cload.Cur_Chunk_ID()) {
|
|
|
|
case CHUNKID_VARIABLES:
|
|
while (cload.Open_Micro_Chunk()) {
|
|
switch(cload.Cur_Micro_Chunk_ID()) {
|
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_FRAME, Frame );
|
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_MODE, Mode );
|
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_TARGET_FRAME, TargetFrame );
|
|
|
|
case MICROCHUNKID_ANIMATION_NAME:
|
|
{
|
|
char anim_name[80];
|
|
cload.Read( anim_name, cload.Cur_Micro_Chunk_Length() );
|
|
Set_Animation( anim_name );
|
|
break;
|
|
}
|
|
default:
|
|
Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
|
|
break;
|
|
}
|
|
cload.Close_Micro_Chunk();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
|
|
break;
|
|
|
|
}
|
|
cload.Close_Chunk();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void AnimChannelClass::Set_Animation( const char *name )
|
|
{
|
|
// If this is our current anim, bail
|
|
if ( ( Animation != NULL ) && ( name != NULL ) ) {
|
|
if ( stricmp( Animation->Get_Name(), name ) == 0 ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( ( Animation == NULL ) && ( name == NULL ) ) {
|
|
return;
|
|
}
|
|
|
|
// Release the old anim
|
|
if ( Animation ) {
|
|
Animation->Release_Ref();
|
|
Animation = NULL;
|
|
}
|
|
|
|
// we need to switch anims
|
|
if (( name != NULL ) && ( name[0] != 0 ) ) {
|
|
Animation = WW3DAssetManager::Get_Instance()->Get_HAnim( name );
|
|
}
|
|
|
|
if ( Animation ) {
|
|
SET_REF_OWNER( Animation );
|
|
NumFrames = Animation->Get_Num_Frames();
|
|
Mode = ANIM_MODE_ONCE;
|
|
Frame = 0;
|
|
TargetFrame = 0;
|
|
}
|
|
}
|
|
|
|
void AnimChannelClass::Set_Animation( const HAnimClass *anim )
|
|
{
|
|
// If this is our current anim, bail
|
|
if ( Animation == anim ) {
|
|
return;
|
|
}
|
|
|
|
// Release the old anim
|
|
if ( Animation ) {
|
|
Animation->Release_Ref();
|
|
Animation = NULL;
|
|
}
|
|
|
|
// we need to switch anims
|
|
Animation = (HAnimClass *)anim;
|
|
|
|
if ( Animation ) {
|
|
Animation->Add_Ref();
|
|
NumFrames = Animation->Get_Num_Frames();
|
|
Mode = ANIM_MODE_ONCE;
|
|
Frame = 0;
|
|
TargetFrame = 0;
|
|
}
|
|
}
|
|
|
|
void AnimChannelClass::Set_Mode( AnimMode mode, float frame )
|
|
{
|
|
Mode = mode;
|
|
if ( frame >= 0 ) {
|
|
Frame = frame;
|
|
}
|
|
}
|
|
|
|
void AnimChannelClass::Update( float dtime )
|
|
{
|
|
if ( Mode == ANIM_MODE_STOP ) {
|
|
return;
|
|
}
|
|
|
|
if ( Animation != NULL ) {
|
|
|
|
switch ( Mode )
|
|
{
|
|
case ANIM_MODE_LOOP:
|
|
|
|
//
|
|
// Increment the frame based on the current timeslice
|
|
//
|
|
Frame += dtime * Animation->Get_Frame_Rate();
|
|
|
|
//
|
|
// Handle wrapping
|
|
//
|
|
if ( Frame >= NumFrames-1 ) {
|
|
Frame -= NumFrames-1;
|
|
}
|
|
|
|
if ( Frame >= NumFrames ) {
|
|
Frame = 0;
|
|
}
|
|
break;
|
|
|
|
case ANIM_MODE_TARGET:
|
|
|
|
//
|
|
// Which direction are we animating?
|
|
//
|
|
if ( Frame < TargetFrame ) {
|
|
Frame += dtime * Animation->Get_Frame_Rate();
|
|
|
|
//
|
|
// If we overshoot targetframe, snap to targetframe
|
|
//
|
|
if (Frame >= TargetFrame) {
|
|
Frame = TargetFrame;
|
|
}
|
|
|
|
} else if ( Frame > TargetFrame ) {
|
|
Frame -= dtime * Animation->Get_Frame_Rate();
|
|
|
|
//
|
|
// If we overshoot targetframe, snap to targetframe
|
|
//
|
|
if ( Frame <= TargetFrame ) {
|
|
Frame = TargetFrame;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ANIM_MODE_ONCE:
|
|
|
|
//
|
|
// Increment the frame based on the current timeslice
|
|
//
|
|
Frame += dtime * Animation->Get_Frame_Rate();
|
|
|
|
//
|
|
// Make sure we don't go past the end
|
|
//
|
|
if ( Frame > NumFrames-1 ) {
|
|
Frame = NumFrames-1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
if ( dtime != 0 ) {
|
|
Debug_Say(( "Anim %s frame %1.3f\n", Animation->Get_Name(), Frame ));
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void AnimChannelClass::Get_Animation_Data( AnimationDataList & list, float weight )
|
|
{
|
|
if ( Animation != NULL && weight > 0 ) {
|
|
AnimationDataRecord * record = list.Uninitialized_Add();
|
|
WWASSERT( record != NULL );
|
|
record->Animation = Animation;
|
|
record->Frame = Frame;
|
|
record->Weight = weight;
|
|
}
|
|
}
|
|
|
|
void AnimChannelClass::Update_Model( RenderObjClass *anim_model )
|
|
{
|
|
if ( Animation ) {
|
|
anim_model->Set_Animation( Animation, Frame );
|
|
} else {
|
|
anim_model->Set_Animation();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** BlendableAnimChannelClass
|
|
*/
|
|
BlendableAnimChannelClass::BlendableAnimChannelClass( void ) :
|
|
BlendTimer( 0 ),
|
|
BlendTotal( 0 )
|
|
{
|
|
}
|
|
|
|
bool BlendableAnimChannelClass::Save( ChunkSaveClass & csave )
|
|
{
|
|
csave.Begin_Chunk( CHUNKID_NEW );
|
|
NewChannel.Save( csave );
|
|
csave.End_Chunk();
|
|
|
|
csave.Begin_Chunk( CHUNKID_OLD );
|
|
OldChannel.Save( csave );
|
|
csave.End_Chunk();
|
|
|
|
csave.Begin_Chunk( CHUNKID_VARIABLES );
|
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_BLEND_TIMER, BlendTimer );
|
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_BLEND_TOTAL, BlendTotal );
|
|
csave.End_Chunk();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BlendableAnimChannelClass::Load( ChunkLoadClass &cload )
|
|
{
|
|
while (cload.Open_Chunk()) {
|
|
switch(cload.Cur_Chunk_ID()) {
|
|
|
|
case CHUNKID_NEW:
|
|
NewChannel.Load( cload );
|
|
break;
|
|
|
|
case CHUNKID_OLD:
|
|
OldChannel.Load( cload );
|
|
break;
|
|
|
|
case CHUNKID_VARIABLES:
|
|
while (cload.Open_Micro_Chunk()) {
|
|
switch(cload.Cur_Micro_Chunk_ID()) {
|
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_BLEND_TIMER, BlendTimer );
|
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_BLEND_TOTAL, BlendTotal );
|
|
default:
|
|
Debug_Say(( "Unrecognized BlendableAnimChannel Variable chunkID\n" ));
|
|
break;
|
|
}
|
|
cload.Close_Micro_Chunk();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Debug_Say(( "Unrecognized BlendableAnimChannel chunkID\n" ));
|
|
break;
|
|
|
|
}
|
|
cload.Close_Chunk();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BlendableAnimChannelClass::Set_Animation( const char *name, float blendtime, float start_frame )
|
|
{
|
|
// if setting to our current anim, bail
|
|
if ( ( NewChannel.Peek_Animation() == NULL ) && ( name == NULL ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ( NewChannel.Peek_Animation() != NULL ) && ( name != NULL ) ) {
|
|
if ( stricmp( NewChannel.Peek_Animation()->Get_Name(), name ) == 0 ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// if no current channel, or no blend, or no new name, don't blend
|
|
if ( (NewChannel.Peek_Animation() == NULL) || (blendtime == 0) || (name == NULL) ) {
|
|
BlendTotal = 0.0f;
|
|
BlendTimer = 0.0f;
|
|
} else if ( BlendTotal == 0.0 ) { //if not currently blending
|
|
OldChannel = NewChannel;
|
|
BlendTimer = 0.0f;
|
|
BlendTotal = blendtime;
|
|
#if 0
|
|
} else if ( OldChannel.Peek_Animation() == new_anim.Peek_Animation() ) { // if old anim is the new one
|
|
OldChannel.Copy_From( NewChannel );
|
|
BlendTimer = (1.0f - (BlendTimer / BlendTotal)) * blendtime;
|
|
BlendTotal = blendtime;
|
|
#endif
|
|
} else if ( (BlendTimer / BlendTotal) > 0.5 ) { // if more than halfway through the old blend
|
|
OldChannel = NewChannel;
|
|
BlendTimer = (1.0f - (BlendTimer / BlendTotal)) * blendtime;
|
|
BlendTotal = blendtime;
|
|
} else {
|
|
BlendTimer = (BlendTimer / BlendTotal) * blendtime;
|
|
BlendTotal = blendtime;
|
|
}
|
|
NewChannel.Set_Animation( name );
|
|
if ( NewChannel.Peek_Animation() != NULL ) {
|
|
NewChannel.Set_Frame( start_frame );
|
|
}
|
|
if ( name == NULL ) {
|
|
OldChannel.Set_Animation( (const char *)NULL );
|
|
}
|
|
}
|
|
|
|
void BlendableAnimChannelClass::Set_Animation( const HAnimClass * anim, float blendtime, float start_frame )
|
|
{
|
|
// if setting to our current anim, bail
|
|
if ( ( NewChannel.Peek_Animation() == NULL ) && ( anim == NULL ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ( NewChannel.Peek_Animation() != NULL ) && ( anim != NULL ) ) {
|
|
if ( NewChannel.Peek_Animation() == anim ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// if no current channel, or no blend, or no new name, don't blend
|
|
if ( (NewChannel.Peek_Animation() == NULL) || (blendtime == 0) || (anim == NULL) ) {
|
|
BlendTotal = 0.0f;
|
|
BlendTimer = 0.0f;
|
|
} else if ( BlendTotal == 0.0 ) { //if not currently blending
|
|
OldChannel = NewChannel;
|
|
BlendTimer = 0.0f;
|
|
BlendTotal = blendtime;
|
|
#if 0
|
|
} else if ( OldChannel.Peek_Animation() == new_anim.Peek_Animation() ) { // if old anim is the new one
|
|
OldChannel.Copy_From( NewChannel );
|
|
BlendTimer = (1.0f - (BlendTimer / BlendTotal)) * blendtime;
|
|
BlendTotal = blendtime;
|
|
#endif
|
|
} else if ( (BlendTimer / BlendTotal) > 0.5 ) { // if more than halfway through the old blend
|
|
OldChannel = NewChannel;
|
|
BlendTimer = (1.0f - (BlendTimer / BlendTotal)) * blendtime;
|
|
BlendTotal = blendtime;
|
|
} else {
|
|
BlendTimer = (BlendTimer / BlendTotal) * blendtime;
|
|
BlendTotal = blendtime;
|
|
}
|
|
NewChannel.Set_Animation( anim );
|
|
if ( NewChannel.Peek_Animation() != NULL ) {
|
|
NewChannel.Set_Frame( start_frame );
|
|
}
|
|
if ( anim == NULL ) {
|
|
OldChannel.Set_Animation( (const HAnimClass *)NULL );
|
|
}
|
|
}
|
|
|
|
|
|
void BlendableAnimChannelClass::Update( float dtime )
|
|
{
|
|
if ( BlendTotal != 0.0f ) { // if blending between two animations
|
|
BlendTimer += dtime; // Bump blend timer forward
|
|
if ( BlendTimer >= BlendTotal ) // blend complete, remove oldanim
|
|
{
|
|
BlendTotal = 0.0f;
|
|
BlendTimer = 0.0f;
|
|
OldChannel.Set_Animation( (const char *)NULL );
|
|
}
|
|
}
|
|
|
|
// Calculate which frame we are on in each of the animations
|
|
NewChannel.Update( dtime );
|
|
OldChannel.Update( dtime );
|
|
}
|
|
|
|
void BlendableAnimChannelClass::Get_Animation_Data( AnimationDataList & list, float weight )
|
|
{
|
|
float blend_ratio = 1.0f; // assume no blending
|
|
if ( BlendTotal != 0.0f ) { // if blending between two animations
|
|
// Calculate the blend percentage between the two animations.
|
|
// This starts at 0.0 (all OldAnimation) and proceeds to 1.0 (all Animation)
|
|
blend_ratio = WWMath::Clamp( BlendTimer / BlendTotal, 0, 1 );
|
|
}
|
|
|
|
NewChannel.Get_Animation_Data( list, weight * blend_ratio );
|
|
OldChannel.Get_Animation_Data( list, weight * ( 1 - blend_ratio ) );
|
|
}
|
|
|
|
|
|
void BlendableAnimChannelClass::Update_Model( RenderObjClass *anim_model )
|
|
{
|
|
float blend_ratio = 1.0f; // assume no blending
|
|
if ( BlendTotal != 0.0f ) { // if blending between two animations
|
|
// Calculate the blend percentage between the two animations.
|
|
// This starts at 0.0 (all OldAnimation) and proceeds to 1.0 (all Animation)
|
|
blend_ratio = WWMath::Clamp( BlendTimer / BlendTotal, 0, 1 );
|
|
}
|
|
|
|
if ( OldChannel.Peek_Animation() ) {
|
|
anim_model->Set_Animation( OldChannel.Peek_Animation(), OldChannel.Get_Frame(),
|
|
NewChannel.Peek_Animation(), NewChannel.Get_Frame(),
|
|
blend_ratio );
|
|
} else if ( NewChannel.Peek_Animation() ) {
|
|
anim_model->Set_Animation( NewChannel.Peek_Animation(), NewChannel.Get_Frame() );
|
|
} else {
|
|
anim_model->Set_Animation();
|
|
}
|
|
}
|
|
|
|
/*
|
|
** AnimControlClass
|
|
*/
|
|
AnimControlClass::AnimControlClass( void ) :
|
|
Model( NULL )
|
|
{
|
|
}
|
|
|
|
AnimControlClass::~AnimControlClass( void )
|
|
{
|
|
REF_PTR_RELEASE( Model );
|
|
}
|
|
|
|
bool AnimControlClass::Save( ChunkSaveClass & csave )
|
|
{
|
|
csave.Begin_Chunk( CHUNKID_VARIABLES );
|
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MODEL_PTR, Model );
|
|
csave.End_Chunk();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AnimControlClass::Load( ChunkLoadClass &cload )
|
|
{
|
|
while (cload.Open_Chunk()) {
|
|
switch(cload.Cur_Chunk_ID()) {
|
|
|
|
case CHUNKID_VARIABLES:
|
|
|
|
WWASSERT( Model == NULL );
|
|
while (cload.Open_Micro_Chunk()) {
|
|
switch(cload.Cur_Micro_Chunk_ID()) {
|
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_MODEL_PTR, Model );
|
|
|
|
default:
|
|
Debug_Say(( "Unrecognized AnimControl Variable chunkID\n" ));
|
|
break;
|
|
}
|
|
cload.Close_Micro_Chunk();
|
|
}
|
|
|
|
if ( Model != NULL ) {
|
|
REQUEST_REF_COUNTED_POINTER_REMAP( (RefCountClass **)&Model );
|
|
} else {
|
|
Debug_Say(( "Loaded NULL model\n" ));
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
Debug_Say(( "Unrecognized AnimControl chunkID\n" ));
|
|
break;
|
|
|
|
}
|
|
cload.Close_Chunk();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void AnimControlClass::Set_Model( RenderObjClass *anim_model )
|
|
{
|
|
REF_PTR_SET( Model, anim_model );
|
|
}
|
|
|
|
/*
|
|
** SimpleAnimControlClass
|
|
*/
|
|
SimpleAnimControlClass::SimpleAnimControlClass( void )
|
|
{
|
|
}
|
|
|
|
SimpleAnimControlClass::~SimpleAnimControlClass( void )
|
|
{
|
|
}
|
|
|
|
bool SimpleAnimControlClass::Save( ChunkSaveClass & csave )
|
|
{
|
|
csave.Begin_Chunk( CHUNKID_PARENT );
|
|
AnimControlClass::Save( csave );
|
|
csave.End_Chunk();
|
|
|
|
csave.Begin_Chunk( CHUNKID_CHANNEL );
|
|
Channel.Save( csave );
|
|
csave.End_Chunk();
|
|
return true;
|
|
}
|
|
|
|
bool SimpleAnimControlClass::Load( ChunkLoadClass &cload )
|
|
{
|
|
while (cload.Open_Chunk()) {
|
|
switch(cload.Cur_Chunk_ID()) {
|
|
|
|
case CHUNKID_PARENT:
|
|
AnimControlClass::Load( cload );
|
|
break;
|
|
|
|
case CHUNKID_CHANNEL:
|
|
Channel.Load( cload );
|
|
break;
|
|
|
|
default:
|
|
Debug_Say(( "Unrecognized HumanAnimControl chunkID\n" ));
|
|
break;
|
|
|
|
}
|
|
cload.Close_Chunk();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SimpleAnimControlClass::Set_Animation( const char *name, float blendtime, float start_frame )
|
|
{
|
|
Channel.Set_Animation( name, blendtime, start_frame );
|
|
}
|
|
|
|
void SimpleAnimControlClass::Set_Animation( const HAnimClass * anim, float blendtime, float start_frame )
|
|
{
|
|
Channel.Set_Animation( anim, blendtime, start_frame );
|
|
}
|
|
|
|
void SimpleAnimControlClass::Update( float dtime )
|
|
{
|
|
Channel.Update( dtime );
|
|
|
|
// Setup the model for the current frame(s)
|
|
assert( Model != NULL );
|
|
|
|
Channel.Update_Model( Model );
|
|
}
|
|
|
|
/*
|
|
** HumanAnimControlClass
|
|
*/
|
|
HumanAnimControlClass::HumanAnimControlClass( void ) :
|
|
AnimCombo( 2 ),
|
|
Channel2Ratio( 0 ),
|
|
Skeleton( 'A' ),
|
|
AnimSpeedScale( 1 )
|
|
{
|
|
}
|
|
|
|
HumanAnimControlClass::~HumanAnimControlClass( void )
|
|
{
|
|
}
|
|
|
|
bool HumanAnimControlClass::Save( ChunkSaveClass & csave )
|
|
{
|
|
csave.Begin_Chunk( CHUNKID_PARENT );
|
|
AnimControlClass::Save( csave );
|
|
csave.End_Chunk();
|
|
|
|
csave.Begin_Chunk( CHUNKID_VARIABLES );
|
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_CHANNEL2_RATIO, Channel2Ratio );
|
|
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SKELETON, Skeleton );
|
|
csave.End_Chunk();
|
|
|
|
csave.Begin_Chunk( CHUNKID_CHANNEL1 );
|
|
Channel1.Save( csave );
|
|
csave.End_Chunk();
|
|
|
|
csave.Begin_Chunk( CHUNKID_CHANNEL2 );
|
|
Channel2.Save( csave );
|
|
csave.End_Chunk();
|
|
|
|
// Don't need to save the animcombo
|
|
|
|
return true;
|
|
}
|
|
|
|
bool HumanAnimControlClass::Load( ChunkLoadClass &cload )
|
|
{
|
|
while (cload.Open_Chunk()) {
|
|
switch(cload.Cur_Chunk_ID()) {
|
|
|
|
case CHUNKID_PARENT:
|
|
AnimControlClass::Load( cload );
|
|
break;
|
|
|
|
case CHUNKID_VARIABLES:
|
|
while (cload.Open_Micro_Chunk()) {
|
|
switch(cload.Cur_Micro_Chunk_ID()) {
|
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_CHANNEL2_RATIO, Channel2Ratio );
|
|
READ_MICRO_CHUNK( cload, MICROCHUNKID_SKELETON, Skeleton );
|
|
|
|
default:
|
|
Debug_Say(( "Unrecognized HumanAnimControl Variable chunkID\n" ));
|
|
break;
|
|
}
|
|
cload.Close_Micro_Chunk();
|
|
}
|
|
break;
|
|
|
|
case CHUNKID_CHANNEL1:
|
|
Channel1.Load( cload );
|
|
break;
|
|
|
|
case CHUNKID_CHANNEL2:
|
|
Channel2.Load( cload );
|
|
break;
|
|
|
|
default:
|
|
Debug_Say(( "Unrecognized HumanAnimControl chunkID\n" ));
|
|
break;
|
|
|
|
}
|
|
cload.Close_Chunk();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void HumanAnimControlClass::Build_Skeleton_Anim_Name( StringClass& new_name, const char * name )
|
|
{
|
|
if (name == NULL) return;
|
|
if (*name == NULL) {
|
|
Debug_Say(( "No name in Build_Skeleton_Anim_Name\n" ));
|
|
return;
|
|
}
|
|
new_name = name;
|
|
|
|
if ( Skeleton == 'V' ) { // Special case for visceroids
|
|
return;
|
|
}
|
|
|
|
// If the anim doesn't start with "S_A_HUMAN.", add it
|
|
if ( ::strnicmp( name, "S_", 2 ) != 0 ) {
|
|
new_name.Format( "S_%c_HUMAN.%s", Skeleton, name );
|
|
}
|
|
|
|
// If the anim name is "S_A_HUMAN.H_A_*", and the Skeleton is not 'A', use
|
|
// the other skeleton anim, if found
|
|
if ( new_name.Get_Length() > 14 && Skeleton != 'A' && ::strnicmp( new_name, "S_A_HUMAN.H_A_", 14 ) == 0 ) {
|
|
StringClass mod_name(new_name,true);
|
|
mod_name[2] = Skeleton;
|
|
mod_name[12] = Skeleton;
|
|
|
|
// can we find the anim name?
|
|
HAnimClass * anim = WW3DAssetManager::Get_Instance()->Get_HAnim( mod_name );
|
|
if ( anim != NULL ) {
|
|
anim->Release_Ref();
|
|
new_name=mod_name;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HumanAnimControlClass::Set_Animation( const char *name, float blendtime, float start_frame )
|
|
{
|
|
StringClass new_name(0,true);
|
|
Build_Skeleton_Anim_Name( new_name, name );
|
|
|
|
Channel1.Set_Animation( new_name, blendtime, start_frame );
|
|
Channel2.Set_Animation( (const char *)NULL );
|
|
Channel2Ratio = 0;
|
|
}
|
|
|
|
void HumanAnimControlClass::Set_Animation( const HAnimClass * anim, float blendtime, float start_frame )
|
|
{
|
|
if ( anim != NULL ) {
|
|
Set_Animation( anim->Get_Name(), blendtime, start_frame );
|
|
} else {
|
|
Set_Animation( (const char *)NULL, blendtime, start_frame );
|
|
}
|
|
}
|
|
|
|
void HumanAnimControlClass::Set_Animation( const char *name1, const char * name2, float ratio, float blendtime )
|
|
{
|
|
StringClass new_name1(0,true);
|
|
Build_Skeleton_Anim_Name( new_name1, name1 );
|
|
StringClass new_name2(0,true);
|
|
Build_Skeleton_Anim_Name( new_name2, name2 );
|
|
|
|
if ( ratio == 0 ) {
|
|
Set_Animation( new_name1, blendtime );
|
|
return;
|
|
}
|
|
if ( Channel2Ratio == 0 ) {
|
|
Channel2 = Channel1;
|
|
}
|
|
Channel1.Set_Animation( new_name1, blendtime );
|
|
Channel2.Set_Animation( new_name2, blendtime );
|
|
Channel2Ratio = ratio;
|
|
}
|
|
|
|
void HumanAnimControlClass::Set_Mode( AnimMode mode, float frame )
|
|
{
|
|
Channel1.Set_Mode( mode, frame );
|
|
Channel2.Set_Mode( mode, frame );
|
|
}
|
|
|
|
void HumanAnimControlClass::Set_Model( RenderObjClass *anim_model )
|
|
{
|
|
AnimControlClass::Set_Model( anim_model );
|
|
|
|
// Update the skeleton
|
|
if( anim_model != NULL ) {
|
|
const HTreeClass * tree = anim_model->Get_HTree();
|
|
if ( tree != NULL ) {
|
|
const char * name = tree->Get_Name();
|
|
Skeleton = name[2];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
**
|
|
*/
|
|
StringClass _MonitorAnimDescription;
|
|
HumanAnimControlClass * _Monitor = NULL;
|
|
|
|
void HumanAnimControlClass::Update( float dtime )
|
|
{
|
|
// Update channels
|
|
Channel1.Update( dtime * AnimSpeedScale );
|
|
Channel2.Update( dtime * AnimSpeedScale );
|
|
|
|
if ( Model != NULL ) {
|
|
|
|
// Get Animation data
|
|
DataList.Reset_Active();
|
|
Channel1.Get_Animation_Data( DataList, (1 - Channel2Ratio) );
|
|
Channel2.Get_Animation_Data( DataList, Channel2Ratio );
|
|
|
|
// Use the cheapest anim method possible
|
|
int total_animations = DataList.Count();
|
|
|
|
if ( total_animations == 0 ) {
|
|
|
|
// Debug_Say(( "No animations to display!\n" ));
|
|
Model->Set_Animation();
|
|
|
|
} else if ( total_animations == 1 ) {
|
|
|
|
// Debug_Say(( "1 animation to display!\n" ));
|
|
Model->Set_Animation( DataList[0].Animation, DataList[0].Frame );
|
|
|
|
} else if ( total_animations == 2 ) {
|
|
|
|
// Debug_Say(( "2 animation to display!\n" ));
|
|
float percent = DataList[1].Weight / (DataList[0].Weight + DataList[1].Weight);
|
|
Model->Set_Animation( DataList[0].Animation, DataList[0].Frame,
|
|
DataList[1].Animation, DataList[1].Frame, percent );
|
|
|
|
} else {
|
|
|
|
// Debug_Say(( ">2 animation to display!\n" ));
|
|
|
|
// set up anim combo
|
|
AnimCombo.Reset();
|
|
for ( int i = 0; i < total_animations; i++ ) {
|
|
HAnimComboDataClass * anim_data = new HAnimComboDataClass();
|
|
anim_data->Set_HAnim( DataList[i].Animation );
|
|
anim_data->Set_Frame( DataList[i].Frame );
|
|
anim_data->Set_Weight( DataList[i].Weight );
|
|
AnimCombo.Append_Anim_Combo_Data( anim_data );
|
|
}
|
|
|
|
// Setup the model for the current frame(s)
|
|
Model->Set_Animation( &AnimCombo );
|
|
}
|
|
|
|
if ( _Monitor == this ) {
|
|
for ( int i = 0; i < total_animations; i++ ) {
|
|
if ( i == 0 ) {
|
|
_MonitorAnimDescription = "";
|
|
}
|
|
StringClass a(0,true);
|
|
a.Format( "%s %1.0f%% %1.0f\n", DataList[i].Animation->Get_Name()+10, DataList[i].Weight*100, DataList[i].Frame );
|
|
_MonitorAnimDescription += a;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
**
|
|
*/
|
|
void HumanAnimControlClass::Get_Information( StringClass & string )
|
|
{
|
|
_Monitor = this;
|
|
string += _MonitorAnimDescription;
|
|
}
|
|
|