/* ** 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 . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : WW3D * * * * $Archive:: /Commando/Code/ww3d2/hrawanim.cpp $* * * * Original Author:: Greg Hjelstrom * * * * $Author:: Jani_p $* * * * $Modtime:: 11/25/01 6:06p $* * * * $Revision:: 5 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * HRawAnimClass::HRawAnimClass -- constructor * * HRawAnimClass::~HRawAnimClass -- Destructor * * HRawAnimClass::Free -- De-allocates all memory in use * * HRawAnimClass::Load -- Loads hierarchy animation from a file * * HRawAnimClass::read_channel -- Reads in a single channel of motion * * HRawAnimClass::add_channel -- Adds a motion channel to the animation * * HRawAnimClass::Get_Translation -- returns the translation vector for the given frame * * HRawAnimClass::Get_Orientation -- returns a quaternion for the orientation of the pivot * * HRawAnimClass::Get_XRotation -- Returns the X euler angle for the given pivot, frame * * HRawAnimClass::Get_YRotation -- returns the Y Euler angle for the given pivot, frame * * HRawAnimClass::Get_ZRotation -- returns the Z Euler angle for the given pivot, frame * * HRawAnimClass::read_bit_channel -- read a bit channel from the file * * HRawAnimClass::add_bit_channel -- install a bit channel into the animation * * HRawAnimClass::Get_Visibility -- return visibility state for given pivot/frame * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "hrawanim.h" #include "motchan.h" #include "chunkio.h" #include "assetmgr.h" #include "htree.h" struct NodeMotionStruct { NodeMotionStruct(); ~NodeMotionStruct(); MotionChannelClass * X; MotionChannelClass * Y; MotionChannelClass * Z; MotionChannelClass * XR; MotionChannelClass * YR; MotionChannelClass * ZR; MotionChannelClass * Q; BitChannelClass * Vis; }; /*********************************************************************************************** * NodeMotionStruct::NodeMotionStruct -- constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * *=============================================================================================*/ NodeMotionStruct::NodeMotionStruct() : X(NULL), Y(NULL), Z(NULL), XR(NULL), YR(NULL), ZR(NULL), Q(NULL), Vis(NULL) { } /*********************************************************************************************** * NodeMotionStruct::~NodeMotionStruct -- destructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 10/23/98 GTH : Created. * *=============================================================================================*/ NodeMotionStruct::~NodeMotionStruct() { if (X != NULL) { delete X; } if (Y != NULL) { delete Y; } if (Z != NULL) { delete Z; } if (XR != NULL) { delete XR; } if (YR != NULL) { delete YR; } if (ZR != NULL) { delete ZR; } if (Q != NULL) { delete Q; } if (Vis != NULL) { delete Vis; } } /*********************************************************************************************** * HRawAnimClass::HRawAnimClass -- constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ HRawAnimClass::HRawAnimClass(void) : NumFrames(0), NumNodes(0), FrameRate(0), NodeMotion(NULL) { memset(Name,0,W3D_NAME_LEN); memset(HierarchyName,0,W3D_NAME_LEN); } /*********************************************************************************************** * HRawAnimClass::~HRawAnimClass -- Destructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ HRawAnimClass::~HRawAnimClass(void) { Free(); } /*********************************************************************************************** * HRawAnimClass::Free -- De-allocates all memory in use * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ void HRawAnimClass::Free(void) { if (NodeMotion != NULL) { delete[] NodeMotion; } } /*********************************************************************************************** * HRawAnimClass::Load -- Loads hierarchy animation from a file * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ int HRawAnimClass::Load_W3D(ChunkLoadClass & cload) { bool pre30 = false; /* ** First make sure we release any memory in use */ Free(); /* ** Open the first chunk, it should be the animation header */ if (!cload.Open_Chunk()) return LOAD_ERROR; if (cload.Cur_Chunk_ID() != W3D_CHUNK_ANIMATION_HEADER) { // ERROR: Expected Animation Header! return LOAD_ERROR; } W3dAnimHeaderStruct aheader; if (cload.Read(&aheader,sizeof(W3dAnimHeaderStruct)) != sizeof(W3dAnimHeaderStruct)) { return LOAD_ERROR; } cload.Close_Chunk(); /* ** Check if the animation version is pre-3.0. If so, we need to add 1 to all of the ** bone indexes. In version 3.0 onward, all htree's use bone 0 as the root node. */ if (aheader.Version < W3D_MAKE_VERSION(3,0)) { pre30 = true; } strcpy(Name,aheader.HierarchyName); strcat(Name,"."); strcat(Name,aheader.Name); // TSS chasing crash bug 05/26/99 WWASSERT(HierarchyName != NULL); WWASSERT(aheader.HierarchyName != NULL); WWASSERT(sizeof(HierarchyName) >= W3D_NAME_LEN); strncpy(HierarchyName,aheader.HierarchyName,W3D_NAME_LEN); HTreeClass * base_pose = WW3DAssetManager::Get_Instance()->Get_HTree(HierarchyName); if (base_pose == NULL) { goto Error; } NumNodes = base_pose->Num_Pivots(); NumFrames = aheader.NumFrames; FrameRate = aheader.FrameRate; NodeMotion = new NodeMotionStruct[ NumNodes ]; if (NodeMotion == NULL) { goto Error; } /* ** Now, read in all of the other chunks (motion channels). */ MotionChannelClass * newchan; BitChannelClass * newbitchan; while (cload.Open_Chunk()) { switch (cload.Cur_Chunk_ID()) { case W3D_CHUNK_ANIMATION_CHANNEL: if (!read_channel(cload,&newchan,pre30)) { goto Error; } // (gth) if the channel is referring to a node which is outside the range, // just throw away the channel. This probably means the animation must // be re-exported if (newchan->Get_Pivot() < NumNodes) { add_channel(newchan); } else { WWDEBUG_SAY(("Animation %s referring to missing Bone! Please re-export.\n",Name)); delete newchan; } break; case W3D_CHUNK_BIT_CHANNEL: if (!read_bit_channel(cload,&newbitchan,pre30)) { goto Error; } // (gth) if the channel is referring to a node which is outside the range, // just throw away the channel. This probably means the animation must // be re-exported if (newbitchan->Get_Pivot() < NumNodes) { add_bit_channel(newbitchan); } else { WWDEBUG_SAY(("Animation %s referring to missing Bone! Please re-export.\n",Name)); delete newbitchan; } break; default: break; } cload.Close_Chunk(); } return OK; Error: Free(); return LOAD_ERROR; } /*********************************************************************************************** * HRawAnimClass::read_channel -- Reads in a single channel of motion * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ bool HRawAnimClass::read_channel(ChunkLoadClass & cload,MotionChannelClass * * newchan,bool pre30) { *newchan = new MotionChannelClass; bool result = (*newchan)->Load_W3D(cload); if (result && pre30) { // (*newchan)->PivotIdx += 1; (*newchan)->Set_Pivot((*newchan)->Get_Pivot()+1); } return result; } /*********************************************************************************************** * HRawAnimClass::add_channel -- Adds a motion channel to the animation * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ void HRawAnimClass::add_channel(MotionChannelClass * newchan) { int idx = newchan->Get_Pivot(); switch (newchan->Get_Type()) { case ANIM_CHANNEL_X: NodeMotion[idx].X = newchan; break; case ANIM_CHANNEL_Y: NodeMotion[idx].Y = newchan; break; case ANIM_CHANNEL_Z: NodeMotion[idx].Z = newchan; break; case ANIM_CHANNEL_XR: NodeMotion[idx].XR = newchan; break; case ANIM_CHANNEL_YR: NodeMotion[idx].YR = newchan; break; case ANIM_CHANNEL_ZR: NodeMotion[idx].ZR = newchan; break; case ANIM_CHANNEL_Q: NodeMotion[idx].Q = newchan; break; } } /*********************************************************************************************** * HRawAnimClass::read_bit_channel -- read a bit channel from the file * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/19/98 GTH : Created. * *=============================================================================================*/ bool HRawAnimClass::read_bit_channel(ChunkLoadClass & cload,BitChannelClass * * newchan,bool pre30) { *newchan = new BitChannelClass; bool result = (*newchan)->Load_W3D(cload); if (result && pre30) { (*newchan)->PivotIdx += 1; } return result; } /*********************************************************************************************** * HRawAnimClass::add_bit_channel -- install a bit channel into the animation * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/19/98 GTH : Created. * *=============================================================================================*/ void HRawAnimClass::add_bit_channel(BitChannelClass * newchan) { int idx = newchan->Get_Pivot(); switch (newchan->Get_Type()) { case BIT_CHANNEL_VIS: NodeMotion[idx].Vis = newchan; break; } } /*********************************************************************************************** * HRawAnimClass::Get_Translation -- returns the translation vector for the given fr * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ void HRawAnimClass::Get_Translation(Vector3& trans, int pividx, float frame ) const { struct NodeMotionStruct * motion = &NodeMotion[pividx]; if ( (motion->X == NULL) && (motion->Y == NULL) && (motion->Z == NULL) ) { trans.Set(0.0f,0.0f,0.0f); return; } // int frame0 = (int)frame; int frame0=WWMath::Float_To_Long(frame-0.499999f); int frame1 = frame0 + 1; float ratio = frame - (float)frame0; WWASSERT( (ratio >= -WWMATH_EPSILON) && (ratio < 1.0f + WWMATH_EPSILON) ); if ( frame1 >= NumFrames ) { frame1 = 0; } Vector3 trans0(0.0f,0.0f,0.0f); if (motion->X != NULL) { motion->X->Get_Vector((int)frame0,&(trans0[0])); } if (motion->Y != NULL) { motion->Y->Get_Vector((int)frame0,&(trans0[1])); } if (motion->Z != NULL) { motion->Z->Get_Vector((int)frame0,&(trans0[2])); } if ( ratio == 0.0f ) { trans=trans0; return; } Vector3 trans1(0.0f,0.0f,0.0f); if (motion->X != NULL) { motion->X->Get_Vector((int)frame1,&(trans1[0])); } if (motion->Y != NULL) { motion->Y->Get_Vector((int)frame1,&(trans1[1])); } if (motion->Z != NULL) { motion->Z->Get_Vector((int)frame1,&(trans1[2])); } trans = Lerp( trans0, trans1, ratio ); } /*********************************************************************************************** * HRawAnimClass::Get_Orientation -- returns a quaternion for the orientation of the * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ void HRawAnimClass::Get_Orientation(Quaternion& q, int pividx,float frame) const { // int frame0 = (int)frame; int frame0=WWMath::Float_To_Long(frame-0.499999f); int frame1 = frame0 + 1; float ratio = frame - (float)frame0; WWASSERT( (ratio >= -WWMATH_EPSILON) && (ratio < 1.0f + WWMATH_EPSILON) ); if ( frame1 >= NumFrames ) { frame1 = 0; } float vals[4]; Quaternion q0(1); if (NodeMotion[pividx].Q != NULL) { NodeMotion[pividx].Q->Get_Vector((int)frame0,vals); q0.Set(vals[0],vals[1],vals[2],vals[3]); } if ( ratio == 0.0f ) { q=q0; return; } Quaternion q1(1); if (NodeMotion[pividx].Q != NULL) { NodeMotion[pividx].Q->Get_Vector((int)frame1,vals); q1.Set(vals[0],vals[1],vals[2],vals[3]); } Fast_Slerp(q, q0, q1, ratio ); } /*********************************************************************************************** * HRawAnimClass::Get_Transform -- returns the transform matrix for the given frame * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ void HRawAnimClass::Get_Transform(Matrix3D& mtx, int pividx, float frame ) const { struct NodeMotionStruct * motion = &NodeMotion[pividx]; // if ( (motion->X == NULL) && (motion->Y == NULL) && (motion->Z == NULL) ) { // trans.Set(0.0f,0.0f,0.0f); // return; // } int frame0=WWMath::Float_To_Long(frame-0.499999f); int frame1 = frame0 + 1; float ratio = frame - (float)frame0; WWASSERT( (ratio >= -WWMATH_EPSILON) && (ratio < 1.0f + WWMATH_EPSILON) ); if ( frame1 >= NumFrames ) { frame1 = 0; } float vals[4]; Quaternion q0(1); if (NodeMotion[pividx].Q != NULL) { NodeMotion[pividx].Q->Get_Vector((int)frame0,vals); q0.Set(vals[0],vals[1],vals[2],vals[3]); } if ( ratio == 0.0f ) { mtx=::Build_Matrix3D(q0); if (motion->X != NULL) motion->X->Get_Vector((int)frame0,&(mtx[0][3])); if (motion->Y != NULL) motion->Y->Get_Vector((int)frame0,&(mtx[1][3])); if (motion->Z != NULL) motion->Z->Get_Vector((int)frame0,&(mtx[2][3])); return; } Quaternion q1(1); if (NodeMotion[pividx].Q != NULL) { NodeMotion[pividx].Q->Get_Vector((int)frame1,vals); q1.Set(vals[0],vals[1],vals[2],vals[3]); } Quaternion q; Fast_Slerp(q, q0, q1, ratio ); mtx=::Build_Matrix3D(q); Vector3 trans0(0.0f,0.0f,0.0f); if (motion->X != NULL) motion->X->Get_Vector((int)frame0,&(trans0[0])); if (motion->Y != NULL) motion->Y->Get_Vector((int)frame0,&(trans0[1])); if (motion->Z != NULL) motion->Z->Get_Vector((int)frame0,&(trans0[2])); Vector3 trans1(0.0f,0.0f,0.0f); if (motion->X != NULL) motion->X->Get_Vector((int)frame1,&(trans1[0])); if (motion->Y != NULL) motion->Y->Get_Vector((int)frame1,&(trans1[1])); if (motion->Z != NULL) motion->Z->Get_Vector((int)frame1,&(trans1[2])); Vector3 trans = Lerp( trans0, trans1, ratio ); mtx.Set_Translation(trans); } /*********************************************************************************************** * HRawAnimClass::Get_Visibility -- return visibility state for given pivot/frame * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/19/98 GTH : Created. * *=============================================================================================*/ bool HRawAnimClass::Get_Visibility(int pividx,float frame) { if (NodeMotion[pividx].Vis != NULL) { return (NodeMotion[pividx].Vis->Get_Bit((int)frame) == 1); } // default to always visible... return 1; } /*********************************************************************************************** * HRawAnimClass::Is_Node_Motion_Present -- return true if there is motion defined for this frame * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 3/23/99 EHC : Created. * *=============================================================================================*/ bool HRawAnimClass::Is_Node_Motion_Present(int pividx) { WWASSERT((pividx >= 0) && (pividx < NumNodes)); if (NodeMotion[pividx].X != NULL) return true; if (NodeMotion[pividx].Y != NULL) return true; if (NodeMotion[pividx].Z != NULL) return true; if (NodeMotion[pividx].XR != NULL) return true; if (NodeMotion[pividx].YR != NULL) return true; if (NodeMotion[pividx].ZR != NULL) return true; if (NodeMotion[pividx].Q != NULL) return true; if (NodeMotion[pividx].Vis != NULL) return true; return false; } bool HRawAnimClass::Has_X_Translation (int pividx) { WWASSERT((pividx >= 0) && (pividx < NumNodes)); return NodeMotion[pividx].X != NULL; } bool HRawAnimClass::Has_Y_Translation (int pividx) { WWASSERT((pividx >= 0) && (pividx < NumNodes)); return NodeMotion[pividx].Y != NULL; } bool HRawAnimClass::Has_Z_Translation (int pividx) { WWASSERT((pividx >= 0) && (pividx < NumNodes)); return NodeMotion[pividx].Z != NULL; } bool HRawAnimClass::Has_Rotation (int pividx) { WWASSERT((pividx >= 0) && (pividx < NumNodes)); return NodeMotion[pividx].Q != NULL; } bool HRawAnimClass::Has_Visibility (int pividx) { WWASSERT((pividx >= 0) && (pividx < NumNodes)); return NodeMotion[pividx].Vis != NULL; }