Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
vchannel.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/* $Header: /Commando/Code/Tools/max2w3d/vchannel.cpp 10 10/30/00 6:56p Greg_h $ */
20/***********************************************************************************************
21 *** Confidential - Westwood Studios ***
22 ***********************************************************************************************
23 * *
24 * Project Name : Commando Tools - W3D export *
25 * *
26 * $Archive:: /Commando/Code/Tools/max2w3d/vchannel.cpp $*
27 * *
28 * $Author:: Greg_h $*
29 * *
30 * $Modtime:: 10/30/00 5:26p $*
31 * *
32 * $Revision:: 10 $*
33 * *
34 *---------------------------------------------------------------------------------------------*
35 * Functions: *
36 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
37
38
39#include "vchannel.h"
40#include <assert.h>
41#include <string.h>
42#include <stdlib.h>
43#include "w3d_file.h"
44#include "w3dquat.h"
45#include "bchannel.h"
46#include "exportlog.h"
47
48
49#define FILTER_TABLE_SIZE (256)
50#define FILTER_TABLE_GEN_START (16)
51#define FILTER_TABLE_GEN_SIZE (FILTER_TABLE_SIZE - FILTER_TABLE_GEN_START)
52
53static float filtertable[FILTER_TABLE_SIZE] = {
54 0.00000001f,
55 0.0000001f,
56 0.000001f,
57 0.00001f,
58 0.0001f,
59 0.001f,
60 0.01f,
61 0.1f,
62 1.0f,
63 10.0f,
64 100.0f,
65 1000.0f,
66 10000.0f,
67 100000.0f,
68 1000000.0f,
69 10000000.0f,
70
71};
72static bool table_valid = false;
73
74
76(
77 uint32 id,
78 int maxframes,
79 uint32 flags,
80 int vectorlength,
81 float32 * identvect
82) :
83 ID(id),
84 Flags(flags),
85 MaxFrames(maxframes),
86 VectorLen(vectorlength),
87 IsEmpty(true),
88 IdentVect(NULL),
89 Data(NULL),
90 Begin(0),
91 End(0),
92 ReduceAnimation(false),
93 ReduceAnimationPercent(0),
94 CompressAnimation(false),
95 CompressAnimationFlavor(0),
96 CompressAnimationTranslationError(0.0f),
97 CompressAnimationRotationError(0.0f)
98{
99
100 assert(VectorLen > 0);
101 IdentVect = new float32[VectorLen];
102 Data = new float32[MaxFrames * VectorLen];
103 assert(Data);
104 assert(IdentVect);
105
106 memcpy(IdentVect,identvect,VectorLen * sizeof(float32));
107 memset(Data,0,MaxFrames * VectorLen * sizeof(float32));
108
109 // start "Begin" at the end of the array, whenever we set a value
110 // at an index less than "Begin", we push "Begin" back.
111 Begin = MaxFrames;
112 End = 0;
113
114 if (false == table_valid) {
115 // Create Filter Table, used in delta compression
116
117 for (int i=0; i<FILTER_TABLE_GEN_SIZE; i++)
118 {
119 float ratio = i;
120
121 //ratio = ((ratio + 1.0f) / 128.0f);
122 ratio/=((float) FILTER_TABLE_GEN_SIZE);
123
124 filtertable[i + FILTER_TABLE_GEN_START] = 1.0f - sin( DEG_TO_RAD(90.0f * ratio));
125 }
126
127 table_valid = true;
128
129 }
130
131
132}
133
135{
136 if (Data) {
137 delete[] Data;
138 }
139 if (IdentVect) {
140 delete[] IdentVect;
141 }
142}
143
144void VectorChannelClass::Set_Vector(int frameidx,float32 * vector)
145{
146 assert(frameidx >= 0);
147 assert(frameidx < MaxFrames);
148
149 for (int vi=0; vi<VectorLen; vi++) {
150 set_value(frameidx,vi,vector[vi]);
151 }
152
153 if (!is_identity(vector)) {
154 IsEmpty = false;
155 }
156}
157
159{
160 assert(frameidx >= 0);
161 assert(frameidx < MaxFrames);
162
163 return &(Data[frameidx * VectorLen]);
164}
165
166
167bool VectorChannelClass::SaveTimeCoded(ChunkSaveClass & csave, BitChannelClass *binmov)
168{
169 uint32 channelsize = sizeof(W3dTimeCodedAnimChannelStruct);
170 uint32 packetsize = (VectorLen * sizeof(float32)) + sizeof(uint32);
171 channelsize += packetsize * MaxFrames;
172 channelsize -= sizeof(uint32);
173
175
176 if (chn == NULL) {
177 return false;
178 }
179
180 chn->NumTimeCodes = MaxFrames;
181 chn->VectorLen = VectorLen;
182 chn->Pivot = ID;
183 chn->Flags = Flags;
184
185 // Fetch Channel Data into new format
186 // tc [data] tc [data] tc [data] .. ...
187 uint32 fidx = 0;
188
189 for (int fcount=0; fcount < MaxFrames; fcount++, fidx += (VectorLen+1) ) {
190
191 uint32 * pivec;
192 float32 * pfvec;
193
194 pivec = &chn->Data[ fidx ];
195 pfvec = (float32 *) (pivec + 1);
196
197 *pivec = fcount;
198
199 if (binmov) {
200
201 bool binary_move = binmov->Get_Bit( fcount );
202
203 // check for false binary movement
204 if (fcount != Begin) {
205 if (binary_move) {
207 }
208 }
209 else {
210 //if (log) log->printf("\nFALSE Binary\n");
211 }
212 }
213
214
215 // Copy Vector
216 for (int vidx=0; vidx < VectorLen; vidx++) {
217
218 pfvec[vidx] = get_value(fcount,vidx);
219 }
220 }
221
222 // Compress the new structure
223
224 VectorChannelClass::compress( chn );
225
226 // update the size
227
228 float original_channelsize = channelsize;
229
230 channelsize = sizeof(W3dTimeCodedAnimChannelStruct);
231 channelsize += packetsize * chn->NumTimeCodes;
232 channelsize -= sizeof(uint32);
233
234 float percent = (((float)channelsize) / original_channelsize) * 100.0f;
235 // save
236
237 ExportLog::printf("%.0f", percent);
238
239 if (csave.Write(chn,channelsize) != channelsize) {
240 return false;
241 }
242
243 if (chn != NULL) {
244 free(chn);
245 }
246
247 if (!csave.End_Chunk()) {
248 return false;
249 }
250
251 return true;
252
253} // SaveTimeCoded
254
255
256/*
257struct W3dAdaptiveDeltaAnimChannelStruct
258{
259 uint32 NumFrames; // number of frames of animation
260 uint16 Pivot; // pivot effected by this channel
261 uint8 VectorLen; // num Channels
262 uint8 Flags; // channel type
263 float Scale; // Filter Table Scale
264
265 uint32 Data[1]; // OpCode Data Stream
266
267};
268*/
269
270struct
271{
272 unsigned char filter : 7;
273 unsigned char flag : 1;
274 unsigned char d0 : 4;
275 unsigned char d1 : 4;
276 unsigned char d2 : 4;
277 unsigned char d3 : 4;
278 unsigned char d4 : 4;
279 unsigned char d5 : 4;
280 unsigned char d6 : 4;
281 unsigned char d7 : 4;
282 unsigned char d8 : 4;
283 unsigned char d9 : 4;
284 unsigned char d10 : 4;
285 unsigned char d11 : 4;
286 unsigned char d12 : 4;
287 unsigned char d13 : 4;
288 unsigned char d14 : 4;
289 unsigned char d15 : 4;
290
292
293//
294// test compress Adaptive Delta packet
295//
296// inputs: filter - forced filter to use
297// scale (filter table scale)
298// value1 (continue compression from this initial value)
299// float *indata // 16 values to compress
300// float *outdata // 16 decompressed values
301//
302// output: float error; // aggregate error for packet
303//
304float VectorChannelClass::test_compress(int filter_index, float scale, float value1, float *indata, float *outdata)
305{
306
307 float error = 0.0f;
308
309 // compute filter
310
311 float filter = filtertable[filter_index] * scale;
312
313 assert(filter_index < FILTER_TABLE_SIZE);
314
315 float last_value = value1;
316
317 for(int data_idx=0; data_idx < 16; data_idx++) {
318
319 // NOTE: DETERMINE best factor via Brute Force
320 // This helps under/over-flow problems
321
322 // brute
323 int best_factor = 100;
324 float least_error = 999999999.9f;
325 for (float try_factor = -8.0f; try_factor < 8.0f; try_factor+=1.0f) {
326 float temp = (try_factor * filter) + last_value; // decompress using this filter
327 temp-=indata[data_idx]; // delta decompressed value, vs original value
328 temp=fabs(temp);
329 if (temp < least_error) {
330 least_error = temp;
331 best_factor = try_factor;
332 }
333 }
334 assert(best_factor <= 7);
335 assert(best_factor >=-8);
336
337 float dfactor = best_factor;
338
339 outdata[data_idx] = (dfactor * filter) + last_value;
340 // END BRUTE FORCE IT
341
342 float delta = outdata[data_idx] - indata[data_idx];
343
344 //if (delta > error) error = delta;
345 error+= (delta * delta);
346
347 last_value = outdata[data_idx];
348 }
349
350 return( sqrt(error) );
351
352} // test_compress
353
354//
355// compress Adaptive Delta packet
356//
357// inputs: filter - forced filter to use
358// scale (filter table scale)
359// value1 (continue compression from this initial value)
360// float *indata // 16 values to compress
361// unsigned char *pPacket
362//
363// output: float error; // aggregate error for packet
364//
365float VectorChannelClass::compress(int filter_index, float scale, float value1, float *indata, unsigned char *pPacket, float *outdata)
366{
367 float error = 0.0f;
368
369 // compute filter
370
371 float filter = filtertable[filter_index] * scale;
372
373 assert(filter_index < FILTER_TABLE_SIZE);
374
375 *pPacket = filter_index;
376 pPacket++;
377
378 float last_value = value1;
379
380 for(int data_idx=0; data_idx < 16; data_idx++) {
381
382 // NOTE: PLAN TO ADD A LOOP IN HERE, to DETERMINE best factor via Brute Force
383 // This could help under/over-flow problems
384 {
385 // brute
386 int best_factor = 100;
387 float least_error = 999999999.9f;
388 for (float try_factor = -8.0f; try_factor < 8.0f; try_factor+=1.0f) {
389 float temp = (try_factor * filter) + last_value; // decompress using this filter
390 temp-=indata[data_idx]; // delta decompressed value, vs original value
391 temp=fabs(temp);
392 if (temp < least_error) {
393 least_error = temp;
394 best_factor = try_factor;
395 }
396 }
397 assert(best_factor <= 7);
398 assert(best_factor >=-8);
399
400 float dfactor = best_factor;
401
402 outdata[data_idx] = (dfactor * filter) + last_value;
403
404 int pi = data_idx>>1;
405
406 if (data_idx & 1) {
407 best_factor<<=4;
408 pPacket[pi]&=0x0f;
409 pPacket[pi]|=best_factor;
410 }
411 else {
412 best_factor&=0xf;
413 pPacket[pi]&=0xf0;
414 pPacket[pi]|=best_factor;
415 }
416 }
417
418 // END BRUTE FORCE IT
419
420 error+=fabs(outdata[data_idx] - indata[data_idx]);
421
422 last_value = outdata[data_idx];
423
424 }
425
426 return( error );
427
428} // compress
429
430
431bool VectorChannelClass::SaveAdaptiveDelta(ChunkSaveClass & csave, BitChannelClass *binmov)
432{
433 uint32 channelsize = sizeof(W3dAdaptiveDeltaAnimChannelStruct);
434 int packetsize = sizeof(AdaptiveDeltaPacketStruct);
435 int numpackets = (MaxFrames + 15) / 16;
436 channelsize += packetsize * numpackets * VectorLen;
437 channelsize -= sizeof(char);
438 channelsize += VectorLen * sizeof(float);
439
440 W3dAdaptiveDeltaAnimChannelStruct * chn = (W3dAdaptiveDeltaAnimChannelStruct *)malloc(channelsize);
441
442 if (chn == NULL) {
443 return false;
444 }
445
446// Brute Force Compressor
447
448 // Initialize Chan Data
449 chn->NumFrames = MaxFrames;
450 chn->Pivot = ID;
451 chn->VectorLen = VectorLen;
452 chn->Flags = Flags;
453 chn->Scale = 0.0f;
454 memset(&chn->Data[0], channelsize - (sizeof(W3dAdaptiveDeltaAnimChannelStruct) - sizeof(char)), 0x00);
455
456 assert(VectorLen <= 4); // otherwise temp vector won't have room
457
458 float *initial = (float *)&chn->Data[0];
459 float work[4];
460
461 // Fetch initial value
462 for (int i=0; i < VectorLen; i++) {
463 work[i] = initial[i] = get_value(0, i);
464 }
465
466 float original_packet[16];
467 float decompressed_packet[16];
468 float scale = 0.0f;
469 float last_value=0.0f;
470 int frame = 1;
471 float delta;
472
473 // Compute Scale
474
475 for (int fidx=1; fidx < MaxFrames; fidx++) {
476 for (i=0; i<VectorLen; i++) {
477
478 delta = fabs( get_value(fidx, i) - get_value(fidx - 1, i));
479
480 if (delta > scale) scale = delta;
481 }
482 }
483
484 scale/=7.0f; // range of the delta encode is +- 7 units, in which ever filter range
485 // the smaller the scale, the better our precision is going to be
486
487 // End Compute Scale
488
489 for (i=0; i < numpackets; i++) {
490 for (int vi=0; vi<VectorLen; vi++) {
491 last_value = work[vi];
492 // Copy Original Data into the original packet
493 int temp_frame = frame;
494
495 for(int gi=0; gi<16; gi++) {
496
497 if (temp_frame < MaxFrames) {
498 last_value = original_packet[gi] = get_value(temp_frame, vi);
499 }
500 else {
501 original_packet[gi] = last_value;
502 }
503
504 temp_frame++;
505
506 } // for gi
507
508 // Brute Force Filter
509
510 int best_filter = 2 * FILTER_TABLE_SIZE;
511 float least_error = 999999999.9f;
512 last_value = work[vi];
513
514 for(int try_filter=0; try_filter < FILTER_TABLE_SIZE; try_filter++) {
515
516 float temp_error = test_compress(try_filter, scale, last_value, original_packet, decompressed_packet);
517
518 if (temp_error < least_error) {
519 best_filter = try_filter;
520 least_error = temp_error;
521 }
522
523 }
524
525 assert(best_filter < FILTER_TABLE_SIZE);
526
527 //log->printf("\nvi= %d frames %d to %d : error = %f ",vi, frame, frame+15, least_error);
528
529 // Encode current packet
530
531 unsigned char * pPacket;
532
533 pPacket = (unsigned char *) &chn->Data[0]; // beginning of data struct
534 pPacket+= (VectorLen * sizeof(float)); // skip over initial values
535 pPacket+= (sizeof(AdaptiveDeltaPacketStruct) * VectorLen * ((frame-1)>>4)); // skip up to the appropriate packet
536 pPacket+= sizeof(AdaptiveDeltaPacketStruct) * vi; // skip up the appropriate vector index
537
538 compress(best_filter, scale, last_value, original_packet, pPacket, decompressed_packet);
539
540 // update work[vi];
541 work[vi] = decompressed_packet[15];
542
543 } // for vi
544 frame+=16;
545 } // for numpackets
546
547
548 // print how big we are vs non-compressed
549 float rawsize = sizeof(W3dAnimChannelStruct);
550 rawsize += (VectorLen * sizeof(float32) * (MaxFrames)) - sizeof(float32);
551
552 float percent = ((float)channelsize) / rawsize;
553 percent*=100.0f;
554
555 ExportLog::printf("%.0f", percent);
556
557
558 // update final scale
559 chn->Scale = scale;
560
561 if (csave.Write(chn,channelsize) != channelsize) {
562 return false;
563 }
564
565 if (chn != NULL) {
566 free(chn);
567 }
568
569 if (!csave.End_Chunk()) {
570 return false;
571 }
572
573 return true;
574
575} // SaveAdaptiveDelta
576
577
579{
580 if (IsEmpty) return true;
581
582 compute_range();
583 if (End<Begin) {
584 IsEmpty = true;
585 return true;
586 }
587
588 if (CompressAnimation) {
589 // Save the Channel Data compressed
590 // TIMECODED
591
593 return false;
594 }
595
596 switch (CompressAnimationFlavor)
597 {
599 {
600 return(SaveTimeCoded(csave, binmov));
601
602 break;
603 }
604
606 {
607 return(SaveAdaptiveDelta(csave, binmov));
608
609 break;
610 }
611
612 default:
613 assert(0); // unknown compressed format
614 return false;
615 }
616
617 }
618 else {
619 // Classic Non Compressed Data
620
622 return false;
623 }
624
625
626 unsigned int channelsize = sizeof(W3dAnimChannelStruct);
627 channelsize += VectorLen * sizeof(float32) * (MaxFrames) - sizeof(float32);
628
629 W3dAnimChannelStruct * chn = (W3dAnimChannelStruct *)malloc(channelsize);
630
631 if (chn == NULL) {
632 return false;
633 }
634
635 chn->FirstFrame = Begin;
636 chn->LastFrame = End;
637 chn->VectorLen = VectorLen;
638 chn->Pivot = ID;
639 chn->Flags = Flags;
640
641 for (int fcount=0; fcount < End-Begin+1; fcount++) {
642 for (int vidx=0; vidx < VectorLen; vidx++) {
643
644 int writeidx = fcount * VectorLen + vidx;
645
646 chn->Data[writeidx] = get_value(Begin + fcount,vidx);
647 }
648 }
649
650 if (csave.Write(chn,channelsize) != channelsize) {
651 return false;
652 }
653
654 if (chn != NULL) {
655 free(chn);
656 }
657
658 if (!csave.End_Chunk()) {
659 return false;
660 }
661
662 }
663
664 return true;
665}
666
667void VectorChannelClass::SetSaveOptions(bool compress, int flavor, float Terr, float Rerr, bool reduce, int reduce_percent)
668{
669
670 ReduceAnimation = reduce;
671 ReduceAnimationPercent = reduce_percent;
672 CompressAnimation = compress;
673 CompressAnimationFlavor = flavor;
674 CompressAnimationTranslationError = Terr;
675 CompressAnimationRotationError = DEG_TO_RAD(Rerr);
676
677} // SetSaveOptions
678
679//
680// Set data in motion channel to identity vector
681// R2 - set invisible data to repeat last known position, the previous algorthm caused problems with
682// the current movie assets
683//
685{
686 float *tvec;
687
688 assert(VectorLen <= 8);
689
690 bool prev_state = vis->Get_Bit( 0 );
691
692 tvec = Get_Vector( 0 );
693
694 for (int idx=0; idx < MaxFrames; idx++) {
695
696 bool cur_state = vis->Get_Bit( idx );
697
698 if (cur_state != prev_state) {
699 prev_state = cur_state;
700 tvec = Get_Vector( idx );
701 }
702
703 if (false == cur_state) {
704 //Set_Vector( idx, IdentVect );
705 Set_Vector( idx, tvec );
706 }
707 }
708} // ClearInvisibleData
709
710
711void VectorChannelClass::set_value(int framenum,int vindex,float32 val)
712{
713 assert(framenum >= 0);
714 assert(framenum < MaxFrames);
715 assert(vindex >= 0);
716 assert(vindex < VectorLen);
717
718 Data[framenum * VectorLen + vindex] = val;
719}
720
721float32 VectorChannelClass::get_value(int framenum,int vindex)
722{
723 assert(framenum >= 0);
724 assert(framenum < MaxFrames);
725 assert(vindex >= 0);
726 assert(vindex < VectorLen);
727
728 return Data[framenum * VectorLen + vindex];
729}
730
731bool VectorChannelClass::is_identity(float32 * vec)
732{
733 const double ERROR_TOLERANCE = 0.00005 * 0.00005;
734 double dist = 0.0;
735
736 for (int vi=0; vi<VectorLen; vi++) {
737 dist += (vec[vi] - IdentVect[vi])*(vec[vi] - IdentVect[vi]);
738 }
739
740 // if distance from identity is very small, it is identity...
741 if (dist < ERROR_TOLERANCE) {
742 return true;
743 } else {
744 return false;
745 }
746}
747
748void VectorChannelClass::compute_range(void)
749{
750 Begin = 0;
751 while ((Begin < MaxFrames) && (is_identity(Get_Vector(Begin)))) {
752 Begin++;
753 }
754
755 End = MaxFrames-1;
756 while ((End >= 0) && (is_identity(Get_Vector(End)))) {
757 End--;
758 }
759} // compute_range
760
761
762//
763// Remove a packet from a W3dTimeCodedAnimChanelStruct
764//
765void VectorChannelClass::remove_packet(W3dTimeCodedAnimChannelStruct * c, uint32 packet_idx)
766{
767 assert( c );
768 assert( c->NumTimeCodes > 1 );
769
770 uint32 packet_size = c->VectorLen + 1;
771 uint32 packet_len = packet_size * sizeof(uint32);
772
773 uint32 *src, *dst;
774
775 dst = (uint32 *) &c->Data[ packet_size * packet_idx ];
776 src = (uint32 *) &c->Data[ packet_size * (packet_idx + 1) ];
777
778 uint32 copy_length = (c->NumTimeCodes - (packet_idx + 1)) * packet_len;
779
780 if (copy_length) {
781
782 memcpy(dst, src, copy_length);
783
784 }
785
786 // Decrement Packet Count
787 c->NumTimeCodes--;
788
789} // remove_packet
790
791//
792// Take a non-compressed TimeCoded Motion Channel
793// and compress the packets
794//
795void VectorChannelClass::compress(W3dTimeCodedAnimChannelStruct * c)
796{
797
798 assert( c );
799
800
801 // Standard Error Threshold Compression
802
803 double Terr = CompressAnimationTranslationError;
804 double Rerr = CompressAnimationRotationError;
805
806 float TimeCodes_ct = c->NumTimeCodes;
807
808 switch( c->Flags )
809 {
810 case ANIM_CHANNEL_X:
811 case ANIM_CHANNEL_Y:
812 case ANIM_CHANNEL_Z: {
813
814 while(1) {
815
816 uint32 idx = find_useless_packet( c, Terr );
817
818 if (PACKETS_ALL_USEFUL == idx) break;
819
820 remove_packet( c, idx );
821
822 }
823
824 break;
825 }
826
827 case ANIM_CHANNEL_XR:
828 case ANIM_CHANNEL_YR:
829 case ANIM_CHANNEL_ZR: {
830
831 while(1) {
832
833 uint32 idx = find_useless_packet( c, Rerr );
834
835 if (PACKETS_ALL_USEFUL == idx) break;
836
837 remove_packet( c, idx );
838
839 }
840
841 break;
842 }
843
844 case ANIM_CHANNEL_Q: {
845
846 while(1) {
847
848 uint32 idx = find_useless_packetQ( c, Rerr );
849
850 if (PACKETS_ALL_USEFUL == idx) break;
851
852 remove_packet( c, idx );
853
854 }
855
856 break;
857 }
858
859 default: // undefined channel
860 assert(0);
861 break;
862 }
863
864 // Forced Reduction Phase
865
866 if (ReduceAnimation) {
867 if (ReduceAnimationPercent) {
868
869 float pct = ReduceAnimationPercent;
870
871 pct *= (0.01f);
872
873 pct = 1.0f - pct;
874
875 // if out of range, don't even try
876 if (pct <= 0.0f) return;
877 if (pct >= 1.0f) return;
878
879 pct*=TimeCodes_ct;
880
881 pct+=0.5f;
882
883 uint32 maxFrames = pct;
884
885 if (maxFrames < 2) maxFrames = 2;
886
887 if (maxFrames >= c->NumTimeCodes) return; // desired minimum already attained
888
889
890 switch( c->Flags )
891 {
892 case ANIM_CHANNEL_X:
893 case ANIM_CHANNEL_Y:
894 case ANIM_CHANNEL_Z:
895 case ANIM_CHANNEL_XR:
896 case ANIM_CHANNEL_YR:
897 case ANIM_CHANNEL_ZR: {
898
899 while(maxFrames < c->NumTimeCodes) {
900
901 uint32 idx = find_least_useful_packet( c );
902 if (PACKETS_ALL_USEFUL == idx) break;
903 remove_packet( c, idx );
904 }
905
906 break;
907 }
908
909 case ANIM_CHANNEL_Q: {
910
911 while(maxFrames < c->NumTimeCodes) {
912
913 uint32 idx = find_least_useful_packetQ( c );
914 if (PACKETS_ALL_USEFUL == idx) break;
915 remove_packet( c, idx );
916 }
917
918 break;
919 }
920
921 default: // undefined channel
922 assert(0);
923 break;
924 }
925 } // if ReducePercent
926
927 } // if Reduce
928
929} // compress
930
931
932
933//
934// find a packet that isn't needed, and return the index
935// if all packets are necessary, then return back PACKETS_ALL_USEFUL
936// a useless packet is defined, as a packet that can be recreated
937// via interpolation
938//
939// Make Sure we NEVER get rid of binary movement packets
940// The rule is, you can't interpolate TOO a binary movement, but you can
941// interpolate FROM
942//#define W3D_TIMECODED_BINARY_MOVEMENT_FLAG 0x80000000
943//
944uint32 VectorChannelClass::find_useless_packet(W3dTimeCodedAnimChannelStruct * c, double tolerance)
945{
946#define MAX_VECTOR_SIZE 8
947static float32 tempvec[MAX_VECTOR_SIZE];
948
949 assert( c ); // make sure pointer exists
950 assert( c->NumTimeCodes ); // make sure some packets exist
951 assert( c->VectorLen <= MAX_VECTOR_SIZE );
952
953 uint32 packet_size = c->VectorLen + 1;
954
955 if (c->NumTimeCodes > 1) {
956 if (c->NumTimeCodes > 2) {
957
958 float32 *pVecSrc, *pVecDst, *pVecOriginal;
959 uint32 *pTcSrc, *pTcDst, *pTcOriginal;
960
961 for(uint32 try_idx = 0; try_idx < (c->NumTimeCodes - 2); try_idx++) {
962
963 // Src Pointers
964 pTcSrc = (uint32 *) &c->Data[ try_idx * packet_size ];
965 pVecSrc = (float32 *) pTcSrc+1;
966
967 // Original Vector we're trying to recreate
968 pTcOriginal = (uint32 *) &c->Data[ (try_idx + 1) * packet_size ];
969 pVecOriginal = (float32 *) pTcOriginal+1;
970
971 // Dst Pointers
972 pTcDst = (uint32 *) &c->Data[ (try_idx + 2 ) * packet_size ];
973 pVecDst = (float32 *) pTcDst+1;
974
975 // Skip automagically, if binary movement involved
976 if (*pTcOriginal & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
977 continue; // can't get rid of this guy
978 }
980 continue; // can't get rid of this guy either
981 }
982
983 // Linear Interpolate between Src, and Dst, to recreate the
984 // Original
985
986 float32 tStart = ((*pTcSrc) & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); // upgrade to floats
987 float32 tRecreate = *pTcOriginal;
988 float32 tEnd = *pTcDst;
989 float32 tRatio = (tRecreate - tStart) / (tEnd - tStart);
990
991 for (uint32 idx=0; idx < c->VectorLen; idx++) {
992
993 tempvec[ idx ] = WWMath::Lerp(pVecSrc[idx], pVecDst[idx], tRatio);
994
995 }
996
997 // Compare Original to our re-creation
998
999 bool close_enough = true;
1000
1001 for (idx=0; idx < c->VectorLen; idx++) {
1002
1003 float32 delta;
1004
1005 delta = fabs(pVecOriginal[idx] - tempvec[idx]);
1006
1007 if (delta > tolerance) {
1008 close_enough = false;
1009 break;
1010 }
1011
1012 }
1013
1014 // If our Recreation is very close to the original,
1015 // then discard the original
1016
1017 if (true == close_enough) {
1018 return (try_idx + 1);
1019 }
1020
1021 // else continue
1022
1023 } // for
1024
1025 }
1026 else {
1027 // Special Case, when there are only 2 time codes
1028 // Check to see if they are equal, value
1029 // if so, then return the 2nd timecode as useless
1030
1031 float32 *pVecSrc = (float32 *) &c->Data[ 1 ];
1032 float32 *pVecDst = (float32 *) &c->Data[ packet_size + 1 ];
1033
1034 bool identical = true;
1035
1036 if ((c->Data[ packet_size ] & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) == 0) {
1037
1038 for(uint32 idx=0; idx < c->VectorLen; idx++) {
1039
1040 float32 delta;
1041
1042 delta = fabs(pVecDst[idx] - pVecSrc[idx]);
1043
1044 if (delta > tolerance) {
1045 identical = false;
1046 break;
1047 }
1048
1049 }
1050
1051 if (identical) return( 1 );
1052
1053 }
1054 }
1055 }
1056
1057 return( PACKETS_ALL_USEFUL );
1058
1059} // find_useless_packet
1060
1061
1062//
1063// Special Case for Quaternion Packets
1064//
1065// Make Sure we NEVER get rid of binary movement packets
1066// The rule is, you can't interpolate TOO a binary movement, but you can
1067// interpolate FROM
1068//#define W3D_TIMECODED_BINARY_MOVEMENT_FLAG 0x80000000
1069//
1070uint32 VectorChannelClass::find_useless_packetQ(W3dTimeCodedAnimChannelStruct * c, double tolerance)
1071{
1072
1073 assert( c ); // make sure pointer exists
1074 assert( c->NumTimeCodes ); // make sure some packets exist
1075 assert( c->VectorLen == 4);
1076
1077 uint32 packet_size = c->VectorLen + 1;
1078
1079 if (c->NumTimeCodes > 1) {
1080 if (c->NumTimeCodes > 2) {
1081
1082 float32 *pVecSrc, *pVecDst, *pVecOrg;
1083 uint32 *pTcSrc, *pTcDst, *pTcOrg;
1084
1085 for(uint32 try_idx = 0; try_idx < (c->NumTimeCodes - 2); try_idx++) {
1086
1087 // Src Pointers
1088 pTcSrc = (uint32 *) &c->Data[ try_idx * packet_size ];
1089 pVecSrc = (float32 *) pTcSrc+1;
1090
1091 // Original Vector we're trying to recreate
1092 pTcOrg = (uint32 *) &c->Data[ (try_idx + 1) * packet_size ];
1093 pVecOrg = (float32 *) pTcOrg+1;
1094
1095 // Dst Pointers
1096 pTcDst = (uint32 *) &c->Data[ (try_idx + 2 ) * packet_size ];
1097 pVecDst = (float32 *) pTcDst+1;
1098
1099 // Sphereical Linear Interpolate between Src, and Dst, to recreate the
1100 // Original
1101
1102 // Skip automagically, if binary movement involved
1103 if (*pTcOrg & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
1104 continue; // can't get rid of this guy
1105 }
1106 if (*pTcDst & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
1107 continue; // can't get rid of this guy either
1108 }
1109
1110 float32 tStart = ((*pTcSrc) & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); // upgrade to floats
1111 float32 tRecreate = *pTcOrg;
1112 float32 tEnd = *pTcDst;
1113 float32 tRatio = (tRecreate - tStart) / (tEnd - tStart);
1114
1115
1116 Quaternion qSrc(1);
1117 qSrc.Set(pVecSrc[0],pVecSrc[1],pVecSrc[2],pVecSrc[3]);
1118
1119 Quaternion qOrg(1);
1120 qOrg.Set(pVecOrg[0],pVecOrg[1],pVecOrg[2],pVecOrg[3]);
1121
1122 Quaternion qDst(1);
1123 qDst.Set(pVecDst[0],pVecDst[1],pVecDst[2],pVecDst[3]);
1124
1125 Quaternion q = Slerp( qSrc, qDst, tRatio );
1126
1127 Quaternion Delta(1);
1128 Delta = Inverse(qOrg) * q;
1129
1130 double angle = acosf( fabs( Delta.W ) ) * 2.0;
1131
1132 if (angle <= tolerance ) {
1133 return (try_idx + 1);
1134 }
1135
1136 // else continue
1137
1138 } // for
1139
1140 }
1141
1142 }
1143 else {
1144 // Special Case, when there are only 2 time codes
1145 // Check to see if they are equal, value
1146 // if so, then return the 2nd timecode as useless
1147
1148 float32 *pVecSrc = (float32 *) &c->Data[ 1 ];
1149 float32 *pVecDst = (float32 *) &c->Data[ packet_size + 1 ];
1150
1151 if ((c->Data[ packet_size ] & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) == 0) {
1152
1153 Quaternion qSrc(1);
1154 qSrc.Set(pVecSrc[0], pVecSrc[1], pVecSrc[2], pVecSrc[3]);
1155
1156 Quaternion qDst(1);
1157 qDst.Set(pVecDst[0], pVecDst[1], pVecDst[2], pVecDst[3]);
1158
1159 Quaternion Delta(1);
1160 Delta = Inverse(qSrc) * qDst;
1161
1162 double angle = acosf( fabs( Delta.W ) ) * 2.0;
1163
1164 if (angle <= tolerance ) {
1165 return (1);
1166 }
1167 }
1168 }
1169
1170 return( PACKETS_ALL_USEFUL );
1171
1172} // find_useless_packetQ
1173
1174
1175//
1176// Instead of using a fixed error threshold, find the packet
1177// that generates the least amount of error, via removal
1178//
1179// Make Sure we NEVER get rid of binary movement packets
1180// The rule is, you can't interpolate too a binary movement, but you can
1181// interpolate FROM
1182//#define W3D_TIMECODED_BINARY_MOVEMENT_FLAG 0x80000000
1183//
1184uint32 VectorChannelClass::find_least_useful_packet(W3dTimeCodedAnimChannelStruct *c)
1185{
1186
1187static float32 tempvec[MAX_VECTOR_SIZE];
1188
1189 assert( c ); // make sure pointer exists
1190 assert( c->NumTimeCodes ); // make sure some packets exist
1191 assert( c->VectorLen <= MAX_VECTOR_SIZE );
1192
1193 uint32 packet_size = c->VectorLen + 1;
1194
1195 double leasterror = 9999999.0f;
1196 uint32 ret_idx = PACKETS_ALL_USEFUL;
1197
1198 if (c->NumTimeCodes > 1) {
1199 if (c->NumTimeCodes > 2) {
1200
1201 float32 *pVecSrc, *pVecDst, *pVecOriginal;
1202 uint32 *pTcSrc, *pTcDst, *pTcOriginal;
1203
1204 for(uint32 try_idx = 0; try_idx < (c->NumTimeCodes - 2); try_idx++) {
1205
1206 // Src Pointers
1207 pTcSrc = (uint32 *) &c->Data[ try_idx * packet_size ];
1208 pVecSrc = (float32 *) pTcSrc+1;
1209
1210 // Original Vector we're trying to recreate
1211 pTcOriginal = (uint32 *) &c->Data[ (try_idx + 1) * packet_size ];
1212 pVecOriginal = (float32 *) pTcOriginal+1;
1213
1214 // Dst Pointers
1215 pTcDst = (uint32 *) &c->Data[ (try_idx + 2 ) * packet_size ];
1216 pVecDst = (float32 *) pTcDst+1;
1217
1218 // Skip automagically, if binary movement involved
1219 if (*pTcOriginal & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
1220 continue; // can't get rid of this guy
1221 }
1222 if (*pTcDst & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
1223 continue; // can't get rid of this guy either
1224 }
1225
1226 // Linear Interpolate between Src, and Dst, to recreate the
1227 // Original
1228
1229 float32 tStart = ((*pTcSrc) & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); // upgrade to floats
1230 float32 tRecreate = *pTcOriginal;
1231 float32 tEnd = *pTcDst;
1232 float32 tRatio = (tRecreate - tStart) / (tEnd - tStart);
1233
1234 for (uint32 idx=0; idx < c->VectorLen; idx++) {
1235
1236 tempvec[ idx ] = WWMath::Lerp(pVecSrc[idx], pVecDst[idx], tRatio);
1237
1238 }
1239
1240 // Compare Original to our re-creation
1241
1242 double delta = 0.0;
1243
1244
1245 for (idx=0; idx < c->VectorLen; idx++) {
1246
1247 double tmp;
1248
1249 tmp = pVecOriginal[idx] - tempvec[idx];
1250
1251 delta += (tmp * tmp);
1252
1253 }
1254
1255 delta = sqrtf( delta );
1256
1257 if (delta < leasterror)
1258 {
1259
1260 // If our Recreation is very close to the original,
1261 // then discard the original
1262 leasterror = delta;
1263 ret_idx =(try_idx + 1);
1264 }
1265
1266 // else continue
1267
1268 } // for
1269
1270 return( ret_idx );
1271
1272 }
1273 else {
1274
1275 if ((c->Data[ packet_size ] & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) == 0) {
1276 return( 1 );
1277 }
1278 }
1279 }
1280
1281
1282 return( PACKETS_ALL_USEFUL );
1283
1284
1285} // Find Least useful packet
1286
1287
1288//
1289// Instead of using a fixed error threshold, find the packet
1290// that generates the least amount of error, via removal
1291// special case for Quaternion channel
1292//
1293// Make Sure we NEVER get rid of binary movement packets
1294// The rule is, you can't interpolate FROM a binary movement, but you can
1295// interpolate TOO
1296//#define W3D_TIMECODED_BINARY_MOVEMENT_FLAG 0x80000000
1297//
1298uint32 VectorChannelClass::find_least_useful_packetQ(W3dTimeCodedAnimChannelStruct *c)
1299{
1300
1301 assert( c ); // make sure pointer exists
1302 assert( c->NumTimeCodes ); // make sure some packets exist
1303 assert( c->VectorLen == 4);
1304
1305 uint32 packet_size = c->VectorLen + 1;
1306
1307 double leasterror = 9999999.0f;
1308 uint32 ret_idx = PACKETS_ALL_USEFUL;
1309
1310 if (c->NumTimeCodes > 1) {
1311 if (c->NumTimeCodes > 2) {
1312
1313 float32 *pVecSrc, *pVecDst, *pVecOrg;
1314 uint32 *pTcSrc, *pTcDst, *pTcOrg;
1315
1316 for(uint32 try_idx = 0; try_idx < (c->NumTimeCodes - 2); try_idx++) {
1317
1318 // Src Pointers
1319 pTcSrc = (uint32 *) &c->Data[ try_idx * packet_size ];
1320 pVecSrc = (float32 *) pTcSrc+1;
1321
1322 // Original Vector we're trying to recreate
1323 pTcOrg = (uint32 *) &c->Data[ (try_idx + 1) * packet_size ];
1324 pVecOrg = (float32 *) pTcOrg+1;
1325
1326 // Dst Pointers
1327 pTcDst = (uint32 *) &c->Data[ (try_idx + 2 ) * packet_size ];
1328 pVecDst = (float32 *) pTcDst+1;
1329
1330 // Skip automagically, if binary movement involved
1331 if (*pTcOrg & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
1332 continue; // can't get rid of this guy
1333 }
1334 if (*pTcDst & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) {
1335 continue; // can't get rid of this guy either
1336 }
1337
1338 // Spherical Linear Interpolate between Src, and Dst, to recreate the
1339 // Original
1340
1341 float32 tStart = ((*pTcSrc) & ~W3D_TIMECODED_BINARY_MOVEMENT_FLAG); // upgrade to floats
1342 float32 tRecreate = *pTcOrg;
1343 float32 tEnd = *pTcDst;
1344 float32 tRatio = (tRecreate - tStart) / (tEnd - tStart);
1345
1346
1347 Quaternion qSrc(1);
1348 qSrc.Set(pVecSrc[0],pVecSrc[1],pVecSrc[2],pVecSrc[3]);
1349
1350 Quaternion qOrg(1);
1351 qOrg.Set(pVecOrg[0],pVecOrg[1],pVecOrg[2],pVecOrg[3]);
1352
1353 Quaternion qDst(1);
1354 qDst.Set(pVecDst[0],pVecDst[1],pVecDst[2],pVecDst[3]);
1355
1356 Quaternion q = Slerp( qSrc, qDst, tRatio );
1357
1358 Quaternion Delta(1);
1359 Delta = Inverse(qOrg) * q;
1360
1361 double angle = acosf( fabs( Delta.W ) ) * 2;
1362
1363 if (angle < leasterror ) {
1364 leasterror = angle;
1365 ret_idx = (try_idx + 1);
1366 }
1367
1368 // else continue
1369
1370 } // for
1371
1372 return( ret_idx );
1373
1374 }
1375
1376 }
1377 else {
1378 // Special Case, when there are only 2 time codes
1379 // Check to see if they are equal, value
1380 // if so, then return the 2nd timecode as useless
1381
1382 if ((c->Data[ packet_size ] & W3D_TIMECODED_BINARY_MOVEMENT_FLAG) == 0) {
1383 return( 1 );
1384 }
1385 }
1386
1387
1388 return( PACKETS_ALL_USEFUL );
1389
1390} // find_least_useful_packetQ
1391
1392
1393// EOF - vchannel.cpp
#define NULL
Definition BaseType.h:92
Color scale(const Color &a, const Color &b)
Definition GameMtl.cpp:722
@ ANIM_FLAVOR_TIMECODED
Definition w3d_file.h:1437
@ ANIM_FLAVOR_ADAPTIVE_DELTA
Definition w3d_file.h:1438
#define W3D_TIMECODED_BINARY_MOVEMENT_FLAG
Definition w3d_file.h:1478
@ W3D_CHUNK_COMPRESSED_ANIMATION_CHANNEL
Definition w3d_file.h:408
@ W3D_CHUNK_ANIMATION_CHANNEL
Definition w3d_file.h:403
@ ANIM_CHANNEL_YR
Definition w3d_file.h:1417
@ ANIM_CHANNEL_Y
Definition w3d_file.h:1414
@ ANIM_CHANNEL_XR
Definition w3d_file.h:1416
@ ANIM_CHANNEL_Z
Definition w3d_file.h:1415
@ ANIM_CHANNEL_X
Definition w3d_file.h:1413
@ ANIM_CHANNEL_ZR
Definition w3d_file.h:1418
@ ANIM_CHANNEL_Q
Definition w3d_file.h:1419
unsigned long uint32
Definition bittype.h:46
float float32
Definition bittype.h:54
@ true
Definition bool.h:59
@ false
Definition bool.h:59
#define DEG_TO_RAD(x)
Definition wwmath.h:79
WWINLINE int Get_Bit(int frame) const
Definition motchan.h:191
uint32 Write(const void *buf, uint32 nbytes)
Definition chunkio.cpp:264
bool Begin_Chunk(uint32 id)
Definition chunkio.cpp:108
bool End_Chunk()
Definition chunkio.cpp:148
static void printf(char *,...)
VectorChannelClass(uint32 id, int maxframes, uint32 flags, int vectorlength, float32 *identvec)
Definition vchannel.cpp:76
float * Get_Vector(int frameidx)
Definition vchannel.cpp:158
void ClearInvisibleData(BitChannelClass *vis)
Definition vchannel.cpp:684
void SetSaveOptions(bool compress, int flavor, float Terr, float Rerr, bool reduce, int reduce_percent)
Definition vchannel.cpp:667
void Set_Vector(int framenumber, float32 *vector)
Definition vchannel.cpp:144
bool Save(ChunkSaveClass &csave, BitChannelClass *binmov)
Definition vchannel.cpp:578
static float Lerp(float a, float b, float lerp)
Definition wwmath.h:270
#define FILTER_TABLE_SIZE
Definition motchan.cpp:57
#define FILTER_TABLE_GEN_START
Definition motchan.cpp:58
#define FILTER_TABLE_GEN_SIZE
Definition motchan.cpp:59
int percent
Definition patch.cpp:426
int q
Definition test1.cpp:94
void Slerp(Quaternion &res, const Quaternion &p, const Quaternion &q, float alpha)
Definition quat.cpp:487
WWINLINE Quaternion Inverse(const Quaternion &a)
Definition quat.h:117
unsigned char d1
Definition vchannel.cpp:275
unsigned char d7
Definition vchannel.cpp:281
unsigned char d0
Definition vchannel.cpp:274
unsigned char d4
Definition vchannel.cpp:278
struct @125103322152314045032077201241336000106237244067 AdaptiveDeltaPacketStruct
unsigned char d8
Definition vchannel.cpp:282
unsigned char d5
Definition vchannel.cpp:279
unsigned char d11
Definition vchannel.cpp:285
unsigned char d3
Definition vchannel.cpp:277
unsigned char d13
Definition vchannel.cpp:287
unsigned char d12
Definition vchannel.cpp:286
unsigned char flag
Definition vchannel.cpp:273
unsigned char d10
Definition vchannel.cpp:284
unsigned char d6
Definition vchannel.cpp:280
unsigned char d2
Definition vchannel.cpp:276
unsigned char d9
Definition vchannel.cpp:283
unsigned char d15
Definition vchannel.cpp:289
unsigned char d14
Definition vchannel.cpp:288
#define MAX_VECTOR_SIZE
unsigned char filter
Definition vchannel.cpp:272
#define PACKETS_ALL_USEFUL
Definition vchannel.h:76