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/ww3d2/motchan.cpp

1350 lines
55 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/>.
*/
/* $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 <stdio.h>
//#include <Windows.h>
// 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<FILTER_TABLE_GEN_SIZE; i++)
{
float ratio = i;
//ratio = ((ratio + 1.0f) / 128.0f);
ratio/=((float) FILTER_TABLE_GEN_SIZE);
filtertable[i + FILTER_TABLE_GEN_START] = 1.0f - WWMath::Sin( DEG_TO_RAD(90.0f * ratio));
}
table_valid = true;
}
}
/***********************************************************************************************
* MotionChannelClass::~MotionChannelClass -- destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/18/1000 JGA : Created. *
*=============================================================================================*/
AdaptiveDeltaMotionChannelClass::~AdaptiveDeltaMotionChannelClass(void)
{
Free();
}
/***********************************************************************************************
* AdaptiveDeltaMotionChannelClass::Free -- de-allocates all memory in use *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/18/2000 JGA : Created. *
*=============================================================================================*/
void AdaptiveDeltaMotionChannelClass::Free(void)
{
if (Data) {
delete[] Data;
Data = NULL;
}
if (CacheData) {
delete CacheData;
CacheData = NULL;
}
} // Free
/***********************************************************************************************
* AdaptiveDeltaMotionChannelClass::Load -- loads a motion channel from a file *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/18/2000 JGA : Created. *
*=============================================================================================*/
bool AdaptiveDeltaMotionChannelClass::Load_W3D(ChunkLoadClass & cload)
{
int size = cload.Cur_Chunk_Length();
unsigned int datasize = size - sizeof(W3dAdaptiveDeltaAnimChannelStruct);
unsigned int numInts = (datasize / sizeof(uint32)) + 1;
W3dAdaptiveDeltaAnimChannelStruct chan;
if (cload.Read(&chan,sizeof(W3dAdaptiveDeltaAnimChannelStruct)) != sizeof(W3dAdaptiveDeltaAnimChannelStruct)) {
return false;
}
VectorLen = chan.VectorLen;
Type = chan.Flags;
PivotIdx = chan.Pivot;
NumFrames = chan.NumFrames;
Scale = chan.Scale;
CacheFrame = 0x7FFFFFFF; // a big number, so we know its not valid
CacheData = new float[VectorLen * 2]; // cacheframe & cachedframe+1 by VectorLen
Data = new uint32[numInts];
Data[0] = chan.Data[0];
if (cload.Read(&(Data[1]), datasize) != datasize) {
Free();
return false;
}
return true;
} // Load_W3D
/***********************************************************************************************
* AdaptiveDeltaMotionChannelClass::decompress *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/23/2000 JGA : Created. *
*=============================================================================================*/
#define PACKET_SIZE (9)
void AdaptiveDeltaMotionChannelClass::decompress(uint32 frame_idx, float *outdata)
{
// Start Over from the beginning
float *base = (float *) &Data[0]; // pointer to our true know beginning values
bool done = false;
for(int vi=0; vi<VectorLen; vi++) {
// Decompress all the vector indices, since they will probably all be needed
unsigned char *pPacket = (unsigned char *) Data; // pointer to current packet
pPacket+= (sizeof(float) * VectorLen); // skip non-compressed header information
pPacket+= PACKET_SIZE * vi; // skip to the appropriate packet start
float last_value = base[vi];
for (uint32 frame=1; 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 (int fi=0; 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
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<VectorLen; vi++) {
// Decompress all the vector indices, since they will probably all be needed
unsigned char *pPacket = (unsigned char *) base; // pointer to current packet
pPacket+= PACKET_SIZE * vi; // skip to the appropriate packet start
pPacket+= (PACKET_SIZE * VectorLen) * ((src_idx-1)>>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;i<count;i++) {
float value=Data[i];
if (_isnan(value)) value=0.0f;
if (value>100000.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;i<count;++i) {
float value=Data[i];
value-=ValueOffset;
value*=inv_scale;
int ivalue=WWMath::Float_To_Int_Floor(value);
CompressedData[i]=unsigned short(ivalue);
float new_scale=ValueScale/65535.0f;
float new_value=int(CompressedData[i]);
float new_float = new_value*new_scale+ValueOffset;
// if (fabs(new_float-Data[i])>ValueScale/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<VectorLen; i++) {
setvec[i] = Data[vframe * VectorLen + i];
}
}
else {
WWASSERT(CompressedData);
float scale=ValueScale/65535.0f;
for (int i=0; i<VectorLen; i++) {
float value=int(CompressedData[vframe * VectorLen + i]);
setvec[i] = value*scale+ValueOffset;
}
}
}
}
// EOF - motchan.cpp