| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742 |
- /*
- ** Command & Conquer Generals(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/>.
- */
- /***********************************************************************************************
- *** 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/hmorphanim.cpp $*
- * *
- * Original Author:: Greg Hjelstrom *
- * *
- * $Author:: Jani_p $*
- * *
- * $Modtime:: 6/27/01 7:50p $*
- * *
- * $Revision:: 5 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "hmorphanim.h"
- #include "w3d_file.h"
- #include "chunkio.h"
- #include "assetmgr.h"
- #include "htree.h"
- #include "wwstring.h"
- #include "textfile.h"
- #include "simplevec.h"
- TimeCodedMorphKeysClass::TimeCodedMorphKeysClass(void)
- : CachedIdx (0)
- {
- }
- TimeCodedMorphKeysClass::~TimeCodedMorphKeysClass(void)
- {
- Free();
- }
- void TimeCodedMorphKeysClass::Free(void)
- {
- Keys.Delete_All ();
- CachedIdx = 0;
- }
- bool TimeCodedMorphKeysClass::Load_W3D(ChunkLoadClass & cload)
- {
- Free();
- uint32 key_count = cload.Cur_Chunk_Length() / sizeof(W3dMorphAnimKeyStruct);
- W3dMorphAnimKeyStruct w3dkey;
- for (uint32 i=0; i<key_count; i++) {
- cload.Read(&w3dkey,sizeof(w3dkey));
- Keys.Add (MorphKeyStruct (w3dkey.MorphFrame, w3dkey.PoseFrame));
- }
- CachedIdx = 0;
- return true;
- }
- bool TimeCodedMorphKeysClass::Save_W3D(ChunkSaveClass & csave)
- {
- W3dMorphAnimKeyStruct w3dkey;
- for (int i=0; i<Keys.Count (); i++) {
- w3dkey.MorphFrame = Keys[i].MorphFrame;
- w3dkey.PoseFrame = Keys[i].PoseFrame;
- csave.Write(&w3dkey,sizeof(w3dkey));
- }
- return true;
- }
- void TimeCodedMorphKeysClass::Add_Key (uint32 morph_frame, uint32 pose_frame)
- {
- Keys.Add (MorphKeyStruct (morph_frame, pose_frame));
- return ;
- }
- void TimeCodedMorphKeysClass::Get_Morph_Info(float morph_frame,int * pose_frame0,int * pose_frame1,float * fraction)
- {
- if (morph_frame < 0.0f) {
- *pose_frame0 = *pose_frame1 = Keys[0].PoseFrame;
- *fraction = 0.0f;
- return;
- }
- if (morph_frame >= Keys[Keys.Count ()-1].MorphFrame) {
- *pose_frame0 = *pose_frame1 = Keys[Keys.Count ()-1].PoseFrame;
- *fraction = 0.0f;
- return;
- }
- int key_index = get_index(morph_frame);
- *pose_frame0 = Keys[key_index].PoseFrame;
- *pose_frame1 = Keys[key_index+1].PoseFrame;
- *fraction = (morph_frame - Keys[key_index].MorphFrame) / (Keys[key_index+1].MorphFrame - Keys[key_index].MorphFrame);
- }
- uint32 TimeCodedMorphKeysClass::get_index(float frame)
- {
- assert(CachedIdx <= (uint32)Keys.Count ()-1);
- float cached_frame = Keys[CachedIdx].MorphFrame;
- // check if the requested time is in the cached interval or the following one
- if (frame >= cached_frame) {
- // special case for end packets
- if (CachedIdx == (uint32)Keys.Count ()-1) return(CachedIdx);
-
- // check if the requested time is still in the cached interval
- if (frame < Keys[CachedIdx + 1].MorphFrame) return(CachedIdx);
- // do one time look-ahead before reverting to a search
- CachedIdx++;
-
- // again, special case the end interval
- if (CachedIdx == (uint32)Keys.Count ()-1) return(CachedIdx);
- // check if requested time is in this interval
- if (frame < Keys[CachedIdx + 1].MorphFrame) return(CachedIdx);
- }
- // nope, fall back to a binary search for the requested interval
- CachedIdx = binary_search_index(frame);
- return(CachedIdx);
- }
- uint32 TimeCodedMorphKeysClass::binary_search_index(float req_frame)
- {
- // special case first and last packet
- if (req_frame < Keys[0].MorphFrame) return 0;
- if (req_frame >= Keys[Keys.Count ()-1].MorphFrame) return Keys.Count ()-1;
- int leftIdx = 0;
- int rightIdx = Keys.Count ();
- int idx,dx;
-
- // binary search for the desired interval
- for (;;) {
-
- // if we've zeroed in on the interval, return the left index
- dx = rightIdx - leftIdx;
- if (dx == 1) {
- WWASSERT(req_frame >= Keys[leftIdx].MorphFrame);
- WWASSERT(req_frame < Keys[rightIdx].MorphFrame);
- return leftIdx;
- }
- // otherwise, split our interval in half and descend into one of them
- dx>>=1;
- idx = leftIdx + dx;
- if (req_frame < Keys[idx].MorphFrame) {
- rightIdx = idx;
- } else {
- leftIdx = idx;
- }
- }
- assert(0);
- return(0);
- }
- /*********************************************************************************************
- **
- ** HMorphAnimClass Implementation
- **
- *********************************************************************************************/
- HMorphAnimClass::HMorphAnimClass(void) :
- FrameCount(0),
- FrameRate(0.0f),
- ChannelCount(0),
- NumNodes(0),
- PoseData(NULL),
- MorphKeyData(NULL),
- PivotChannel(NULL)
- {
- memset(Name,0,sizeof(Name));
- memset(HierarchyName,0,sizeof(HierarchyName));
- }
- HMorphAnimClass::~HMorphAnimClass(void)
- {
- Free();
- }
- void HMorphAnimClass::Free(void)
- {
- if (PoseData != NULL) {
- for (int i=0; i<ChannelCount; i++) {
- REF_PTR_RELEASE(PoseData[i]);
- }
- delete[] PoseData;
- PoseData = NULL;
- }
- if (MorphKeyData != NULL) {
- delete[] MorphKeyData;
- MorphKeyData = NULL;
- }
- if (PivotChannel != NULL) {
- delete[] PivotChannel;
- PivotChannel = NULL;
- }
- }
- int Build_List_From_String
- (
- const char * buffer,
- const char * delimiter,
- StringClass ** string_list
- )
- {
- int count = 0;
- WWASSERT (buffer != NULL);
- WWASSERT (delimiter != NULL);
- WWASSERT (string_list != NULL);
- if ((buffer != NULL) &&
- (delimiter != NULL) &&
- (string_list != NULL))
- {
- int delim_len = ::strlen (delimiter);
- //
- // Determine how many entries there will be in the list
- //
- for (const char *entry = buffer;
- (entry != NULL) && (entry[1] != 0);
- entry = ::strstr (entry, delimiter))
- {
-
- //
- // Move past the current delimiter (if necessary)
- //
- if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
- entry += delim_len;
- }
- // Increment the count of entries
- count ++;
- }
-
- if (count > 0) {
- //
- // Allocate enough StringClass objects to hold all the strings in the list
- //
- (*string_list) = W3DNEWARRAY StringClass[count];
-
- //
- // Parse the string and pull out its entries.
- //
- count = 0;
- for (entry = buffer;
- (entry != NULL) && (entry[1] != 0);
- entry = ::strstr (entry, delimiter))
- {
-
- //
- // Move past the current delimiter (if necessary)
- //
- if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
- entry += delim_len;
- }
- //
- // Copy this entry into its own string
- //
- StringClass entry_string = entry;
- char *delim_start = ::strstr (entry_string, delimiter);
- if (delim_start != NULL) {
- delim_start[0] = 0;
- }
- //
- // Add this entry to our list
- //
- if ((entry_string.Get_Length () > 0) || (count == 0)) {
- (*string_list)[count++] = entry_string;
- }
- }
- } else if (delim_len > 0) {
- count = 1;
- (*string_list) = W3DNEWARRAY StringClass[count];
- (*string_list)[0] = buffer;
- }
-
- }
- //
- // Return the number of entries in our list
- //
- return count;
- }
- bool Is_Number (const char *str)
- {
- bool retval = true;
- while (retval && str[0] != NULL){
- retval = ((str[0] >= '0' && str[0] <= '9') || str[0] == '-' || str[0] == '.');
- str ++;
- }
- return retval;
- }
- bool HMorphAnimClass::Import(const char *hierarchy_name, TextFileClass &text_desc)
- {
- bool retval = false;
- Free ();
- FrameCount = 0;
- FrameRate = 30.0F;
- //
- // Copy the hierarchy name into a class variable
- //
- ::strncpy (HierarchyName, hierarchy_name, W3D_NAME_LEN);
- HierarchyName[W3D_NAME_LEN - 1] = 0;
-
- //
- // Attempt to load the new base pose
- //
- HTreeClass * base_pose = WW3DAssetManager::Get_Instance()->Get_HTree(HierarchyName);
- WWASSERT (base_pose != NULL);
- NumNodes = base_pose->Num_Pivots();
- //
- // Read the header from the file
- //
- StringClass header;
- bool success = text_desc.Read_Line (header);
- if (success) {
- //
- // Get the list of comma-delimited strings from the header
- //
- StringClass *column_list = NULL;
- int column_count = Build_List_From_String (header, ",", &column_list);
- //
- // The first column header should be 'Frame#', all other headers
- // should be channel animation names.
- //
- ChannelCount = column_count - 1;
- WWASSERT (ChannelCount > 0);
- if (ChannelCount > 0) {
-
- //
- // Allocate and initialize each animation channel
- //
- PoseData = W3DNEWARRAY HAnimClass *[ChannelCount];
- MorphKeyData = W3DNEWARRAY TimeCodedMorphKeysClass[ChannelCount];
- for (int index = 0; index < ChannelCount; index ++) {
- StringClass channel_anim_name;
- channel_anim_name.Format ("%s.%s", HierarchyName, (const char *)column_list[index + 1]);
- PoseData[index] = WW3DAssetManager::Get_Instance()->Get_HAnim (channel_anim_name);
- }
- //
- // Now read the animation data for each frame
- //
- StringClass frame_desc;
- while (text_desc.Read_Line (frame_desc)) {
- //
- // Get the frame descriptions from this line
- //
- StringClass *channel_list = NULL;
- int list_count = Build_List_From_String (frame_desc, ",", &channel_list);
- WWASSERT (list_count > 0);
- if (list_count > 0) {
- //
- // The first column contains the morph frame number
- //
- int frame = ::atoi (channel_list[0]);
- //
- // Now read the animation frame number for each channel
- //
- for (int index = 1; index < list_count; index ++) {
- StringClass &channel_frame = channel_list[index];
-
- //
- // If this channel contains a valid number, then record
- // its animation frame
- //
- if (::Is_Number (channel_frame)) {
- MorphKeyData[index - 1].Add_Key (frame, ::atoi (channel_frame));
- }
- }
- FrameCount = frame + 1;
- }
- //
- // Cleanup
- //
- if (channel_list != NULL) {
- delete [] channel_list;
- channel_list = NULL;
- }
- }
- //
- // Allocate the pivot channel list
- //
- PivotChannel = W3DNEWARRAY uint32[NumNodes];
- Resolve_Pivot_Channels ();
- }
- //
- // Cleanup
- //
- if (column_list != NULL) {
- delete [] column_list;
- column_list = NULL;
- }
- }
- return retval;
- }
- void HMorphAnimClass::Resolve_Pivot_Channels(void)
- {
- WWASSERT (PivotChannel != NULL);
- //
- // Loop over all the pivots in the HTree
- //
- for (int pivot = 0; pivot < NumNodes; pivot ++) {
- PivotChannel[pivot] = 0;
- //
- // Find out which animation channel affects this pivot
- //
- for (int channel = 0; channel < ChannelCount; channel ++) {
- if (PoseData[channel]->Is_Node_Motion_Present (pivot)) {
- PivotChannel[pivot] = channel;
- }
- }
- }
- return ;
- }
- void HMorphAnimClass::Set_Name(const char * name)
- {
- //
- // Copy the full name
- //
- ::strcpy (Name, name);
- //
- // Try to find the separator (a period)
- //
- StringClass full_name = name;
- char *separator = ::strchr (full_name, '.');
- if (separator != NULL) {
-
- //
- // Null out the separator and copy the two names
- // into our two buffers
- //
- separator[0] = 0;
- ::strcpy (AnimName, separator + 1);
- ::strcpy (HierarchyName, full_name);
- }
- return ;
- }
- void HMorphAnimClass::Free_Morph(void)
- {
- Free();
- }
- int HMorphAnimClass::Create_New_Morph(const int channels, HAnimClass *anim[])
- {
- // clean out the previous instance of this class
- Free();
- // set the number of channels
- ChannelCount = channels;
- // read in the animation header
- if (anim == NULL) {
- return LOAD_ERROR;
- }
-
- // set up info
- // FrameCount = anim[0]->Get_Num_Frames();
- // FrameRate = anim[0]->Get_Frame_Rate();
- FrameCount = 0;
- FrameRate = 30.0f;
- NumNodes = anim[0]->Get_Num_Pivots();
- // Set up the anim data for all the channels
- PoseData = W3DNEWARRAY HAnimClass * [ChannelCount];
- for(int i=0;i<ChannelCount;i++)
- PoseData[i] = anim[i];
- // Create a timecodekey array for each channel and initialize the pivot channels
- MorphKeyData = W3DNEWARRAY TimeCodedMorphKeysClass[ChannelCount];
- PivotChannel = W3DNEWARRAY uint32[NumNodes];
- // Resolve the pivots so that they correspond to the proper morphing channels
- memset(PivotChannel,0,NumNodes * sizeof(uint32));
- Resolve_Pivot_Channels();
-
- // Signal successful process
- return OK;
- }
- int HMorphAnimClass::Load_W3D(ChunkLoadClass & cload)
- {
- Free();
- // read in the animation header
- W3dMorphAnimHeaderStruct header;
- cload.Open_Chunk();
- WWASSERT(cload.Cur_Chunk_ID() == W3D_CHUNK_MORPHANIM_HEADER);
- cload.Read(&header,sizeof(header));
- cload.Close_Chunk();
- strncpy(AnimName,header.Name,sizeof(AnimName));
- strncpy(HierarchyName,header.HierarchyName,sizeof(HierarchyName));
- strcpy(Name,HierarchyName);
- strcat(Name,".");
- strcat(Name,AnimName);
- HTreeClass * base_pose = WW3DAssetManager::Get_Instance()->Get_HTree(HierarchyName);
- if (base_pose == NULL) {
- return LOAD_ERROR;
- }
- NumNodes = base_pose->Num_Pivots();
- FrameCount = header.FrameCount;
- FrameRate = header.FrameRate;
- ChannelCount = header.ChannelCount;
- PoseData = W3DNEWARRAY HAnimClass * [ChannelCount];
- MorphKeyData = W3DNEWARRAY TimeCodedMorphKeysClass[ChannelCount];
- PivotChannel = W3DNEWARRAY uint32[NumNodes];
- memset(PivotChannel,0,NumNodes * sizeof(uint32));
- // read in the rest of the chunks
- int cur_channel = 0;
- while (cload.Open_Chunk()) {
- switch(cload.Cur_Chunk_ID())
- {
- case W3D_CHUNK_MORPHANIM_CHANNEL:
- read_channel(cload,cur_channel++);
- break;
- case W3D_CHUNK_MORPHANIM_PIVOTCHANNELDATA:
- cload.Read(PivotChannel,cload.Cur_Chunk_Length());
- break;
- };
- cload.Close_Chunk();
- }
- return OK;
- }
- void HMorphAnimClass::read_channel(ChunkLoadClass & cload,int channel)
- {
- WWASSERT(channel >= 0);
- WWASSERT(channel < ChannelCount);
- StringClass anim_name;
- cload.Open_Chunk();
- WWASSERT(cload.Cur_Chunk_ID() == W3D_CHUNK_MORPHANIM_POSENAME);
- cload.Read(anim_name.Get_Buffer(cload.Cur_Chunk_Length()),cload.Cur_Chunk_Length());
- cload.Close_Chunk();
-
- //StringClass channel_anim_name;
- //channel_anim_name.Format ("%s.%s", HierarchyName, anim_name);
- PoseData[channel] = WW3DAssetManager::Get_Instance()->Get_HAnim(anim_name);
- WWASSERT(PoseData[channel] != NULL);
- cload.Open_Chunk();
- WWASSERT(cload.Cur_Chunk_ID() == W3D_CHUNK_MORPHANIM_KEYDATA);
- MorphKeyData[channel].Load_W3D(cload);
- cload.Close_Chunk();
- }
- int HMorphAnimClass::Save_W3D(ChunkSaveClass & csave)
- {
- // W3D objects write their own wrapper chunks
- csave.Begin_Chunk(W3D_CHUNK_MORPH_ANIMATION);
-
- // init the header data
- W3dMorphAnimHeaderStruct header;
- memset(&header,0,sizeof(header));
- strncpy(header.Name,AnimName,sizeof(header.Name));
- strncpy(header.HierarchyName,HierarchyName,sizeof(header.HierarchyName));
- header.FrameCount = FrameCount;
- header.FrameRate = FrameRate;
- header.ChannelCount = ChannelCount;
- // write out the animation header
- csave.Begin_Chunk(W3D_CHUNK_MORPHANIM_HEADER);
- csave.Write(&header,sizeof(header));
- csave.End_Chunk();
- // write out the morph channels
- for (int ci=0; ci<ChannelCount; ci++) {
- csave.Begin_Chunk(W3D_CHUNK_MORPHANIM_CHANNEL);
- write_channel(csave,ci);
- csave.End_Chunk();
- }
- // write out the pivot attachments
- csave.Begin_Chunk(W3D_CHUNK_MORPHANIM_PIVOTCHANNELDATA);
- csave.Write(PivotChannel,NumNodes * sizeof(uint32));
- csave.End_Chunk();
- csave.End_Chunk();
- return OK;
- }
- void HMorphAnimClass::write_channel(ChunkSaveClass & csave,int channel)
- {
- WWASSERT(PoseData[channel] != NULL);
- const char * pose_name = PoseData[channel]->Get_Name();
- csave.Begin_Chunk(W3D_CHUNK_MORPHANIM_POSENAME);
- csave.Write(pose_name,strlen(pose_name) + 1);
- csave.End_Chunk();
- csave.Begin_Chunk(W3D_CHUNK_MORPHANIM_KEYDATA);
- MorphKeyData[channel].Save_W3D(csave);
- csave.End_Chunk();
- }
- void HMorphAnimClass::Get_Translation(Vector3& trans,int pividx,float frame) const
- {
- int channel = PivotChannel[pividx];
- int pose_frame0,pose_frame1;
- float fraction;
- MorphKeyData[channel].Get_Morph_Info(frame,&pose_frame0,&pose_frame1,&fraction);
-
- Vector3 t0;
- PoseData[channel]->Get_Translation(t0,pividx,pose_frame0);
- Vector3 t1;
- PoseData[channel]->Get_Translation(t1,pividx,pose_frame1);
- Vector3::Lerp(t0,t1,fraction,&trans);
- }
- void HMorphAnimClass::Get_Orientation(Quaternion& q, int pividx,float frame) const
- {
- int channel = PivotChannel[pividx];
- int pose_frame0,pose_frame1;
- float fraction;
- MorphKeyData[channel].Get_Morph_Info(frame,&pose_frame0,&pose_frame1,&fraction);
-
- Quaternion q0;
- PoseData[channel]->Get_Orientation(q0,pividx,pose_frame0);
- Quaternion q1;
- PoseData[channel]->Get_Orientation(q1,pividx,pose_frame1);
- ::Fast_Slerp(q,q0,q1,fraction);
- }
- void HMorphAnimClass::Get_Transform(Matrix3D& mtx,int pividx,float frame) const
- {
- int channel = PivotChannel[pividx];
- int pose_frame0,pose_frame1;
- float fraction;
- MorphKeyData[channel].Get_Morph_Info(frame,&pose_frame0,&pose_frame1,&fraction);
- Quaternion q0;
- PoseData[channel]->Get_Orientation(q0,pividx,pose_frame0);
- Quaternion q1;
- PoseData[channel]->Get_Orientation(q1,pividx,pose_frame1);
- Quaternion q;
- ::Fast_Slerp(q,q0,q1,fraction);
- ::Build_Matrix3D(q,mtx);
- Vector3 t0;
- PoseData[channel]->Get_Translation(t0,pividx,pose_frame0);
- Vector3 t1;
- PoseData[channel]->Get_Translation(t1,pividx,pose_frame1);
- Vector3 trans;
- Vector3::Lerp(t0,t1,fraction,&trans);
- mtx.Set_Translation(trans);
- }
- void HMorphAnimClass::Insert_Morph_Key(const int channel, uint32 morph_frame, uint32 pose_frame)
- {
- assert(channel<ChannelCount);
- MorphKeyData[channel].Add_Key(morph_frame,pose_frame);
- // update the framecount to reflect the newly added key
- FrameCount = WWMath::Max(morph_frame,FrameCount);
- }
- void HMorphAnimClass::Release_Keys(void)
- {
- for(int i=0;i<ChannelCount;i++)
- MorphKeyData[i].Free();
- // update the framecount as 0
- FrameCount = 0;
- }
|