/*
** 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 .
*/
/* $Header: /Commando/Code/Tools/max2w3d/vchannel.cpp 10 10/30/00 6:56p Greg_h $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando Tools - W3D export *
* *
* $Archive:: /Commando/Code/Tools/max2w3d/vchannel.cpp $*
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 10/30/00 5:26p $*
* *
* $Revision:: 10 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "vchannel.h"
#include
#include
#include
#include "w3d_file.h"
#include "w3dquat.h"
#include "bchannel.h"
#include "exportlog.h"
#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;
VectorChannelClass::VectorChannelClass
(
uint32 id,
int maxframes,
uint32 flags,
int vectorlength,
float32 * identvect
) :
ID(id),
Flags(flags),
MaxFrames(maxframes),
VectorLen(vectorlength),
IsEmpty(true),
IdentVect(NULL),
Data(NULL),
Begin(0),
End(0),
ReduceAnimation(false),
ReduceAnimationPercent(0),
CompressAnimation(false),
CompressAnimationFlavor(0),
CompressAnimationTranslationError(0.0f),
CompressAnimationRotationError(0.0f)
{
assert(VectorLen > 0);
IdentVect = new float32[VectorLen];
Data = new float32[MaxFrames * VectorLen];
assert(Data);
assert(IdentVect);
memcpy(IdentVect,identvect,VectorLen * sizeof(float32));
memset(Data,0,MaxFrames * VectorLen * sizeof(float32));
// start "Begin" at the end of the array, whenever we set a value
// at an index less than "Begin", we push "Begin" back.
Begin = MaxFrames;
End = 0;
if (false == table_valid) {
// Create Filter Table, used in delta compression
for (int i=0; i= 0);
assert(frameidx < MaxFrames);
for (int vi=0; vi= 0);
assert(frameidx < MaxFrames);
return &(Data[frameidx * VectorLen]);
}
bool VectorChannelClass::SaveTimeCoded(ChunkSaveClass & csave, BitChannelClass *binmov)
{
uint32 channelsize = sizeof(W3dTimeCodedAnimChannelStruct);
uint32 packetsize = (VectorLen * sizeof(float32)) + sizeof(uint32);
channelsize += packetsize * MaxFrames;
channelsize -= sizeof(uint32);
W3dTimeCodedAnimChannelStruct * chn = (W3dTimeCodedAnimChannelStruct *)malloc(channelsize);
if (chn == NULL) {
return false;
}
chn->NumTimeCodes = MaxFrames;
chn->VectorLen = VectorLen;
chn->Pivot = ID;
chn->Flags = Flags;
// Fetch Channel Data into new format
// tc [data] tc [data] tc [data] .. ...
uint32 fidx = 0;
for (int fcount=0; fcount < MaxFrames; fcount++, fidx += (VectorLen+1) ) {
uint32 * pivec;
float32 * pfvec;
pivec = &chn->Data[ fidx ];
pfvec = (float32 *) (pivec + 1);
*pivec = fcount;
if (binmov) {
bool binary_move = binmov->Get_Bit( fcount );
// check for false binary movement
if (fcount != Begin) {
if (binary_move) {
*pivec |= W3D_TIMECODED_BINARY_MOVEMENT_FLAG;
}
}
else {
//if (log) log->printf("\nFALSE Binary\n");
}
}
// Copy Vector
for (int vidx=0; vidx < VectorLen; vidx++) {
pfvec[vidx] = get_value(fcount,vidx);
}
}
// Compress the new structure
VectorChannelClass::compress( chn );
// update the size
float original_channelsize = channelsize;
channelsize = sizeof(W3dTimeCodedAnimChannelStruct);
channelsize += packetsize * chn->NumTimeCodes;
channelsize -= sizeof(uint32);
float percent = (((float)channelsize) / original_channelsize) * 100.0f;
// save
ExportLog::printf("%.0f", percent);
if (csave.Write(chn,channelsize) != channelsize) {
return false;
}
if (chn != NULL) {
free(chn);
}
if (!csave.End_Chunk()) {
return false;
}
return true;
} // SaveTimeCoded
/*
struct W3dAdaptiveDeltaAnimChannelStruct
{
uint32 NumFrames; // number of frames of animation
uint16 Pivot; // pivot effected by this channel
uint8 VectorLen; // num Channels
uint8 Flags; // channel type
float Scale; // Filter Table Scale
uint32 Data[1]; // OpCode Data Stream
};
*/
struct
{
unsigned char filter : 7;
unsigned char flag : 1;
unsigned char d0 : 4;
unsigned char d1 : 4;
unsigned char d2 : 4;
unsigned char d3 : 4;
unsigned char d4 : 4;
unsigned char d5 : 4;
unsigned char d6 : 4;
unsigned char d7 : 4;
unsigned char d8 : 4;
unsigned char d9 : 4;
unsigned char d10 : 4;
unsigned char d11 : 4;
unsigned char d12 : 4;
unsigned char d13 : 4;
unsigned char d14 : 4;
unsigned char d15 : 4;
} AdaptiveDeltaPacketStruct;
//
// test compress Adaptive Delta packet
//
// inputs: filter - forced filter to use
// scale (filter table scale)
// value1 (continue compression from this initial value)
// float *indata // 16 values to compress
// float *outdata // 16 decompressed values
//
// output: float error; // aggregate error for packet
//
float VectorChannelClass::test_compress(int filter_index, float scale, float value1, float *indata, float *outdata)
{
float error = 0.0f;
// compute filter
float filter = filtertable[filter_index] * scale;
assert(filter_index < FILTER_TABLE_SIZE);
float last_value = value1;
for(int data_idx=0; data_idx < 16; data_idx++) {
// NOTE: DETERMINE best factor via Brute Force
// This helps under/over-flow problems
// brute
int best_factor = 100;
float least_error = 999999999.9f;
for (float try_factor = -8.0f; try_factor < 8.0f; try_factor+=1.0f) {
float temp = (try_factor * filter) + last_value; // decompress using this filter
temp-=indata[data_idx]; // delta decompressed value, vs original value
temp=fabs(temp);
if (temp < least_error) {
least_error = temp;
best_factor = try_factor;
}
}
assert(best_factor <= 7);
assert(best_factor >=-8);
float dfactor = best_factor;
outdata[data_idx] = (dfactor * filter) + last_value;
// END BRUTE FORCE IT
float delta = outdata[data_idx] - indata[data_idx];
//if (delta > error) error = delta;
error+= (delta * delta);
last_value = outdata[data_idx];
}
return( sqrt(error) );
} // test_compress
//
// compress Adaptive Delta packet
//
// inputs: filter - forced filter to use
// scale (filter table scale)
// value1 (continue compression from this initial value)
// float *indata // 16 values to compress
// unsigned char *pPacket
//
// output: float error; // aggregate error for packet
//
float VectorChannelClass::compress(int filter_index, float scale, float value1, float *indata, unsigned char *pPacket, float *outdata)
{
float error = 0.0f;
// compute filter
float filter = filtertable[filter_index] * scale;
assert(filter_index < FILTER_TABLE_SIZE);
*pPacket = filter_index;
pPacket++;
float last_value = value1;
for(int data_idx=0; data_idx < 16; data_idx++) {
// NOTE: PLAN TO ADD A LOOP IN HERE, to DETERMINE best factor via Brute Force
// This could help under/over-flow problems
{
// brute
int best_factor = 100;
float least_error = 999999999.9f;
for (float try_factor = -8.0f; try_factor < 8.0f; try_factor+=1.0f) {
float temp = (try_factor * filter) + last_value; // decompress using this filter
temp-=indata[data_idx]; // delta decompressed value, vs original value
temp=fabs(temp);
if (temp < least_error) {
least_error = temp;
best_factor = try_factor;
}
}
assert(best_factor <= 7);
assert(best_factor >=-8);
float dfactor = best_factor;
outdata[data_idx] = (dfactor * filter) + last_value;
int pi = data_idx>>1;
if (data_idx & 1) {
best_factor<<=4;
pPacket[pi]&=0x0f;
pPacket[pi]|=best_factor;
}
else {
best_factor&=0xf;
pPacket[pi]&=0xf0;
pPacket[pi]|=best_factor;
}
}
// END BRUTE FORCE IT
error+=fabs(outdata[data_idx] - indata[data_idx]);
last_value = outdata[data_idx];
}
return( error );
} // compress
bool VectorChannelClass::SaveAdaptiveDelta(ChunkSaveClass & csave, BitChannelClass *binmov)
{
uint32 channelsize = sizeof(W3dAdaptiveDeltaAnimChannelStruct);
int packetsize = sizeof(AdaptiveDeltaPacketStruct);
int numpackets = (MaxFrames + 15) / 16;
channelsize += packetsize * numpackets * VectorLen;
channelsize -= sizeof(char);
channelsize += VectorLen * sizeof(float);
W3dAdaptiveDeltaAnimChannelStruct * chn = (W3dAdaptiveDeltaAnimChannelStruct *)malloc(channelsize);
if (chn == NULL) {
return false;
}
// Brute Force Compressor
// Initialize Chan Data
chn->NumFrames = MaxFrames;
chn->Pivot = ID;
chn->VectorLen = VectorLen;
chn->Flags = Flags;
chn->Scale = 0.0f;
memset(&chn->Data[0], channelsize - (sizeof(W3dAdaptiveDeltaAnimChannelStruct) - sizeof(char)), 0x00);
assert(VectorLen <= 4); // otherwise temp vector won't have room
float *initial = (float *)&chn->Data[0];
float work[4];
// Fetch initial value
for (int i=0; i < VectorLen; i++) {
work[i] = initial[i] = get_value(0, i);
}
float original_packet[16];
float decompressed_packet[16];
float scale = 0.0f;
float last_value=0.0f;
int frame = 1;
float delta;
// Compute Scale
for (int fidx=1; fidx < MaxFrames; fidx++) {
for (i=0; i scale) scale = delta;
}
}
scale/=7.0f; // range of the delta encode is +- 7 units, in which ever filter range
// the smaller the scale, the better our precision is going to be
// End Compute Scale
for (i=0; i < numpackets; i++) {
for (int vi=0; viprintf("\nvi= %d frames %d to %d : error = %f ",vi, frame, frame+15, least_error);
// Encode current packet
unsigned char * pPacket;
pPacket = (unsigned char *) &chn->Data[0]; // beginning of data struct
pPacket+= (VectorLen * sizeof(float)); // skip over initial values
pPacket+= (sizeof(AdaptiveDeltaPacketStruct) * VectorLen * ((frame-1)>>4)); // skip up to the appropriate packet
pPacket+= sizeof(AdaptiveDeltaPacketStruct) * vi; // skip up the appropriate vector index
compress(best_filter, scale, last_value, original_packet, pPacket, decompressed_packet);
// update work[vi];
work[vi] = decompressed_packet[15];
} // for vi
frame+=16;
} // for numpackets
// print how big we are vs non-compressed
float rawsize = sizeof(W3dAnimChannelStruct);
rawsize += (VectorLen * sizeof(float32) * (MaxFrames)) - sizeof(float32);
float percent = ((float)channelsize) / rawsize;
percent*=100.0f;
ExportLog::printf("%.0f", percent);
// update final scale
chn->Scale = scale;
if (csave.Write(chn,channelsize) != channelsize) {
return false;
}
if (chn != NULL) {
free(chn);
}
if (!csave.End_Chunk()) {
return false;
}
return true;
} // SaveAdaptiveDelta
bool VectorChannelClass::Save(ChunkSaveClass & csave, BitChannelClass *binmov)
{
if (IsEmpty) return true;
compute_range();
if (EndFirstFrame = Begin;
chn->LastFrame = End;
chn->VectorLen = VectorLen;
chn->Pivot = ID;
chn->Flags = Flags;
for (int fcount=0; fcount < End-Begin+1; fcount++) {
for (int vidx=0; vidx < VectorLen; vidx++) {
int writeidx = fcount * VectorLen + vidx;
chn->Data[writeidx] = get_value(Begin + fcount,vidx);
}
}
if (csave.Write(chn,channelsize) != channelsize) {
return false;
}
if (chn != NULL) {
free(chn);
}
if (!csave.End_Chunk()) {
return false;
}
}
return true;
}
void VectorChannelClass::SetSaveOptions(bool compress, int flavor, float Terr, float Rerr, bool reduce, int reduce_percent)
{
ReduceAnimation = reduce;
ReduceAnimationPercent = reduce_percent;
CompressAnimation = compress;
CompressAnimationFlavor = flavor;
CompressAnimationTranslationError = Terr;
CompressAnimationRotationError = DEG_TO_RAD(Rerr);
} // SetSaveOptions
//
// Set data in motion channel to identity vector
// R2 - set invisible data to repeat last known position, the previous algorthm caused problems with
// the current movie assets
//
void VectorChannelClass::ClearInvisibleData(BitChannelClass *vis)
{
float *tvec;
assert(VectorLen <= 8);
bool prev_state = vis->Get_Bit( 0 );
tvec = Get_Vector( 0 );
for (int idx=0; idx < MaxFrames; idx++) {
bool cur_state = vis->Get_Bit( idx );
if (cur_state != prev_state) {
prev_state = cur_state;
tvec = Get_Vector( idx );
}
if (false == cur_state) {
//Set_Vector( idx, IdentVect );
Set_Vector( idx, tvec );
}
}
} // ClearInvisibleData
void VectorChannelClass::set_value(int framenum,int vindex,float32 val)
{
assert(framenum >= 0);
assert(framenum < MaxFrames);
assert(vindex >= 0);
assert(vindex < VectorLen);
Data[framenum * VectorLen + vindex] = val;
}
float32 VectorChannelClass::get_value(int framenum,int vindex)
{
assert(framenum >= 0);
assert(framenum < MaxFrames);
assert(vindex >= 0);
assert(vindex < VectorLen);
return Data[framenum * VectorLen + vindex];
}
bool VectorChannelClass::is_identity(float32 * vec)
{
const double ERROR_TOLERANCE = 0.00005 * 0.00005;
double dist = 0.0;
for (int vi=0; vi= 0) && (is_identity(Get_Vector(End)))) {
End--;
}
} // compute_range
//
// Remove a packet from a W3dTimeCodedAnimChanelStruct
//
void VectorChannelClass::remove_packet(W3dTimeCodedAnimChannelStruct * c, uint32 packet_idx)
{
assert( c );
assert( c->NumTimeCodes > 1 );
uint32 packet_size = c->VectorLen + 1;
uint32 packet_len = packet_size * sizeof(uint32);
uint32 *src, *dst;
dst = (uint32 *) &c->Data[ packet_size * packet_idx ];
src = (uint32 *) &c->Data[ packet_size * (packet_idx + 1) ];
uint32 copy_length = (c->NumTimeCodes - (packet_idx + 1)) * packet_len;
if (copy_length) {
memcpy(dst, src, copy_length);
}
// Decrement Packet Count
c->NumTimeCodes--;
} // remove_packet
//
// Take a non-compressed TimeCoded Motion Channel
// and compress the packets
//
void VectorChannelClass::compress(W3dTimeCodedAnimChannelStruct * c)
{
assert( c );
// Standard Error Threshold Compression
double Terr = CompressAnimationTranslationError;
double Rerr = CompressAnimationRotationError;
float TimeCodes_ct = c->NumTimeCodes;
switch( c->Flags )
{
case ANIM_CHANNEL_X:
case ANIM_CHANNEL_Y:
case ANIM_CHANNEL_Z: {
while(1) {
uint32 idx = find_useless_packet( c, Terr );
if (PACKETS_ALL_USEFUL == idx) break;
remove_packet( c, idx );
}
break;
}
case ANIM_CHANNEL_XR:
case ANIM_CHANNEL_YR:
case ANIM_CHANNEL_ZR: {
while(1) {
uint32 idx = find_useless_packet( c, Rerr );
if (PACKETS_ALL_USEFUL == idx) break;
remove_packet( c, idx );
}
break;
}
case ANIM_CHANNEL_Q: {
while(1) {
uint32 idx = find_useless_packetQ( c, Rerr );
if (PACKETS_ALL_USEFUL == idx) break;
remove_packet( c, idx );
}
break;
}
default: // undefined channel
assert(0);
break;
}
// Forced Reduction Phase
if (ReduceAnimation) {
if (ReduceAnimationPercent) {
float pct = ReduceAnimationPercent;
pct *= (0.01f);
pct = 1.0f - pct;
// if out of range, don't even try
if (pct <= 0.0f) return;
if (pct >= 1.0f) return;
pct*=TimeCodes_ct;
pct+=0.5f;
uint32 maxFrames = pct;
if (maxFrames < 2) maxFrames = 2;
if (maxFrames >= c->NumTimeCodes) return; // desired minimum already attained
switch( c->Flags )
{
case ANIM_CHANNEL_X:
case ANIM_CHANNEL_Y:
case ANIM_CHANNEL_Z:
case ANIM_CHANNEL_XR:
case ANIM_CHANNEL_YR:
case ANIM_CHANNEL_ZR: {
while(maxFrames < c->NumTimeCodes) {
uint32 idx = find_least_useful_packet( c );
if (PACKETS_ALL_USEFUL == idx) break;
remove_packet( c, idx );
}
break;
}
case ANIM_CHANNEL_Q: {
while(maxFrames < c->NumTimeCodes) {
uint32 idx = find_least_useful_packetQ( c );
if (PACKETS_ALL_USEFUL == idx) break;
remove_packet( c, idx );
}
break;
}
default: // undefined channel
assert(0);
break;
}
} // if ReducePercent
} // if Reduce
} // compress
//
// find a packet that isn't needed, and return the index
// if all packets are necessary, then return back PACKETS_ALL_USEFUL
// a useless packet is defined, as a packet that can be recreated
// via interpolation
//
// Make Sure we NEVER get rid of binary movement packets
// The rule is, you can't interpolate TOO a binary movement, but you can
// interpolate FROM
//#define W3D_TIMECODED_BINARY_MOVEMENT_FLAG 0x80000000
//
uint32 VectorChannelClass::find_useless_packet(W3dTimeCodedAnimChannelStruct * c, double tolerance)
{
#define MAX_VECTOR_SIZE 8
static float32 tempvec[MAX_VECTOR_SIZE];
assert( c ); // make sure pointer exists
assert( c->NumTimeCodes ); // make sure some packets exist
assert( c->VectorLen <= MAX_VECTOR_SIZE );
uint32 packet_size = c->VectorLen + 1;
if (c->NumTimeCodes > 1) {
if (c->NumTimeCodes > 2) {
float32 *pVecSrc, *pVecDst, *pVecOriginal;
uint32 *pTcSrc, *pTcDst, *pTcOriginal;
for(uint32 try_idx = 0; try_idx < (c->NumTimeCodes - 2); try_idx++) {
// Src Pointers
pTcSrc = (uint32 *) &c->Data[ try_idx * packet_size ];
pVecSrc = (float32 *) pTcSrc+1;
// Original Vector we're trying to recreate
pTcOriginal = (uint32 *) &c->Data[ (try_idx + 1) * packet_size ];
pVecOriginal = (float32 *) pTcOriginal+1;
// Dst Pointers
pTcDst = (uint32 *) &c->Data[ (try_idx + 2 ) * packet_size ];
pVecDst = (float32 *) pTcDst+1;
// Skip automagically, if binary movement involved
if (*pTcOriginal & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
continue; // can't get rid of this guy
}
if (*pTcDst & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
continue; // can't get rid of this guy either
}
// Linear Interpolate between Src, and Dst, to recreate the
// Original
float32 tStart = ((*pTcSrc) & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); // upgrade to floats
float32 tRecreate = *pTcOriginal;
float32 tEnd = *pTcDst;
float32 tRatio = (tRecreate - tStart) / (tEnd - tStart);
for (uint32 idx=0; idx < c->VectorLen; idx++) {
tempvec[ idx ] = WWMath::Lerp(pVecSrc[idx], pVecDst[idx], tRatio);
}
// Compare Original to our re-creation
bool close_enough = true;
for (idx=0; idx < c->VectorLen; idx++) {
float32 delta;
delta = fabs(pVecOriginal[idx] - tempvec[idx]);
if (delta > tolerance) {
close_enough = false;
break;
}
}
// If our Recreation is very close to the original,
// then discard the original
if (true == close_enough) {
return (try_idx + 1);
}
// else continue
} // for
}
else {
// Special Case, when there are only 2 time codes
// Check to see if they are equal, value
// if so, then return the 2nd timecode as useless
float32 *pVecSrc = (float32 *) &c->Data[ 1 ];
float32 *pVecDst = (float32 *) &c->Data[ packet_size + 1 ];
bool identical = true;
if ((c->Data[ packet_size ] & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) == 0) {
for(uint32 idx=0; idx < c->VectorLen; idx++) {
float32 delta;
delta = fabs(pVecDst[idx] - pVecSrc[idx]);
if (delta > tolerance) {
identical = false;
break;
}
}
if (identical) return( 1 );
}
}
}
return( PACKETS_ALL_USEFUL );
} // find_useless_packet
//
// Special Case for Quaternion Packets
//
// Make Sure we NEVER get rid of binary movement packets
// The rule is, you can't interpolate TOO a binary movement, but you can
// interpolate FROM
//#define W3D_TIMECODED_BINARY_MOVEMENT_FLAG 0x80000000
//
uint32 VectorChannelClass::find_useless_packetQ(W3dTimeCodedAnimChannelStruct * c, double tolerance)
{
assert( c ); // make sure pointer exists
assert( c->NumTimeCodes ); // make sure some packets exist
assert( c->VectorLen == 4);
uint32 packet_size = c->VectorLen + 1;
if (c->NumTimeCodes > 1) {
if (c->NumTimeCodes > 2) {
float32 *pVecSrc, *pVecDst, *pVecOrg;
uint32 *pTcSrc, *pTcDst, *pTcOrg;
for(uint32 try_idx = 0; try_idx < (c->NumTimeCodes - 2); try_idx++) {
// Src Pointers
pTcSrc = (uint32 *) &c->Data[ try_idx * packet_size ];
pVecSrc = (float32 *) pTcSrc+1;
// Original Vector we're trying to recreate
pTcOrg = (uint32 *) &c->Data[ (try_idx + 1) * packet_size ];
pVecOrg = (float32 *) pTcOrg+1;
// Dst Pointers
pTcDst = (uint32 *) &c->Data[ (try_idx + 2 ) * packet_size ];
pVecDst = (float32 *) pTcDst+1;
// Sphereical Linear Interpolate between Src, and Dst, to recreate the
// Original
// Skip automagically, if binary movement involved
if (*pTcOrg & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
continue; // can't get rid of this guy
}
if (*pTcDst & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
continue; // can't get rid of this guy either
}
float32 tStart = ((*pTcSrc) & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); // upgrade to floats
float32 tRecreate = *pTcOrg;
float32 tEnd = *pTcDst;
float32 tRatio = (tRecreate - tStart) / (tEnd - tStart);
Quaternion qSrc(1);
qSrc.Set(pVecSrc[0],pVecSrc[1],pVecSrc[2],pVecSrc[3]);
Quaternion qOrg(1);
qOrg.Set(pVecOrg[0],pVecOrg[1],pVecOrg[2],pVecOrg[3]);
Quaternion qDst(1);
qDst.Set(pVecDst[0],pVecDst[1],pVecDst[2],pVecDst[3]);
Quaternion q = Slerp( qSrc, qDst, tRatio );
Quaternion Delta(1);
Delta = Inverse(qOrg) * q;
double angle = acosf( fabs( Delta.W ) ) * 2.0;
if (angle <= tolerance ) {
return (try_idx + 1);
}
// else continue
} // for
}
}
else {
// Special Case, when there are only 2 time codes
// Check to see if they are equal, value
// if so, then return the 2nd timecode as useless
float32 *pVecSrc = (float32 *) &c->Data[ 1 ];
float32 *pVecDst = (float32 *) &c->Data[ packet_size + 1 ];
if ((c->Data[ packet_size ] & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) == 0) {
Quaternion qSrc(1);
qSrc.Set(pVecSrc[0], pVecSrc[1], pVecSrc[2], pVecSrc[3]);
Quaternion qDst(1);
qDst.Set(pVecDst[0], pVecDst[1], pVecDst[2], pVecDst[3]);
Quaternion Delta(1);
Delta = Inverse(qSrc) * qDst;
double angle = acosf( fabs( Delta.W ) ) * 2.0;
if (angle <= tolerance ) {
return (1);
}
}
}
return( PACKETS_ALL_USEFUL );
} // find_useless_packetQ
//
// Instead of using a fixed error threshold, find the packet
// that generates the least amount of error, via removal
//
// Make Sure we NEVER get rid of binary movement packets
// The rule is, you can't interpolate too a binary movement, but you can
// interpolate FROM
//#define W3D_TIMECODED_BINARY_MOVEMENT_FLAG 0x80000000
//
uint32 VectorChannelClass::find_least_useful_packet(W3dTimeCodedAnimChannelStruct *c)
{
static float32 tempvec[MAX_VECTOR_SIZE];
assert( c ); // make sure pointer exists
assert( c->NumTimeCodes ); // make sure some packets exist
assert( c->VectorLen <= MAX_VECTOR_SIZE );
uint32 packet_size = c->VectorLen + 1;
double leasterror = 9999999.0f;
uint32 ret_idx = PACKETS_ALL_USEFUL;
if (c->NumTimeCodes > 1) {
if (c->NumTimeCodes > 2) {
float32 *pVecSrc, *pVecDst, *pVecOriginal;
uint32 *pTcSrc, *pTcDst, *pTcOriginal;
for(uint32 try_idx = 0; try_idx < (c->NumTimeCodes - 2); try_idx++) {
// Src Pointers
pTcSrc = (uint32 *) &c->Data[ try_idx * packet_size ];
pVecSrc = (float32 *) pTcSrc+1;
// Original Vector we're trying to recreate
pTcOriginal = (uint32 *) &c->Data[ (try_idx + 1) * packet_size ];
pVecOriginal = (float32 *) pTcOriginal+1;
// Dst Pointers
pTcDst = (uint32 *) &c->Data[ (try_idx + 2 ) * packet_size ];
pVecDst = (float32 *) pTcDst+1;
// Skip automagically, if binary movement involved
if (*pTcOriginal & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
continue; // can't get rid of this guy
}
if (*pTcDst & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
continue; // can't get rid of this guy either
}
// Linear Interpolate between Src, and Dst, to recreate the
// Original
float32 tStart = ((*pTcSrc) & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); // upgrade to floats
float32 tRecreate = *pTcOriginal;
float32 tEnd = *pTcDst;
float32 tRatio = (tRecreate - tStart) / (tEnd - tStart);
for (uint32 idx=0; idx < c->VectorLen; idx++) {
tempvec[ idx ] = WWMath::Lerp(pVecSrc[idx], pVecDst[idx], tRatio);
}
// Compare Original to our re-creation
double delta = 0.0;
for (idx=0; idx < c->VectorLen; idx++) {
double tmp;
tmp = pVecOriginal[idx] - tempvec[idx];
delta += (tmp * tmp);
}
delta = sqrtf( delta );
if (delta < leasterror)
{
// If our Recreation is very close to the original,
// then discard the original
leasterror = delta;
ret_idx =(try_idx + 1);
}
// else continue
} // for
return( ret_idx );
}
else {
if ((c->Data[ packet_size ] & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) == 0) {
return( 1 );
}
}
}
return( PACKETS_ALL_USEFUL );
} // Find Least useful packet
//
// Instead of using a fixed error threshold, find the packet
// that generates the least amount of error, via removal
// special case for Quaternion channel
//
// Make Sure we NEVER get rid of binary movement packets
// The rule is, you can't interpolate FROM a binary movement, but you can
// interpolate TOO
//#define W3D_TIMECODED_BINARY_MOVEMENT_FLAG 0x80000000
//
uint32 VectorChannelClass::find_least_useful_packetQ(W3dTimeCodedAnimChannelStruct *c)
{
assert( c ); // make sure pointer exists
assert( c->NumTimeCodes ); // make sure some packets exist
assert( c->VectorLen == 4);
uint32 packet_size = c->VectorLen + 1;
double leasterror = 9999999.0f;
uint32 ret_idx = PACKETS_ALL_USEFUL;
if (c->NumTimeCodes > 1) {
if (c->NumTimeCodes > 2) {
float32 *pVecSrc, *pVecDst, *pVecOrg;
uint32 *pTcSrc, *pTcDst, *pTcOrg;
for(uint32 try_idx = 0; try_idx < (c->NumTimeCodes - 2); try_idx++) {
// Src Pointers
pTcSrc = (uint32 *) &c->Data[ try_idx * packet_size ];
pVecSrc = (float32 *) pTcSrc+1;
// Original Vector we're trying to recreate
pTcOrg = (uint32 *) &c->Data[ (try_idx + 1) * packet_size ];
pVecOrg = (float32 *) pTcOrg+1;
// Dst Pointers
pTcDst = (uint32 *) &c->Data[ (try_idx + 2 ) * packet_size ];
pVecDst = (float32 *) pTcDst+1;
// Skip automagically, if binary movement involved
if (*pTcOrg & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
continue; // can't get rid of this guy
}
if (*pTcDst & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
continue; // can't get rid of this guy either
}
// Spherical Linear Interpolate between Src, and Dst, to recreate the
// Original
float32 tStart = ((*pTcSrc) & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); // upgrade to floats
float32 tRecreate = *pTcOrg;
float32 tEnd = *pTcDst;
float32 tRatio = (tRecreate - tStart) / (tEnd - tStart);
Quaternion qSrc(1);
qSrc.Set(pVecSrc[0],pVecSrc[1],pVecSrc[2],pVecSrc[3]);
Quaternion qOrg(1);
qOrg.Set(pVecOrg[0],pVecOrg[1],pVecOrg[2],pVecOrg[3]);
Quaternion qDst(1);
qDst.Set(pVecDst[0],pVecDst[1],pVecDst[2],pVecDst[3]);
Quaternion q = Slerp( qSrc, qDst, tRatio );
Quaternion Delta(1);
Delta = Inverse(qOrg) * q;
double angle = acosf( fabs( Delta.W ) ) * 2;
if (angle < leasterror ) {
leasterror = angle;
ret_idx = (try_idx + 1);
}
// else continue
} // for
return( ret_idx );
}
}
else {
// Special Case, when there are only 2 time codes
// Check to see if they are equal, value
// if so, then return the 2nd timecode as useless
if ((c->Data[ packet_size ] & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) == 0) {
return( 1 );
}
}
return( PACKETS_ALL_USEFUL );
} // find_least_useful_packetQ
// EOF - vchannel.cpp