motion.cpp 41 KB

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