Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
motion.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/motion.cpp 26 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/motion.cpp $*
27 * *
28 * $Author:: Greg_h $*
29 * *
30 * $Modtime:: 10/30/00 5:25p $*
31 * *
32 * $Revision:: 26 $*
33 * *
34 *---------------------------------------------------------------------------------------------*
35 * Functions: *
36 * MotionClass::MotionClass -- constructor *
37 * MotionClass::MotionClass -- constructor *
38 * MotionClass::init -- initialize *
39 * MotionClass::~MotionClass -- destructor *
40 * MotionClass::compute_frame_motion -- compute the motion for a specified frame *
41 * MotionClass::set_motion_matrix -- save a motin matrix *
42 * MotionClass::get_motion_matrix -- retrieve a motion matrix *
43 * MotionClass::set_eulers -- store euler angles *
44 * MotionClass::get_eulers -- retrieve euler angles *
45 * MotionClass::Save -- save the motion to a W3D file *
46 * MotionClass::save_header -- save the header *
47 * MotionClass::save_channels -- save the motion channels *
48 * MotionClass::set_visibility -- store a visibility bit *
49 * MotionClass::get_visibility -- retrieve the visibility bit for this node:frame *
50 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
51
52#include "motion.h"
53#include "w3d_file.h"
54#include "vchannel.h"
55#include "bchannel.h"
56#include "euler.h"
57#include "util.h"
58#include "errclass.h"
59#include "w3dutil.h"
60#include "exportlog.h"
61
62
63
64/***********************************************************************************************
65 * MotionClass::MotionClass -- constructor *
66 * *
67 * INPUT: *
68 * *
69 * OUTPUT: *
70 * *
71 * WARNINGS: *
72 * *
73 * HISTORY: *
74 * 10/26/1997 GH : Created. *
75 *=============================================================================================*/
77(
78 IScene * scene,
79 INode * rootnode,
80 HierarchySaveClass * basepose,
81 W3dExportOptionsStruct & options,
82 int framerate,
84 HWND MaxHwnd,
85 char * name,
86 Matrix3 & offset
87):
88 BasePose(basepose),
89 Scene(scene),
90 RootNode(rootnode),
91 RootList(NULL),
92 StartFrame(options.StartFrame),
93 EndFrame(options.EndFrame),
94 ReduceAnimation(options.ReduceAnimation),
95 ReduceAnimationPercent(options.ReduceAnimationPercent),
96 CompressAnimation(options.CompressAnimation),
97 CompressAnimationFlavor(options.CompressAnimationFlavor),
98 CompressAnimationTranslationError(options.CompressAnimationTranslationError),
99 CompressAnimationRotationError(options.CompressAnimationRotationError),
100 FrameRate(framerate),
101 Meter(meter),
102 Offset(offset)
103{
104
105 ExportLog::printf("Initializing Capture....\n");
106
107 init();
108
109 Set_W3D_Name(Name,name);
110}
111
112/***********************************************************************************************
113 * MotionClass::MotionClass -- constructor *
114 * *
115 * INPUT: *
116 * *
117 * OUTPUT: *
118 * *
119 * WARNINGS: *
120 * *
121 * HISTORY: *
122 * 10/26/1997 GH : Created. *
123 *=============================================================================================*/
125(
126 IScene * scene,
127 INodeListClass * rootlist,
128 HierarchySaveClass * basepose,
129 W3dExportOptionsStruct & options,
130 int framerate,
131 Progress_Meter_Class * meter,
132 HWND MaxHwnd,
133 char * name,
134 Matrix3 & offset
135):
136 BasePose(basepose),
137 Scene(scene),
138 RootNode(NULL),
139 RootList(rootlist),
140 StartFrame(options.StartFrame),
141 EndFrame(options.EndFrame),
142 ReduceAnimation(options.ReduceAnimation),
143 ReduceAnimationPercent(options.ReduceAnimationPercent),
144 CompressAnimation(options.CompressAnimation),
145 CompressAnimationFlavor(options.CompressAnimationFlavor),
146 CompressAnimationTranslationError(options.CompressAnimationTranslationError),
147 CompressAnimationRotationError(options.CompressAnimationRotationError),
148 FrameRate(framerate),
149 Meter(meter),
150 Offset(offset)
151{
152
153 ExportLog::printf("Initializing Capture....\n");
154
155 init();
156
157 Set_W3D_Name(Name,name);
158}
159
160/***********************************************************************************************
161 * MotionClass::init -- initialize *
162 * *
163 * INPUT: *
164 * *
165 * OUTPUT: *
166 * *
167 * WARNINGS: *
168 * *
169 * HISTORY: *
170 * 10/26/1997 GH : Created. *
171 *=============================================================================================*/
172void MotionClass::init(void)
173{
174 int i,j;
175
176 NumFrames = (EndFrame - StartFrame + 1);
177
178 ExportLog::printf("Extracting %d frames of animation from Max\n", NumFrames);
179 ExportLog::printf("\n");
180
181 /*
182 ** Allocate space for a matrix per frame per node
183 ** and an XYZEulers per frame per node.
184 */
185 MotionMatrix = new Matrix3 * [BasePose->Num_Nodes()];
186 if (MotionMatrix == NULL) {
187 throw (ErrorClass("Out of Memory"));
188 }
189
190 EulerDelta = new Point3 * [BasePose->Num_Nodes()];
191 if (EulerDelta == NULL) {
192 throw (ErrorClass("Out of Memory"));
193 }
194
195 for (i=0; i<BasePose->Num_Nodes(); i++) {
196 MotionMatrix[i] = new Matrix3[NumFrames];
197 if (MotionMatrix[i] == NULL) {
198 throw (ErrorClass("Out of Memory"));
199 }
200
201 /*
202 ** Initialize all of the matrices to identity.
203 */
204 for (j=0; j<NumFrames; j++) {
205 MotionMatrix[i][j] = Matrix3(1);
206 }
207 }
208
209 for (i=0; i<BasePose->Num_Nodes(); i++) {
210 EulerDelta[i] = new Point3[NumFrames];
211 if (EulerDelta[i] == NULL) {
212 throw (ErrorClass("Out of Memory"));
213 }
214
215 /*
216 ** Initialize all of the Euler angles to 0,0,0
217 */
218 for (j=0; j<NumFrames; j++) {
219 EulerDelta[i][j] = Point3(0,0,0);
220 }
221 }
222
223 /*
224 ** allocate boolean vectors for the visiblity data
225 */
226 VisData = new BooleanVectorClass[BasePose->Num_Nodes()];
227
228 for (i=0; i<BasePose->Num_Nodes(); i++) {
229 VisData[i].Resize(NumFrames);
230
231 /*
232 ** initialize to always visible
233 */
234 for (j=0; j<NumFrames; j++) {
235 VisData[i][j] = true;
236 }
237 }
238
239 //
240 // allocate boolean vectors for movement data
241 //
242 BinMoveData = new BooleanVectorClass[BasePose->Num_Nodes()];
243
244 for (i=0; i<BasePose->Num_Nodes(); i++) {
245 BinMoveData[i].Resize(NumFrames);
246
247 /*
248 ** initialize to always interpolate
249 */
250 for (j=0; j<NumFrames; j++) {
251 BinMoveData[i][j] = false;
252 }
253 }
254
255
256 /*
257 ** Allocate a bit for each node in the base pose. These
258 ** bits indicate whether the node actually appeared in the
259 ** scene. If the bit is zero after all of the animation
260 ** has been processed, the node can be ignored.
261 */
262 NodeValidFlags.Resize(BasePose->Num_Nodes());
263
264 for (i=0; i<BasePose->Num_Nodes(); i++) {
265 NodeValidFlags[i] = 0;
266 }
267
268 /*
269 ** Compute motion data for each frame
270 */
271 for (i=0; i < NumFrames; i++) {
272 ExportLog::rprintf("( %d ) ", i);
273 ExportLog::updatebar(i, NumFrames);
274 compute_frame_motion(i);
275 }
276
277 ExportLog::updatebar(1, 1); // 100%
278 ExportLog::rprintf("Extraction Complete.\n");
279}
280
281
282
283/***********************************************************************************************
284 * MotionClass::~MotionClass -- destructor *
285 * *
286 * INPUT: *
287 * *
288 * OUTPUT: *
289 * *
290 * WARNINGS: *
291 * *
292 * HISTORY: *
293 * 10/26/1997 GH : Created. *
294 *=============================================================================================*/
296{
297 int i;
298
299 for (i=0; i<BasePose->Num_Nodes(); i++) {
300 if (MotionMatrix[i]) delete[] MotionMatrix[i];
301 }
302 if (MotionMatrix) {
303 delete[] MotionMatrix;
304 }
305
306 for (i=0; i<BasePose->Num_Nodes(); i++) {
307 if (EulerDelta[i]) delete[] EulerDelta[i];
308 }
309 if (EulerDelta) {
310 delete[] EulerDelta;
311 }
312
313 if (VisData) {
314 delete[] VisData;
315 }
316
317 if (BinMoveData) {
318 delete[] BinMoveData;
319 }
320
321 ExportLog::printf("Destroy Log..%d,%d,%d,%d, %s..\n",1,2,3,4,"go");
322
323}
324
325
326/***********************************************************************************************
327 * MotionClass::compute_frame_motion -- compute the motion for a specified frame *
328 * *
329 * INPUT: *
330 * *
331 * OUTPUT: *
332 * *
333 * WARNINGS: *
334 * *
335 * HISTORY: *
336 * 10/26/1997 GH : Created. *
337 *=============================================================================================*/
338void MotionClass::compute_frame_motion(int frame)
339{
340 /*
341 ** Compute MAX's time value for this frame
342 ** NOTE: the frame index passed in is the offset from StartFrame
343 ** to get the original MAX frame number, we add StartFrame.
344 */
345 TimeValue frametime = (StartFrame + frame) * GetTicksPerFrame();
346
347 /*
348 ** Create a hierarchy tree object for the scene at this frame
349 */
350 HierarchySaveClass * tree;
351
352 if (RootNode != NULL) {
353 tree = new HierarchySaveClass(RootNode,frametime,*Meter,"NoName",false,BasePose);
354 } else {
355 tree = new HierarchySaveClass(RootList,frametime,*Meter,"NoName",false,BasePose,Offset);
356 }
357
358 if (tree == NULL) {
359 throw (ErrorClass("Out of memory!"));
360 }
361
362 /*
363 ** Loop over each node in this frame's tree
364 */
365 for (int tindex=0; tindex<tree->Num_Nodes(); tindex++) {
366
367 /*
368 ** Find the node in the Base Pose corresponding to this node.
369 ** If this node is not in the base pose, skip
370 */
371 int bindex = BasePose->Find_Named_Node(tree->Get_Node_Name(tindex));
372
373 if (bindex != -1) {
374
375 /*
376 ** Get the relative transform from the base and from
377 ** this frame's tree. Assume that both have already been "fixed";
378 ** obviously the base pose has... However, the current tree
379 ** needs to be built by passing the basepose in as the "fixup tree"
380 **
381 ** What are the "fixup" matrices? These are the transforms which
382 ** were applied to the base pose when the user wanted to force the
383 ** base pose to use only matrices with certain properties. For
384 ** example, if we wanted the base pose to use translations only,
385 ** the fixup transform for each node is a transform which when
386 ** multiplied by the real node's world transform, yeilds a pure
387 ** translation matrix. Fixup matrices also show up in the mesh
388 ** exporter since all vertices must be transformed by their inverses
389 ** in order to make things work...
390 */
391 Matrix3 basetm = BasePose->Get_Node_Relative_Transform(bindex);
392 Matrix3 thistm = tree->Get_Node_Relative_Transform(tindex);
393 INode *tree_node = tree->Get_Node(tindex);
394
395 Matrix3 motion = thistm * Inverse(basetm);
396
397 motion = Cleanup_Orthogonal_Matrix(motion);
398
399 set_motion_matrix(bindex,frame,motion);
400
401 /*
402 ** Also, store the Euler angles for this node
403 */
404 EulerAnglesClass my_eulers(motion,EulerOrderXYZr);
405 float ex = my_eulers.Get_Angle(0);
406 float ey = my_eulers.Get_Angle(1);
407 float ez = my_eulers.Get_Angle(2);
408
409 set_eulers(bindex,frame,ex,ey,ez);
410
411 /*
412 ** Store the visibility bit for this node
413 */
414 INode * node = tree->Get_Node(tindex);
415 bool vis;
416 if (node) {
417 vis = (node->GetVisibility(frametime) > 0.0f);
418 } else {
419 vis = 1;
420 }
421 set_visibility(bindex,frame,vis);
422
423 //
424 // Store out binary move or not
425 //
426 bool binary_move = false;
427
428 if ((node)&&(vis)) {
429
430 if (frame != 0) {
431 // sample previous frame, and an inbetween time
432 // to determine if there's a binary movement
433
434 TimeValue frametime_prev = frametime - GetTicksPerFrame();
435 TimeValue frametime_mid = (frametime + frametime_prev) / 2;
436
437 // if data at frametime_prev == data at frametime_mid and != data at frametime
438 // then we have a binary movement!
439
440 Control *c;
441
442 c = node->GetTMController()->GetPositionController();
443
444 if (c) {
445
446 Interval iValid;
447
448 Matrix3 smat1; // sample matrix 1
449 Matrix3 smat2; // sample matrix 2
450 Matrix3 smat3; // sample matrix 3
451
452 iValid = FOREVER;
453 smat1 = node->GetParentTM(frametime_prev);
454 c->GetValue(frametime_prev, &smat1, iValid, CTRL_RELATIVE);
455
456 iValid = FOREVER;
457 smat2 = node->GetParentTM(frametime_mid);
458 c->GetValue(frametime_mid, &smat2, iValid, CTRL_RELATIVE);
459
460 iValid = FOREVER;
461 smat3 = node->GetParentTM(frametime);
462 c->GetValue(frametime, &smat3, iValid, CTRL_RELATIVE);
463
464 if ((smat1 == smat2) && (!(smat2 == smat3))) {
465 binary_move = true;
466 DebugPrint(_T("Binary Move on Translation\n"));
467 }
468
469 if (false == binary_move) {
470 c = node->GetTMController()->GetRotationController();
471
472 if (c) {
473
474 iValid = FOREVER;
475 smat1 = node->GetParentTM(frametime_prev);
476 c->GetValue(frametime_prev, &smat1, iValid, CTRL_RELATIVE);
477
478 iValid = FOREVER;
479 smat2 = node->GetParentTM(frametime_mid);
480 c->GetValue(frametime_mid, &smat2, iValid, CTRL_RELATIVE);
481
482 iValid = FOREVER;
483 smat3 = node->GetParentTM(frametime);
484 c->GetValue(frametime, &smat3, iValid, CTRL_RELATIVE);
485
486 if ((smat1 == smat2) && (!(smat2 == smat3))) {
487 binary_move = true;
488 DebugPrint(_T("Binary Move on Rotation\n"));
489 }
490 }
491 }
492 }
493 }
494 }
495
496
497 set_binary_movement(bindex, frame, binary_move);
498
499 } // if(bindex!=-1)
500 }
501
502 /*
503 ** release allocated memory
504 */
505 delete tree;
506}
507
508
509/***********************************************************************************************
510 * MotionClass::set_motion_matrix -- store a motion matrix *
511 * *
512 * INPUT: *
513 * *
514 * OUTPUT: *
515 * *
516 * WARNINGS: *
517 * *
518 * HISTORY: *
519 * 10/26/1997 GH : Created. *
520 *=============================================================================================*/
521void MotionClass::set_motion_matrix(int node,int frame,const Matrix3 & motion)
522{
523 assert(node >= 0);
524 assert(frame >= 0);
525 assert(node < BasePose->Num_Nodes());
526 assert(frame < NumFrames);
527
528 MotionMatrix[node][frame] = motion;
529 NodeValidFlags[node] = 1;
530}
531
532
533/***********************************************************************************************
534 * MotionClass::get_motion_matrix -- retrieve a motion matrix *
535 * *
536 * INPUT: *
537 * *
538 * OUTPUT: *
539 * *
540 * WARNINGS: *
541 * *
542 * HISTORY: *
543 * 10/26/1997 GH : Created. *
544 *=============================================================================================*/
545Matrix3 MotionClass::get_motion_matrix(int node,int frame)
546{
547 assert(node >= 0);
548 assert(frame >= 0);
549 assert(node < BasePose->Num_Nodes());
550 assert(frame < NumFrames);
551
552 return MotionMatrix[node][frame];
553}
554
555
556/***********************************************************************************************
557 * MotionClass::set_eulers -- store euler angles *
558 * *
559 * INPUT: *
560 * *
561 * OUTPUT: *
562 * *
563 * WARNINGS: *
564 * *
565 * HISTORY: *
566 * 10/26/1997 GH : Created. *
567 *=============================================================================================*/
568void MotionClass::set_eulers(int node,int frame, float x, float y, float z)
569{
570 /*
571 ** if we're past the first frame, massage the euler angles to the
572 ** representation closest to the previous frame.
573 */
574 if (frame > 0) {
575
576 /*
577 ** First, compute equivalent euler angles
578 */
579 double x2 = PI + x;
580 double y2 = PI - y;
581 double z2 = PI + z;
582
583 if (x2 > PI) {
584 x2 = x2 - 2*PI;
585 }
586
587 if (y2 > PI) {
588 y2 = y2 - 2*PI;
589 }
590
591 if (z2 > PI) {
592 z2 = z2 - 2*PI;
593 }
594
595 /*
596 ** load up the previous frame eulers
597 */
598 double px,py,pz;
599 px = get_eulers(node,frame - 1)[0];
600 py = get_eulers(node,frame - 1)[1];
601 pz = get_eulers(node,frame - 1)[2];
602
603 // now, pick between the two
604 double mag0 = (x - px) * (x - px) + (y - py) * (y - py) + (z - pz) * (z - pz);
605 double mag1 = (x2 - px) * (x2 - px) + (y2 - py) * (y2 - py) + (z2 - pz) * (z2 - pz);
606
607 if (mag1 < mag0) {
608 x = x2;
609 y = y2;
610 z = z2;
611 }
612 }
613
614 EulerDelta[node][frame].x = x;
615 EulerDelta[node][frame].y = y;
616 EulerDelta[node][frame].z = z;
617 NodeValidFlags[node] = 1;
618}
619
620
621/***********************************************************************************************
622 * MotionClass::get_eulers -- retrieve euler angles *
623 * *
624 * INPUT: *
625 * *
626 * OUTPUT: *
627 * *
628 * WARNINGS: *
629 * *
630 * HISTORY: *
631 * 10/26/1997 GH : Created. *
632 *=============================================================================================*/
633Point3 MotionClass::get_eulers(int node,int frame)
634{
635 return Point3(
636 EulerDelta[node][frame].x,
637 EulerDelta[node][frame].y,
638 EulerDelta[node][frame].z
639 );
640}
641
642
643/***********************************************************************************************
644 * MotionClass::set_visibility -- store a visibility bit *
645 * *
646 * INPUT: *
647 * *
648 * OUTPUT: *
649 * *
650 * WARNINGS: *
651 * *
652 * HISTORY: *
653 * 1/15/98 GTH : Created. *
654 *=============================================================================================*/
655void MotionClass::set_visibility(int node,int frame,bool visible)
656{
657 VisData[node][frame] = visible;
658 NodeValidFlags[node] = 1;
659}
660
661
662/***********************************************************************************************
663 * MotionClass::get_visibility -- retrieve the visibility bit for this node:frame *
664 * *
665 * INPUT: *
666 * *
667 * OUTPUT: *
668 * *
669 * WARNINGS: *
670 * *
671 * HISTORY: *
672 * 1/15/98 GTH : Created. *
673 *=============================================================================================*/
674bool MotionClass::get_visibility(int node,int frame)
675{
676 return VisData[node][frame];
677}
678
679
680/***********************************************************************************************
681 * MotionClass::set_binary_movement -- store a binary movement bit *
682 * *
683 * INPUT: *
684 * *
685 * OUTPUT: *
686 * *
687 * WARNINGS: *
688 * *
689 * HISTORY: *
690 * 1/15/98 GTH : Created. *
691 *=============================================================================================*/
692void MotionClass::set_binary_movement(int node,int frame,bool visible)
693{
694 BinMoveData[node][frame] = visible;
695 //NodeValidFlags[node] = 1;
696}
697
698
699/***********************************************************************************************
700 * MotionClass::get_visibility -- retrieve the movement bit for this node:frame *
701 * *
702 * INPUT: *
703 * *
704 * OUTPUT: *
705 * *
706 * WARNINGS: *
707 * *
708 * HISTORY: *
709 * 1/15/98 GTH : Created. *
710 *=============================================================================================*/
711bool MotionClass::get_binary_movement(int node,int frame)
712{
713 return BinMoveData[node][frame];
714}
715
716
717
718/***********************************************************************************************
719 * MotionClass::Save -- save the motion to a W3D file *
720 * *
721 * INPUT: *
722 * *
723 * OUTPUT: *
724 * *
725 * WARNINGS: *
726 * *
727 * HISTORY: *
728 * 10/26/1997 GH : Created. *
729 *=============================================================================================*/
731{
732 uint32 chunk_anim_type = W3D_CHUNK_ANIMATION;
733
734 ExportLog::printf("\nBegin Save Motion Data\n");
735
736 if (CompressAnimation) {
737 chunk_anim_type = W3D_CHUNK_COMPRESSED_ANIMATION;
738 }
739
740 if (!csave.Begin_Chunk( chunk_anim_type )) {
741 return false;
742 }
743
744 if (!save_header(csave)) {
745 return false;
746 }
747
748 if (!save_channels(csave)) {
749 return false;
750 }
751
752 if (!csave.End_Chunk()) {
753 return false;
754 }
755
756 return true;
757}
758
759
760/***********************************************************************************************
761 * MotionClass::save_header -- save the header *
762 * *
763 * INPUT: *
764 * *
765 * OUTPUT: *
766 * *
767 * WARNINGS: *
768 * *
769 * HISTORY: *
770 * 10/26/1997 GH : Created. *
771 *=============================================================================================*/
772bool MotionClass::save_header(ChunkSaveClass & csave)
773{
774
775 ExportLog::printf("Save Header Type: ");
776
777 if (CompressAnimation) {
778 // New Compressed Style
780 return false;
781 }
782
783 W3dCompressedAnimHeaderStruct aheader;
785 Set_W3D_Name(aheader.Name,Name);
786 Set_W3D_Name(aheader.HierarchyName,BasePose->Get_Name());
787 aheader.NumFrames = NumFrames;
788 aheader.FrameRate = FrameRate;
789 aheader.Flavor = CompressAnimationFlavor; // for future expansion
790
791 switch (CompressAnimationFlavor) {
792
794 ExportLog::printf("TimeCoded\n");
795 break;
797 ExportLog::printf("Adaptive Delta\n");
798 break;
799 default:
800 ExportLog::printf("UNKNOWN\n");
801 break;
802 }
803
804 if (csave.Write(&aheader,sizeof(aheader)) != sizeof(aheader)) {
805 return false;
806 }
807
808 if (!csave.End_Chunk()) {
809 return false;
810 }
811 }
812 else {
813
814 ExportLog::printf("Non-Compressed.\n");
815
816 // Classic Non-Compressed Style
818 return false;
819 }
820
821 W3dAnimHeaderStruct aheader;
823 Set_W3D_Name(aheader.Name,Name);
824 Set_W3D_Name(aheader.HierarchyName,BasePose->Get_Name());
825 aheader.NumFrames = NumFrames;
826 aheader.FrameRate = FrameRate;
827
828 if (csave.Write(&aheader,sizeof(aheader)) != sizeof(aheader)) {
829 return false;
830 }
831
832 if (!csave.End_Chunk()) {
833 return false;
834 }
835 }
836
837
838 return true;
839}
840
841/***********************************************************************************************
842 * MotionClass::save_channels -- save the motion channels *
843 * *
844 * INPUT: *
845 * *
846 * OUTPUT: *
847 * *
848 * WARNINGS: *
849 * *
850 * HISTORY: *
851 * 10/26/1997 GH : Created. *
852 *=============================================================================================*/
853bool MotionClass::save_channels(ChunkSaveClass & csave)
854{
855
856 int NumNodes = BasePose->Num_Nodes();
857
858 ExportLog::printf("\nSaving Channel Data for %d Nodes\n", NumNodes);
859
860 for (int nodeidx = 0; nodeidx < BasePose->Num_Nodes(); nodeidx++) {
861
862 ExportLog::printf("\nnode: %d ", nodeidx);
863
864 /*
865 ** Just ignore this node if it didn't appear in the max scene.
866 */
867 if (NodeValidFlags[nodeidx]) {
868
869 float identity[] = { 0.0f,0.0f,0.0f,1.0f };
870
871 VectorChannelClass xchan (nodeidx, NumFrames, ANIM_CHANNEL_X, 1, identity);
872 VectorChannelClass ychan (nodeidx, NumFrames, ANIM_CHANNEL_Y, 1, identity);
873 VectorChannelClass zchan (nodeidx, NumFrames, ANIM_CHANNEL_Z, 1, identity);
874 VectorChannelClass xrchan(nodeidx, NumFrames, ANIM_CHANNEL_XR, 1, identity);
875 VectorChannelClass yrchan(nodeidx, NumFrames, ANIM_CHANNEL_YR, 1, identity);
876 VectorChannelClass zrchan(nodeidx, NumFrames, ANIM_CHANNEL_ZR, 1, identity);
877 VectorChannelClass qchan (nodeidx, NumFrames, ANIM_CHANNEL_Q, 4, identity);
878
879 xchan.SetSaveOptions(CompressAnimation, CompressAnimationFlavor, CompressAnimationTranslationError, CompressAnimationRotationError, ReduceAnimation, ReduceAnimationPercent);
880 ychan.SetSaveOptions(CompressAnimation, CompressAnimationFlavor, CompressAnimationTranslationError, CompressAnimationRotationError, ReduceAnimation, ReduceAnimationPercent);
881 zchan.SetSaveOptions(CompressAnimation, CompressAnimationFlavor, CompressAnimationTranslationError, CompressAnimationRotationError, ReduceAnimation, ReduceAnimationPercent);
882 xrchan.SetSaveOptions(CompressAnimation, CompressAnimationFlavor, CompressAnimationTranslationError, CompressAnimationRotationError, ReduceAnimation, ReduceAnimationPercent);
883 yrchan.SetSaveOptions(CompressAnimation, CompressAnimationFlavor, CompressAnimationTranslationError, CompressAnimationRotationError, ReduceAnimation, ReduceAnimationPercent);
884 zrchan.SetSaveOptions(CompressAnimation, CompressAnimationFlavor, CompressAnimationTranslationError, CompressAnimationRotationError, ReduceAnimation, ReduceAnimationPercent);
885 qchan.SetSaveOptions(CompressAnimation, CompressAnimationFlavor, CompressAnimationTranslationError, CompressAnimationRotationError, ReduceAnimation, ReduceAnimationPercent);
886
887 BitChannelClass vischan(nodeidx, NumFrames, BIT_CHANNEL_VIS, 1);
888 vischan.Set_Bits(VisData[nodeidx]);
889
890 BitChannelClass binmovechan(nodeidx, NumFrames, 0, 0);
891 binmovechan.Set_Bits(BinMoveData[nodeidx]);
892
893 for (int frameidx = 0; frameidx < NumFrames; frameidx++) {
894
895 float vec[4];
896 Matrix3 tm = get_motion_matrix(nodeidx,frameidx);
897 Point3 eulers = get_eulers(nodeidx,frameidx);
898
899
900 Point3 old_tran = tm.GetTrans();
901 Quat old_rot(tm);
902
903 Point3 tran;
904 Point3 scale;
905 Quat rot;
906
907 DecomposeMatrix(tm,tran,rot,scale);
908
909 /*
910 ** fixup the quaternion - max's quaternions are different than mine(?)
911 */
912 rot[0] = -rot[0];
913 rot[1] = -rot[1];
914 rot[2] = -rot[2];
915 rot[3] = rot[3];
916
917 /*
918 ** Build the x translation channel
919 */
920 vec[0] = tran.x;
921 xchan.Set_Vector(frameidx,vec);
922
923 /*
924 ** Build the y translation channel
925 */
926 vec[0] = tran.y;
927 ychan.Set_Vector(frameidx,vec);
928
929 /*
930 ** Build the z translation channel
931 */
932 vec[0] = tran.z;
933 zchan.Set_Vector(frameidx,vec);
934
935 /*
936 ** Build the x rotation channel
937 */
938 vec[0] = eulers.x;
939 xrchan.Set_Vector(frameidx,vec);
940
941 /*
942 ** Build the y rotation channel
943 */
944 vec[0] = eulers.y;
945 yrchan.Set_Vector(frameidx,vec);
946
947 /*
948 ** Build the z rotation channel
949 */
950 vec[0] = eulers.z;
951 zrchan.Set_Vector(frameidx,vec);
952
953 /*
954 ** Build the quaternion rotation channel
955 */
956 vec[0] = rot[0];
957 vec[1] = rot[1];
958 vec[2] = rot[2];
959 vec[3] = rot[3];
960
961 qchan.Set_Vector(frameidx,vec);
962
963 /*
964 ** build the visibility channel
965 */
966 vischan.Set_Bit(frameidx,get_visibility(nodeidx,frameidx));
967 //
968 // build binarymovement channel
969 //
970 binmovechan.Set_Bit(frameidx, get_binary_movement(nodeidx, frameidx));
971 }
972
973 // If objects arn't visible, then the channel data may as well be empty
974
975 if (!vischan.Is_Empty()) {
976
977 if (!xchan.Is_Empty()) xchan.ClearInvisibleData(&vischan);
978 if (!ychan.Is_Empty()) ychan.ClearInvisibleData(&vischan);
979 if (!zchan.Is_Empty()) zchan.ClearInvisibleData(&vischan);
980 if (!qchan.Is_Empty()) qchan.ClearInvisibleData(&vischan);
981
982 }
983
984 if (!xchan.Is_Empty()) {
986 xchan.Save(csave, &binmovechan );
987 }
988 if (!ychan.Is_Empty()) {
990 ychan.Save(csave, &binmovechan );
991 }
992 if (!zchan.Is_Empty()) {
994 zchan.Save(csave, &binmovechan );
995 }
996
997
998 // (gth) not saving Euler angles any more since we don't use them
999// if (!xrchan.Is_Empty()) xrchan.Save(csave);
1000// if (!yrchan.Is_Empty()) yrchan.Save(csave);
1001// if (!zrchan.Is_Empty()) zrchan.Save(csave);
1002
1003 if (!qchan.Is_Empty()) {
1004 ExportLog::printf("q");
1005 qchan.Save(csave, &binmovechan);
1006 }
1007
1008 if (!vischan.Is_Empty()) {
1009 ExportLog::printf("v");
1010 vischan.Save(csave, CompressAnimation);
1011 }
1012 }
1013
1014 ExportLog::updatebar(nodeidx ,NumNodes);
1015
1016 }
1017
1019
1020 ExportLog::printf("\n\nSave Channel Data Complete.\n");
1021
1022 return true;
1023}
1024
1025// EOF - motion.cpp
1026
1027
#define NULL
Definition BaseType.h:92
#define PI
Definition BaseType.h:86
#define DebugPrint
Definition DebugPrint.h:60
@ FOREVER
Definition GameCommon.h:196
Color scale(const Color &a, const Color &b)
Definition GameMtl.cpp:722
#define W3D_CURRENT_HANIM_VERSION
Definition w3d_file.h:1386
@ ANIM_FLAVOR_TIMECODED
Definition w3d_file.h:1437
@ ANIM_FLAVOR_ADAPTIVE_DELTA
Definition w3d_file.h:1438
#define W3D_CURRENT_COMPRESSED_HANIM_VERSION
Definition w3d_file.h:1387
@ W3D_CHUNK_COMPRESSED_ANIMATION_HEADER
Definition w3d_file.h:407
@ W3D_CHUNK_COMPRESSED_ANIMATION
Definition w3d_file.h:406
@ W3D_CHUNK_ANIMATION_HEADER
Definition w3d_file.h:402
@ W3D_CHUNK_ANIMATION
Definition w3d_file.h:401
@ 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
@ BIT_CHANNEL_VIS
Definition w3d_file.h:1458
unsigned long uint32
Definition bittype.h:46
int EulerOrderXYZr
Definition euler.cpp:107
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 rprintf(char *,...)
static void printf(char *,...)
static void updatebar(float position, float total)
Matrix3 Get_Node_Relative_Transform(int node) const
Definition hiersave.h:124
int Num_Nodes(void) const
Definition hiersave.h:113
const char * Get_Node_Name(int node) const
Definition hiersave.cpp:362
INode * Get_Node(int node) const
Definition hiersave.cpp:342
~MotionClass(void)
Definition motion.cpp:295
bool Save(ChunkSaveClass &csave)
Definition motion.cpp:730
MotionClass(IScene *scene, INode *rootnode, HierarchySaveClass *basepose, W3dExportOptionsStruct &options, int framerate, Progress_Meter_Class *meter, HWND MaxHwnd, char *name, Matrix3 &offset=Matrix3(1))
Definition motion.cpp:77
WWINLINE Quaternion Inverse(const Quaternion &a)
Definition quat.h:117
char HierarchyName[W3D_NAME_LEN]
Definition w3d_file.h:1394
char Name[W3D_NAME_LEN]
Definition w3d_file.h:1393
char Name[W3D_NAME_LEN]
Definition w3d_file.h:1403
char HierarchyName[W3D_NAME_LEN]
Definition w3d_file.h:1404
Matrix3 Cleanup_Orthogonal_Matrix(Matrix3 &mat)
Definition util.cpp:79
void Set_W3D_Name(char *set_name, const char *src)
Definition util.cpp:112