This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Combat/animcontrol.cpp

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;
}