/* ** 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 . */ /* $Header: /Commando/Code/ww3d2/motchan.cpp 6 11/29/01 1:07p Jani_p $ */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando / G 3D Library * * * * $Archive:: /Commando/Code/ww3d2/motchan.cpp $* * * * Author:: Greg_h * * * * $Modtime:: 11/29/01 1:01p $* * * * $Revision:: 6 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * MotionChannelClass::MotionChannelClass -- constructor * * MotionChannelClass::~MotionChannelClass -- destructor * * MotionChannelClass::Free -- de-allocates all memory in use * * MotionChannelClass::Load -- loads a motion channel from a file * * BitChannelClass::BitChannelClass -- Constructor for BitChannelClass * * BitChannelClass::~BitChannelClass -- Destructor for BitChannelClass * * BitChannelClass::Free -- Free all "external" memory in use * * BitChannelClass::Load -- Read a bit channel from a w3d chunk * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "motchan.h" #include "w3d_file.h" #include "chunkio.h" #include "vector.h" #include "wwmath.h" #include "quat.h" #include "wwmath.h" //#include //#include // Static Table, for Adaptive Delta Decompressor #define FILTER_TABLE_SIZE (256) #define FILTER_TABLE_GEN_START (16) #define FILTER_TABLE_GEN_SIZE (FILTER_TABLE_SIZE - FILTER_TABLE_GEN_START) static float filtertable[FILTER_TABLE_SIZE] = { 0.00000001f, 0.0000001f, 0.000001f, 0.00001f, 0.0001f, 0.001f, 0.01f, 0.1f, 1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f, 100000.0f, 1000000.0f, 10000000.0f, }; static bool table_valid = false; /*********************************************************************************************** * MotionChannelClass::MotionChannelClass -- constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ MotionChannelClass::MotionChannelClass(void) : PivotIdx(0), Type(0), VectorLen(0), Data(NULL), FirstFrame(-1), LastFrame(-1), CompressedData(NULL), ValueScale(0.0f), ValueOffset(0.0f) { } /*********************************************************************************************** * MotionChannelClass::~MotionChannelClass -- destructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ MotionChannelClass::~MotionChannelClass(void) { Free(); } /*********************************************************************************************** * MotionChannelClass::Free -- de-allocates all memory in use * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ void MotionChannelClass::Free(void) { if (CompressedData) { delete[] CompressedData; CompressedData=NULL; } if (Data) { delete[] Data; Data = NULL; } } /*********************************************************************************************** * MotionChannelClass::Load -- loads a motion channel from a file * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ bool MotionChannelClass::Load_W3D(ChunkLoadClass & cload) { int size = cload.Cur_Chunk_Length(); // There was a bug in the exporter which saved too much data, so let's try and not load everything. unsigned int saved_datasize = (size - sizeof(W3dAnimChannelStruct)); W3dAnimChannelStruct chan; if (cload.Read(&chan,sizeof(W3dAnimChannelStruct)) != sizeof(W3dAnimChannelStruct)) { return false; } FirstFrame = chan.FirstFrame; LastFrame = chan.LastFrame; VectorLen = chan.VectorLen; Type = chan.Flags; PivotIdx = chan.Pivot; unsigned int num_floats = LastFrame-FirstFrame+1;//(datasize / sizeof(float32)) + 1; num_floats*=VectorLen; unsigned int datasize=(num_floats-1)*sizeof(float); Data = new float32[num_floats]; Data[0] = chan.Data[0]; if (cload.Read(&(Data[1]),datasize) != datasize) { Free(); return false; } // Skip over the extra data at the end of the chunk (saved by an error in the exporter) if (saved_datasize-datasize>0) { cload.Seek(saved_datasize-datasize); } Do_Data_Compression(datasize); return true; } /*********************************************************************************************** * BitChannelClass::BitChannelClass -- Constructor for BitChannelClass * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * *=============================================================================================*/ BitChannelClass::BitChannelClass(void) : PivotIdx(0), Type(0), DefaultVal(0), FirstFrame(-1), LastFrame(-1), Bits(NULL) { } /*********************************************************************************************** * BitChannelClass::~BitChannelClass -- Destructor for BitChannelClass * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/21/98 GTH : Created. * *=============================================================================================*/ BitChannelClass::~BitChannelClass(void) { Free(); } /*********************************************************************************************** * BitChannelClass::Free -- Free all "external" memory in use * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/21/98 GTH : Created. * *=============================================================================================*/ void BitChannelClass::Free(void) { if (Bits != NULL) { delete[] Bits; Bits = NULL; } } /*********************************************************************************************** * BitChannelClass::Load -- Read a bit channel from a w3d chunk * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/21/98 GTH : Created. * *=============================================================================================*/ bool BitChannelClass::Load_W3D(ChunkLoadClass & cload) { Free(); int chunk_size = cload.Cur_Chunk_Length(); W3dBitChannelStruct chan; if (cload.Read(&chan,sizeof(W3dBitChannelStruct)) != sizeof(W3dBitChannelStruct)) { return false; } FirstFrame = chan.FirstFrame; LastFrame = chan.LastFrame; Type = chan.Flags; PivotIdx = chan.Pivot; DefaultVal = chan.DefaultVal; uint32 numbits = LastFrame - FirstFrame + 1; uint32 numbytes = (numbits + 7) / 8; uint32 bytesleft = numbytes - 1; assert((sizeof(W3dBitChannelStruct) + bytesleft) == (unsigned)chunk_size); Bits = new uint8[numbytes]; assert(Bits); Bits[0] = chan.Data[0]; if (bytesleft > 0) { if (cload.Read(&(Bits[1]),bytesleft) != bytesleft) { Free(); return false; } } return true; } /*********************************************************************************************** * TimeCodedMotionChannelClass::TimeCodedMotionChannelClass -- constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ TimeCodedMotionChannelClass::TimeCodedMotionChannelClass(void) : PivotIdx(0), Type(0), VectorLen(0), PacketSize(0), Data(NULL), NumTimeCodes(0), LastTimeCodeIdx(0), // absolute index to last time code CachedIdx(0) // Last Index Used { } /*********************************************************************************************** * MotionChannelClass::~MotionChannelClass -- destructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ TimeCodedMotionChannelClass::~TimeCodedMotionChannelClass(void) { Free(); } /*********************************************************************************************** * TimeCodedMotionChannelClass::Free -- de-allocates all memory in use * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ void TimeCodedMotionChannelClass::Free(void) { if (Data) { delete[] Data; Data = NULL; } } /*********************************************************************************************** * TimeCodedMotionChannelClass::Load -- loads a motion channel from a file * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ bool TimeCodedMotionChannelClass::Load_W3D(ChunkLoadClass & cload) { int size = cload.Cur_Chunk_Length(); unsigned int datasize = size - sizeof(W3dTimeCodedAnimChannelStruct); unsigned int numInts = (datasize / sizeof(uint32)) + 1; W3dTimeCodedAnimChannelStruct chan; if (cload.Read(&chan,sizeof(W3dTimeCodedAnimChannelStruct)) != sizeof(W3dTimeCodedAnimChannelStruct)) { return false; } NumTimeCodes = chan.NumTimeCodes; VectorLen = chan.VectorLen; Type = chan.Flags; PivotIdx = chan.Pivot; PacketSize = VectorLen+1; CachedIdx = 0; LastTimeCodeIdx = (NumTimeCodes-1) * PacketSize; Data = new uint32[numInts]; Data[0] = chan.Data[0]; if (cload.Read(&(Data[1]), datasize) != datasize) { Free(); return false; } return true; } /*********************************************************************************************** * TimeCodedMotionChannelClass::Get_Vector -- returns the vector for the specified frame # * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ void TimeCodedMotionChannelClass::Get_Vector(float32 frame,float * setvec) { uint32 tc0; tc0 = frame; uint32 pidx = get_index( tc0 ); uint32 p2idx; if (pidx == ((NumTimeCodes - 1) * PacketSize)) { float32 *frm = (float32 *) &Data[pidx+1]; for (int i=0; i < VectorLen; i++) { setvec[i] = frm[i]; } return; } else { p2idx = pidx + PacketSize; } uint32 time = Data[p2idx]; if (time & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) { float32 *frm = (float32 *) &Data[pidx+1]; for (int i=0; i < VectorLen; i++) { setvec[i] = frm[i]; } return; } float32 time1 = (Data[pidx] & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); float32 time2 = (time & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); float32 ratio = (frame - time1) / (time2 - time1); float32 *frame1 = (float32 *) &Data[pidx+1]; float32 *frame2 = (float32 *) &Data[p2idx+1]; for (int i=0; i < VectorLen; i++) { setvec[i] = WWMath::Lerp(frame1[i],frame2[i],ratio); } } // Get_Vector Quaternion TimeCodedMotionChannelClass::Get_QuatVector(float32 frame) { assert(VectorLen == 4); Quaternion q(1); uint32 tc0; tc0 = frame; uint32 pidx = get_index( tc0 ); uint32 p2idx; if (pidx == ((NumTimeCodes - 1) * PacketSize)) { float32 *vec = (float32 *) &Data[pidx+1]; q.Set(vec[0], vec[1], vec[2], vec[3]); return( q ); } else { p2idx = pidx + PacketSize; } uint32 time = Data[p2idx]; if (time & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) { // its a binary movement! float32 *vec = (float32 *) &Data[pidx+1]; q.Set(vec[0], vec[1], vec[2], vec[3]); return( q ); } float32 time1 = (Data[pidx] & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); float32 time2 = (time & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); float32 ratio = (frame - time1) / (time2 - time1); float32 *frame1 = (float32 *) &Data[pidx+1]; float32 *frame2 = (float32 *) &Data[p2idx+1]; Quaternion q1(1); Quaternion q2(1); q1.Set(frame1[0], frame1[1], frame1[2], frame1[3]); q2.Set(frame2[0], frame2[1], frame2[2], frame2[3]); Fast_Slerp(q, q1, q2, ratio); return( q ); } // Get_QuatVector /*********************************************************************************************** * TimeCodedMotionChannelClass::binary_search_index / returns packet index * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 01/27/2000 JGA : Created. * *=============================================================================================*/ // New version that uses a binary search, and no cache uint32 TimeCodedMotionChannelClass::binary_search_index(uint32 timecode) { int leftIdx = 0; int rightIdx = NumTimeCodes - 2; int dx; uint32 time; int idx = LastTimeCodeIdx; //((rightIdx+1) * PacketSize;) //int32 LastTimeCodeIdx; // absolute index to last time code //int32 CachedIdx; // Last Index Used // special case last packet time = Data[idx] & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG; if (timecode >= time) return(idx); for (;;) { dx = rightIdx - leftIdx; dx>>=1; // divide by 2 dx += leftIdx; idx = dx * PacketSize; time = Data[idx] & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG; if (timecode < time) { rightIdx = dx; continue; } time = Data[idx + PacketSize] & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG; if (timecode < time) return(idx); if (leftIdx ^ dx) { leftIdx = dx; continue; } // // if leftIdx == dx prior to assignment, then leftIdx is stuck. // leftIdx++; } assert(0); return(0); } // binary_search_index /*********************************************************************************************** * TimeCodedMotionChannelClass::get_index / returns packet index * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 01/27/2000 JGA : Created. * *=============================================================================================*/ uint32 TimeCodedMotionChannelClass::get_index(uint32 timecode) { assert(CachedIdx <= LastTimeCodeIdx); uint32 time; time = Data[CachedIdx] & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG; if (timecode >= time) { // possibly in the current packet // special case for end packets if (CachedIdx == LastTimeCodeIdx) return(CachedIdx); time = Data[CachedIdx + PacketSize] & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG; if (timecode < time) return(CachedIdx); // Do one time look-ahead before reverting to a search CachedIdx+=PacketSize; if (CachedIdx == LastTimeCodeIdx) return(CachedIdx); time = Data[CachedIdx + PacketSize] & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG; if (timecode < time) return(CachedIdx); } CachedIdx = binary_search_index( timecode ); return(CachedIdx); } // get_index /*********************************************************************************************** * TimeCodedMotionChannelClass::set_identity -- returns an "identity" vector (not really...hmm...) * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 08/11/1997 GH : Created. * *=============================================================================================*/ void TimeCodedMotionChannelClass::set_identity(float * setvec) { if (Type == ANIM_CHANNEL_Q) { setvec[0] = 0.0f; setvec[1] = 0.0f; setvec[2] = 0.0f; setvec[3] = 1.0f; } else { setvec[0] = 0.0f; } } // set_identity /*********************************************************************************************** * TimeCodedBitChannelClass::TimeCodedBitChannelClass -- Constructor for BitChannelClass * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * *=============================================================================================*/ TimeCodedBitChannelClass::TimeCodedBitChannelClass(void) : PivotIdx(0), Type(0), DefaultVal(0), Bits(NULL), CachedIdx(0) { } /*********************************************************************************************** * TimeCodedBitChannelClass::~TimeCodedBitChannelClass -- Destructor for BitChannelClass * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/21/98 GTH : Created. * *=============================================================================================*/ TimeCodedBitChannelClass::~TimeCodedBitChannelClass(void) { Free(); } /*********************************************************************************************** * TimeCodedBitChannelClass::Free -- Free all "external" memory in use * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/21/98 GTH : Created. * *=============================================================================================*/ void TimeCodedBitChannelClass::Free(void) { if (Bits != NULL) { delete[] Bits; Bits = NULL; } } /*********************************************************************************************** * TimeCodedBitChannelClass::Load -- Read a bit channel from a w3d chunk * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/21/98 GTH : Created. * *=============================================================================================*/ bool TimeCodedBitChannelClass::Load_W3D(ChunkLoadClass & cload) { Free(); int chunk_size = cload.Cur_Chunk_Length(); W3dTimeCodedBitChannelStruct chan; if (cload.Read(&chan,sizeof(W3dTimeCodedBitChannelStruct)) != sizeof(W3dTimeCodedBitChannelStruct)) { return false; } NumTimeCodes = chan.NumTimeCodes; Type = chan.Flags; PivotIdx = chan.Pivot; DefaultVal = chan.DefaultVal; CachedIdx = 0; uint32 bytesleft = (NumTimeCodes - 1) * sizeof(uint32); assert((sizeof(W3dTimeCodedBitChannelStruct) + bytesleft) == (unsigned)chunk_size); Bits = new uint32[NumTimeCodes]; assert(Bits); Bits[0] = chan.Data[0]; if (bytesleft > 0) { if (cload.Read(&(Bits[1]),bytesleft) != bytesleft) { Free(); return false; } } return true; } // Load_W3D /*********************************************************************************************** * TimeCodedBitChannelClass::Get_Bit -- Lookup a bit in the bit channel * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/21/98 GTH : Created. * *=============================================================================================*/ int TimeCodedBitChannelClass::Get_Bit(int frame) { assert(frame >= 0); assert(CachedIdx < NumTimeCodes); int time; int idx=0; time = Bits[CachedIdx] & ~W3D_TIMECODED_BIT_MASK; if (frame >= time) { // start from here idx = CachedIdx+1; } for (;idx < (int) NumTimeCodes ; idx++) { time = Bits[idx] &~W3D_TIMECODED_BIT_MASK; if (frame < time) break; } idx--; if (idx < 0) idx = 0; CachedIdx = idx; return (((Bits[idx] & W3D_TIMECODED_BIT_MASK) == W3D_TIMECODED_BIT_MASK)); } // Get_Bit // Begin Adaptive Delta /*********************************************************************************************** * AdaptiveDeltaMotionChannelClass::AdaptiveDeltaMotionChannelClass -- constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 02/18/2000 JGA : Created. * *=============================================================================================*/ AdaptiveDeltaMotionChannelClass::AdaptiveDeltaMotionChannelClass(void) : PivotIdx(0), Type(0), VectorLen(0), Data(NULL), NumFrames(0), CacheData(NULL), Scale(0.0f) { if (false == table_valid) { // Create Filter Table, used in delta compression for (int i=0; i>1; // create packet index int factor; // factor, contains extracted nybble -8 to +7 if (fi & 1) { factor = pPacket[pi]; factor>>=4; } else { factor = pPacket[pi]; } // Sign Extend factor &=0xF; if (factor & 0x8) { factor |= 0xFFFFFFF0; } // Convert to Floating Point float ffactor = factor; float delta = ffactor * filter; last_value+=delta; if (frame == frame_idx) { done = true; break; } frame++; } // for fi < 16 if (done) break; // we're at the desired frame pPacket+= ((PACKET_SIZE * VectorLen) - 1); // skip to next packet } // for frame_idx outdata[vi] = last_value; } // for vi=0; vi < 4 } // decompress, from beginning void AdaptiveDeltaMotionChannelClass::decompress(uint32 src_idx, float *srcdata, uint32 frame_idx, float *outdata) { // Contine decompressing from src_idx, up to frame_idx assert(src_idx < frame_idx); src_idx++; float *base = (float *) &Data[0]; // pointer to our true know beginning values base += VectorLen; // skip header information bool done = false; for(int vi=0; vi>4); // skip out to current packet // initial filter index int fi = (src_idx-1) & 0xF; float last_value = srcdata[vi]; for (uint32 frame=src_idx; frame<=frame_idx;) { // Frame Loop uint32 filter_index = *pPacket; // packet header pPacket++; // skip to nybble compressed data float filter = filtertable[filter_index] * Scale; // decompression filter // data is grouped in sets of 16 nybbles for (fi; fi < 16; fi++) { int pi = fi>>1; // create packet index int factor; // factor, contains extracted nybble -8 to +7 if (fi & 1) { factor = pPacket[pi]; factor>>=4; } else { factor = pPacket[pi]; } // Sign Extend factor &=0xF; if (factor & 0x8) { factor |= 0xFFFFFFF0; } // Convert to Floating Point float ffactor = factor; float delta = ffactor * filter; last_value+=delta; if (frame == frame_idx) { done = true; break; } frame++; } // for fi < 16 fi = 0; if (done) break; // we're at the desired frame pPacket+= ((PACKET_SIZE * VectorLen) - 1); // skip to next packet } // for frame_idx outdata[vi] = last_value; } // for vi=0; vi < 4 } // decompress, from continuation /*********************************************************************************************** * AdaptiveDeltaMotionChannelClass::getframe returns decompressed data for frame/vectorindex * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 02/18/2000 JGA : Created. * *=============================================================================================*/ float AdaptiveDeltaMotionChannelClass::getframe(uint32 frame_idx, uint32 vector_idx) { // Make sure frame_idx is valid if (frame_idx >= NumFrames) frame_idx = NumFrames - 1; // Check to see if the data is already in cache? if (CacheFrame == frame_idx) { return(CacheData[vector_idx]); } if ((CacheFrame+1) == frame_idx) { return(CacheData[vector_idx + VectorLen]); } if (frame_idx < CacheFrame) { // Requested Frame isn't cached, so cache it, and frame_idx+1, and return the decompressed data // from frame_idx decompress(frame_idx, &CacheData[0]); if (frame_idx != (NumFrames - 1)) { decompress(frame_idx, &CacheData[0], frame_idx+1, &CacheData[VectorLen]); } CacheFrame = frame_idx; return(CacheData[vector_idx]); } // Copy last known Cached data down if (frame_idx == (CacheFrame + 2)) { // Sliding window memcpy(&CacheData[0], &CacheData[VectorLen], VectorLen * sizeof(float)); CacheFrame++; decompress(CacheFrame, &CacheData[0], frame_idx, &CacheData[VectorLen]); return(CacheData[VectorLen + vector_idx]); } // Else just use last known frame to decompress forwards assert(VectorLen <= 4); float temp[4]; memcpy(&temp[0], &CacheData[VectorLen], VectorLen * sizeof(float)); decompress(CacheFrame, &temp[0], frame_idx, &CacheData[0]); CacheFrame = frame_idx; if (frame_idx != (NumFrames - 1)) { decompress(CacheFrame, &CacheData[0], frame_idx+1, &CacheData[VectorLen]); } return(CacheData[vector_idx]); } // getframe /*********************************************************************************************** * AdaptiveDeltaMotionChannelClass::Get_Vector -- returns the vector for the specified frame # * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 02/18/2000 JGA : Created. * *=============================================================================================*/ void AdaptiveDeltaMotionChannelClass::Get_Vector(float32 frame,float * setvec) { uint32 frame1 = frame; float ratio = frame - frame1; float value1 = getframe(frame1); float value2 = getframe(frame1 + 1); *setvec = WWMath::Lerp(value1,value2,ratio); } // Get_Vector // // Special Case Quats, so we can use Slerp // Quaternion AdaptiveDeltaMotionChannelClass::Get_QuatVector(float32 frame) { uint32 frame1 = frame; uint32 frame2 = frame1+1; float ratio = frame - frame1; Quaternion q1(1); q1.Set( getframe(frame1, 0), getframe(frame1, 1), getframe(frame1, 2), getframe(frame1, 3) ); Quaternion q2(1); q2.Set( getframe(frame2, 0), getframe(frame2, 1), getframe(frame2, 2), getframe(frame2, 3) ); Quaternion q(1); Fast_Slerp(q, q1, q2, ratio); return( q ); } // Get_QuatVector //========================================================================================== void MotionChannelClass:: Do_Data_Compression(int datasize) { return; //Find Min_Max float value_min=FLT_MAX; float value_max=-FLT_MAX; int count=datasize/sizeof(float); for (int i=0;i100000.0f) value=0.0f; if (value<-100000.0f) value=0.0f; Data[i]=value; if (value_min > value) value_min = value; if (value_max < value) value_max = value; } ValueOffset=value_min; ValueScale=value_max-value_min; // Can't compress if the range is too high if (ValueScale>2000.0f) return; if (Type==ANIM_CHANNEL_Q/* && ValueScale>3.0f*/) return; WWASSERT(!CompressedData); CompressedData=new unsigned short[count]; float inv_scale=0.0f; if (ValueScale!=0.0f) { inv_scale=1.0f/ValueScale; } inv_scale*=65535.0f; for (i=0;iValueScale/65536.0f) { // int ii=0; // } } delete[] Data; Data=NULL; } //========================================================================================== void MotionChannelClass:: Get_Vector(int frame,float * setvec) const{ if ((frame < FirstFrame) || (frame > LastFrame)) { set_identity(setvec); }else { int vframe = frame - FirstFrame; if (Data) { for (int i=0; i