hiersave.cpp 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. /*
  2. ** Command & Conquer Renegade(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/hiersave.cpp 57 4/03/02 9:38a Moumine_ballo $ */
  19. /***********************************************************************************************
  20. *** Confidential - Westwood Studios ***
  21. ***********************************************************************************************
  22. * *
  23. * Project Name : Commando / G 3D Engine *
  24. * *
  25. * $Archive:: /Commando/Code/Tools/max2w3d/hiersave.cpp $*
  26. * *
  27. * $Author:: Moumine_ballo $*
  28. * *
  29. * $Modtime:: 3/18/02 6:01p $*
  30. * *
  31. * $Revision:: 57 $*
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * HierarchySaveClass::HierarchySaveClass -- constructor *
  36. * HierarchySaveClass::HierarchySaveClass -- constructor *
  37. * HierarchySaveClass::HierarchySaveClass -- constructor *
  38. * HierarchySaveClass::~HierarchySaveClass -- destructor *
  39. * HierarchySaveClass::Free -- releases all allocated memory *
  40. * HierarchySaveClass::Get_Node_Transform -- returns the transformation matrix of specified n*
  41. * HierarchySaveClass::get_relative_transform -- retruns tm between this node and its parent *
  42. * HierarchySaveClass::Get_Name -- returns the name of this hierarchy *
  43. * HierarchySaveClass::Get_Node -- Get the Max INode *
  44. * HierarchySaveClass::Get_Node_Name -- returns name of this hierarchy node *
  45. * HierarchySaveClass::Find_Named_Node -- returns index of a named node *
  46. * HierarchySaveClass::Get_Export_Coordinate_System - find the bone and coordinate system *
  47. * HierarchySaveClass::Save -- write the hierarchy into a W3D file *
  48. * HierarchySaveClass::Load -- read the hierarchy from a W3D file *
  49. * HierarchySaveClass::add_tree -- adds a node and all of its children *
  50. * HierarchySaveClass::add_node -- adds a single node to the tree *
  51. * HierarchySaveClass::Get_Fixup_Transform -- gets the "fixup" transform for a node *
  52. * HierarchySaveClass::fixup_matrix -- conditions a matrix *
  53. * HierarchySaveClass::save_header -- writes the header into a W3D file *
  54. * HierarchySaveClass::save_pivots -- writes the pivots into a W3D file *
  55. * HierarchySaveClass::save_fixups -- writes the fixup transforms into a W3D file *
  56. * HierarchySaveClass::load_header -- reads the header from a W3D file *
  57. * HierarchySaveClass::load_pivots -- reads the pivots from a W3D file *
  58. * HierarchySaveClass::load_fixups -- reads the fixup transforms from a W3D file *
  59. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  60. #include "hiersave.h"
  61. #include "w3d_file.h"
  62. #include "nodefilt.h"
  63. #include "euler.h"
  64. #include "util.h"
  65. #include "w3dappdata.h"
  66. #include "errclass.h"
  67. #include "exportlog.h"
  68. #include "resource.h"
  69. bool HierarchySaveClass::TerrainModeEnabled = false;
  70. extern HINSTANCE AppInstance;
  71. /***********************************************************************************************
  72. * HierarchySaveClass::HierarchySaveClass -- constructor *
  73. * *
  74. * INPUT: *
  75. * root - root INode to construct the HTree from *
  76. * time - current time in Max, transforms at this time will be used *
  77. * treemeter - progress meter *
  78. * hname - name for the hierarchy tree *
  79. * fixup_type - can be used to force all transforms to be translation only *
  80. * fixuptree - htree loaded from a previous export *
  81. * *
  82. * OUTPUT: *
  83. * *
  84. * WARNINGS: *
  85. * *
  86. * HISTORY: *
  87. * 10/26/1997 GH : Created. *
  88. *=============================================================================================*/
  89. HierarchySaveClass::HierarchySaveClass
  90. (
  91. INode * root,
  92. TimeValue time,
  93. Progress_Meter_Class & treemeter,
  94. char * hname,
  95. int fixuptype,
  96. HierarchySaveClass * fixuptree
  97. ) :
  98. Node(DEFAULT_NODE_ARRAY_SIZE),
  99. CurNode(0),
  100. FixupType(fixuptype),
  101. FixupTree(fixuptree)
  102. {
  103. CurNode = 0;
  104. CurTime = time;
  105. /*
  106. ** This code-path is activated when the user has created a custom origin. In this case, we
  107. ** need to compute the transform which will make all bones relative to this origin.
  108. */
  109. OriginOffsetTransform = Inverse(root->GetNodeTM(CurTime));
  110. /*
  111. ** Build our tree from the given tree of nodes
  112. */
  113. int rootidx = add_node(NULL,-1);
  114. assert(rootidx == 0);
  115. add_tree(root,rootidx);
  116. HierarchyHeader.Version = W3D_CURRENT_HTREE_VERSION;
  117. Set_W3D_Name(HierarchyHeader.Name,hname);
  118. HierarchyHeader.NumPivots = CurNode;
  119. HierarchyHeader.Center.X = 0.0f;
  120. HierarchyHeader.Center.Y = 0.0f;
  121. HierarchyHeader.Center.Z = 0.0f;
  122. }
  123. /***********************************************************************************************
  124. * HierarchySaveClass::HierarchySaveClass -- constructor *
  125. * *
  126. * INPUT: *
  127. * *
  128. * rootlist - list of root nodes to add to the htree *
  129. * time - current time in Max, transforms at this time will be used *
  130. * treemeter - progress meter *
  131. * hname - name for the hierarchy tree *
  132. * fixup_type - can be used to force all transforms to be translation only *
  133. * fixuptree - htree loaded from a previous export *
  134. * origin_offset - origin offset transform *
  135. * *
  136. * OUTPUT: *
  137. * *
  138. * WARNINGS: *
  139. * *
  140. * HISTORY: *
  141. * 10/26/1997 GH : Created. *
  142. *=============================================================================================*/
  143. HierarchySaveClass::HierarchySaveClass
  144. (
  145. INodeListClass * rootlist,
  146. TimeValue time,
  147. Progress_Meter_Class & treemeter,
  148. char * hname,
  149. int fixuptype,
  150. HierarchySaveClass * fixuptree,
  151. const Matrix3 & origin_offset
  152. ) :
  153. Node(DEFAULT_NODE_ARRAY_SIZE),
  154. CurNode(0),
  155. FixupType(fixuptype),
  156. FixupTree(fixuptree),
  157. OriginOffsetTransform(origin_offset)
  158. {
  159. CurNode = 0;
  160. CurTime = time;
  161. /*
  162. ** Build the tree with all leaves of all of the nodes given
  163. */
  164. int rootidx = add_node(NULL,-1);
  165. assert(rootidx == 0);
  166. for (unsigned int i = 0; i < rootlist->Num_Nodes(); i++) {
  167. add_tree((*rootlist)[i],rootidx);
  168. }
  169. HierarchyHeader.Version = W3D_CURRENT_HTREE_VERSION;
  170. Set_W3D_Name(HierarchyHeader.Name,hname);
  171. HierarchyHeader.NumPivots = CurNode;
  172. HierarchyHeader.Center.X = 0.0f;
  173. HierarchyHeader.Center.Y = 0.0f;
  174. HierarchyHeader.Center.Z = 0.0f;
  175. }
  176. /***********************************************************************************************
  177. * HierarchySaveClass::HierarchySaveClass -- constructor *
  178. * *
  179. * INPUT: *
  180. * *
  181. * OUTPUT: *
  182. * *
  183. * WARNINGS: *
  184. * *
  185. * HISTORY: *
  186. * 10/26/1997 GH : Created. *
  187. *=============================================================================================*/
  188. HierarchySaveClass::HierarchySaveClass():
  189. Node(NULL),
  190. CurNode(0),
  191. CurTime(0)
  192. {
  193. }
  194. /***********************************************************************************************
  195. * HierarchySaveClass::~HierarchySaveClass -- destructor *
  196. * *
  197. * INPUT: *
  198. * *
  199. * OUTPUT: *
  200. * *
  201. * WARNINGS: *
  202. * *
  203. * HISTORY: *
  204. * 10/26/1997 GH : Created. *
  205. *=============================================================================================*/
  206. HierarchySaveClass::~HierarchySaveClass(void)
  207. {
  208. Free();
  209. }
  210. /***********************************************************************************************
  211. * HierarchySaveClass::Free -- releases all allocated memory *
  212. * *
  213. * INPUT: *
  214. * *
  215. * OUTPUT: *
  216. * *
  217. * WARNINGS: *
  218. * *
  219. * HISTORY: *
  220. * 10/26/1997 GH : Created. *
  221. *=============================================================================================*/
  222. void HierarchySaveClass::Free(void)
  223. {
  224. Node.Clear();
  225. }
  226. /***********************************************************************************************
  227. * HierarchySaveClass::Get_Node_Transform -- returns the transformation matrix of specified no *
  228. * *
  229. * INPUT: *
  230. * *
  231. * OUTPUT: *
  232. * *
  233. * WARNINGS: *
  234. * *
  235. * HISTORY: *
  236. * 10/26/1997 GH : Created. *
  237. *=============================================================================================*/
  238. Matrix3 HierarchySaveClass::Get_Node_Transform(int nodeidx) const
  239. {
  240. Matrix3 tm(1);
  241. int idx = nodeidx;
  242. while (idx != -1) {
  243. tm = tm * get_relative_transform(idx);
  244. idx = Node[idx].Pivot.ParentIdx;
  245. }
  246. return tm;
  247. }
  248. /***********************************************************************************************
  249. * HierarchySaveClass::get_relative_transform -- retruns tm between this node and its parent *
  250. * *
  251. * INPUT: *
  252. * *
  253. * OUTPUT: *
  254. * *
  255. * WARNINGS: *
  256. * *
  257. * HISTORY: *
  258. * 10/26/1997 GH : Created. *
  259. *=============================================================================================*/
  260. Matrix3 HierarchySaveClass::get_relative_transform(int nodeidx) const
  261. {
  262. assert(nodeidx >= 0);
  263. assert(nodeidx < CurNode);
  264. Point3 trans;
  265. Quat rot;
  266. Matrix3 tm(true);
  267. Matrix3 rtm(true);
  268. trans.x = Node[nodeidx].Pivot.Translation.X;
  269. trans.y = Node[nodeidx].Pivot.Translation.Y;
  270. trans.z = Node[nodeidx].Pivot.Translation.Z;
  271. // WARNING! I had to fudge the orientation
  272. // quaternion (Max's representation seems to
  273. // rotate in the opposite sense as mine...)
  274. rot[0] = -Node[nodeidx].Pivot.Rotation.Q[0];
  275. rot[1] = -Node[nodeidx].Pivot.Rotation.Q[1];
  276. rot[2] = -Node[nodeidx].Pivot.Rotation.Q[2];
  277. rot[3] = Node[nodeidx].Pivot.Rotation.Q[3];
  278. tm.Translate(trans);
  279. rot.MakeMatrix(rtm);
  280. tm = rtm * tm;
  281. return tm;
  282. }
  283. /***********************************************************************************************
  284. * HierarchySaveClass::Get_Name -- returns the name of this hierarchy *
  285. * *
  286. * INPUT: *
  287. * *
  288. * OUTPUT: *
  289. * *
  290. * WARNINGS: *
  291. * *
  292. * HISTORY: *
  293. * 10/26/1997 GH : Created. *
  294. *=============================================================================================*/
  295. const char * HierarchySaveClass::Get_Name(void) const
  296. {
  297. return HierarchyHeader.Name;
  298. }
  299. /***********************************************************************************************
  300. * HierarchySaveClass::Get_Node -- Get the Max INode *
  301. * *
  302. * INPUT: *
  303. * *
  304. * OUTPUT: *
  305. * *
  306. * WARNINGS: *
  307. * *
  308. * HISTORY: *
  309. * 1/15/98 GTH : Created. *
  310. *=============================================================================================*/
  311. INode * HierarchySaveClass::Get_Node(int node) const
  312. {
  313. assert(node >= 0);
  314. assert(node < CurNode);
  315. return Node[node].MaxNode;
  316. }
  317. /***********************************************************************************************
  318. * HierarchySaveClass::Get_Node_Name -- returns name of this hierarchy node *
  319. * *
  320. * INPUT: *
  321. * *
  322. * OUTPUT: *
  323. * *
  324. * WARNINGS: *
  325. * *
  326. * HISTORY: *
  327. * 10/26/1997 GH : Created. *
  328. *=============================================================================================*/
  329. const char * HierarchySaveClass::Get_Node_Name(int node) const
  330. {
  331. assert(node >= 0);
  332. assert(node < CurNode);
  333. return Node[node].Pivot.Name;
  334. }
  335. /***********************************************************************************************
  336. * HierarchySaveClass::Find_Named_Node -- returns index of a named node *
  337. * *
  338. * INPUT: *
  339. * *
  340. * OUTPUT: *
  341. * *
  342. * WARNINGS: *
  343. * *
  344. * HISTORY: *
  345. * 10/26/1997 GH : Created. *
  346. *=============================================================================================*/
  347. int HierarchySaveClass::Find_Named_Node(const char * name) const
  348. {
  349. int match = -1;
  350. for (int index=0; index<CurNode; index++) {
  351. if (strcmp(Node[index].Pivot.Name,name) == 0) {
  352. match = index;
  353. }
  354. }
  355. return match;
  356. }
  357. /***********************************************************************************************
  358. * HierarchySaveClass::Get_Export_Coordinate_System - find the bone and coordinate system *
  359. * for the given object *
  360. * *
  361. * INPUT: *
  362. * *
  363. * OUTPUT: *
  364. * *
  365. * WARNINGS: *
  366. * *
  367. * HISTORY: *
  368. * 10/17/2000 gth : Created. *
  369. *=============================================================================================*/
  370. void HierarchySaveClass::Get_Export_Coordinate_System
  371. (
  372. INode * node,
  373. int * set_bone_index,
  374. INode ** set_bone_node,
  375. Matrix3 * set_transform
  376. )
  377. {
  378. /*
  379. ** find the first parent of this node which
  380. ** is in the base pose. Note, we're finding the parent bone
  381. ** in our hierarchy with the same name as a bone in the "main"
  382. ** hierarchy. When we're exporting LOD models, there are multiple
  383. ** hierarchies...
  384. */
  385. bool done = false;
  386. int boneidx = -1;
  387. INode * pbone = node;
  388. while (!done) {
  389. char name[W3D_NAME_LEN];
  390. Set_W3D_Name(name,pbone->GetName());
  391. boneidx = Find_Named_Node(name);
  392. if (boneidx != -1) {
  393. /*
  394. ** We found the parent bone!
  395. */
  396. done = true;
  397. } else if (Is_Origin(pbone)) {
  398. /*
  399. ** Don't go up past our origin, use this as our bone.
  400. */
  401. boneidx = 0;
  402. done = true;
  403. } else {
  404. /*
  405. ** Nope, try the next parent
  406. */
  407. pbone = pbone->GetParentNode();
  408. assert(pbone != NULL);
  409. #if 0
  410. if (pbone == NULL) {
  411. /*
  412. ** mesh isn't connected to a bone, use the root
  413. */
  414. boneidx = 0;
  415. pbone = node;
  416. done = true;
  417. }
  418. #endif
  419. }
  420. }
  421. if (set_bone_index != NULL) {
  422. *set_bone_index = boneidx;
  423. }
  424. if (set_bone_node != NULL) {
  425. *set_bone_node = pbone;
  426. }
  427. if (set_transform != NULL) {
  428. *set_transform = Get_Fixup_Transform(boneidx) * pbone->GetNodeTM(CurTime);
  429. }
  430. }
  431. /***********************************************************************************************
  432. * HierarchySaveClass::Save -- write the hierarchy into a W3D file *
  433. * *
  434. * INPUT: *
  435. * *
  436. * OUTPUT: *
  437. * *
  438. * WARNINGS: *
  439. * *
  440. * HISTORY: *
  441. * 10/26/1997 GH : Created. *
  442. *=============================================================================================*/
  443. bool HierarchySaveClass::Save(ChunkSaveClass & csave)
  444. {
  445. ExportLog::printf("\nSaving Hierarchy Tree %s.\n",HierarchyHeader.Name);
  446. ExportLog::printf("Node Count: %d\n",CurNode);
  447. ExportLog::printf("Nodes: \n");
  448. for (int inode = 0; inode < CurNode; inode++) {
  449. ExportLog::printf(" %s\n",Node[inode].Pivot.Name);
  450. }
  451. if (!csave.Begin_Chunk(W3D_CHUNK_HIERARCHY)) {
  452. return false;
  453. }
  454. if (!save_header(csave)) {
  455. return false;
  456. }
  457. if (!save_pivots(csave)) {
  458. return false;
  459. }
  460. if (!save_fixups(csave)) {
  461. return false;
  462. }
  463. if (!csave.End_Chunk()) {
  464. return false;
  465. }
  466. return true;
  467. }
  468. /***********************************************************************************************
  469. * HierarchySaveClass::Load -- read the hierarchy from a W3D file *
  470. * *
  471. * INPUT: *
  472. * *
  473. * OUTPUT: *
  474. * *
  475. * WARNINGS: *
  476. * *
  477. * HISTORY: *
  478. * 10/26/1997 GH : Created. *
  479. *=============================================================================================*/
  480. bool HierarchySaveClass::Load(ChunkLoadClass & cload)
  481. {
  482. Free();
  483. bool error = false;
  484. while (cload.Open_Chunk()) {
  485. switch (cload.Cur_Chunk_ID()) {
  486. case W3D_CHUNK_HIERARCHY_HEADER:
  487. if (!load_header(cload)) error = true;
  488. break;
  489. case W3D_CHUNK_PIVOTS:
  490. if (!load_pivots(cload)) error = true;
  491. break;
  492. case W3D_CHUNK_PIVOT_FIXUPS:
  493. if (!load_fixups(cload)) error = true;
  494. break;
  495. default:
  496. break;
  497. }
  498. if (!cload.Close_Chunk() || error) {
  499. return false;
  500. }
  501. }
  502. CurNode = HierarchyHeader.NumPivots;
  503. return true;
  504. }
  505. /***********************************************************************************************
  506. * HierarchySaveClass::add_tree -- adds a node and all of its children *
  507. * *
  508. * INPUT: *
  509. * *
  510. * OUTPUT: *
  511. * *
  512. * WARNINGS: *
  513. * *
  514. * HISTORY: *
  515. * 10/26/1997 GH : Created. *
  516. *=============================================================================================*/
  517. void HierarchySaveClass::add_tree(INode * node,int pidx)
  518. {
  519. int nextparent;
  520. if (node->IsHidden ()) {
  521. // if the node is hidden, do not add it but add its children to the current parent.
  522. nextparent = pidx;
  523. } else if (TerrainModeEnabled && (Is_Normal_Mesh(node) || Is_Null_Object(node))) {
  524. // terrain optimization, normal meshes are not allowed to have transforms
  525. nextparent = pidx;
  526. } else if (!Is_Bone(node)) {
  527. // This node isn't a bone, don't add it
  528. nextparent = pidx;
  529. } else {
  530. // Add new pivot! it will be parent of all below it.
  531. nextparent = add_node(node,pidx);
  532. }
  533. // Add all of this nodes children
  534. for (int i=0; i < node->NumberOfChildren(); i++) {
  535. add_tree(node->GetChildNode(i),nextparent);
  536. }
  537. }
  538. /***********************************************************************************************
  539. * HierarchySaveClass::add_node -- adds a single node to the tree *
  540. * *
  541. * INPUT: *
  542. * *
  543. * OUTPUT: *
  544. * *
  545. * WARNINGS: *
  546. * *
  547. * HISTORY: *
  548. * 10/26/1997 GH : Created. *
  549. *=============================================================================================*/
  550. int HierarchySaveClass::add_node(INode * node,int pidx)
  551. {
  552. /*
  553. ** 'grow' the node array if necessary
  554. */
  555. if (CurNode >= Node.Length ()) {
  556. Node.Resize (Node.Length () + NODE_ARRAY_GROWTH_SIZE);
  557. }
  558. /*
  559. ** setup the pivot
  560. */
  561. Node[CurNode].MaxNode = node;
  562. Node[CurNode].Pivot.ParentIdx = pidx;
  563. if (node) {
  564. Set_W3D_Name(Node[CurNode].Pivot.Name,node->GetName());
  565. } else {
  566. Set_W3D_Name(Node[CurNode].Pivot.Name,"RootTransform");
  567. }
  568. /*
  569. ** Now, check if there is a bone with this W3D name already
  570. ** if there is, scold the user and bail out
  571. */
  572. if (Find_Named_Node(Node[CurNode].Pivot.Name) != -1) {
  573. char buf[MAX_PATH];
  574. char format[MAX_PATH];
  575. LoadString(AppInstance, IDS_ERROR_DUPNAMES, format, MAX_PATH);
  576. sprintf(buf,format,W3D_NAME_LEN,Node[CurNode].Pivot.Name);
  577. throw ErrorClass(buf);
  578. }
  579. //Moumine 3/18/2002 5:01:10 PM
  580. DebugPrint("\nadding node %s\n",Node[CurNode].Pivot.Name);
  581. //End Moumine
  582. /*
  583. ** Compute the transformation for this node
  584. */
  585. Matrix3 maxnodeTM(1);
  586. Matrix3 ournodeTM(1);
  587. Matrix3 fixupTM(1);
  588. Point3 trans(0,0,0);
  589. Quat rot(1);
  590. Point3 scale(1,1,1);
  591. if (node) {
  592. maxnodeTM = node->GetNodeTM(CurTime) * OriginOffsetTransform;
  593. } else {
  594. maxnodeTM = Matrix3(1);
  595. }
  596. /*
  597. ** If this tree is being "fixed up" the first thing we do
  598. ** is to transform Max's nodeTM by the fixup transform.
  599. ** This is done when a base pose was created using our own
  600. ** types of transforms and we want to apply the same
  601. ** changes to this tree.
  602. **
  603. ** Note that if FixupType is not "NONE", FixupTree must be NULL,
  604. */
  605. assert(!((FixupTree != NULL) && (FixupType != MATRIX_FIXUP_NONE)));
  606. if (FixupTree != NULL) {
  607. int fi = FixupTree->Find_Named_Node(Node[CurNode].Pivot.Name);
  608. if (fi == -1) {
  609. char buf[128];
  610. sprintf(buf,"Incompatible Base Pose!\nMissing Bone: %s\n",Node[CurNode].Pivot.Name);
  611. throw ErrorClass(buf);
  612. }
  613. Matrix3 fixup = FixupTree->Get_Fixup_Transform(fi);
  614. maxnodeTM = fixup * maxnodeTM;
  615. }
  616. ournodeTM = fixup_matrix(maxnodeTM);
  617. fixupTM = ournodeTM * Inverse(maxnodeTM);
  618. /*
  619. ** Now, make ournodeTM relative to its parent transform. We
  620. ** will always store relative transformations. (Also, note
  621. ** that it is relative to our version of the parent transform
  622. ** which is not necessarily the same as the MAX version...)
  623. */
  624. if (pidx != -1) {
  625. Matrix3 parentTM = Get_Node_Transform(pidx);
  626. Matrix3 pinv = Inverse(parentTM);
  627. ournodeTM = ournodeTM * pinv;
  628. }
  629. /*
  630. ** Break the matrix down into a rotation and translation.
  631. */
  632. DecomposeMatrix(ournodeTM,trans,rot,scale);
  633. /*
  634. ** Save the "fixup" matrix
  635. */
  636. for (int j=0;j<4;j++) {
  637. Point3 row = fixupTM.GetRow(j);
  638. Node[CurNode].Fixup.TM[j][0] = row.x;
  639. Node[CurNode].Fixup.TM[j][1] = row.y;
  640. Node[CurNode].Fixup.TM[j][2] = row.z;
  641. }
  642. /*
  643. ** Set the translation and rotation for this pivot.
  644. */
  645. Node[CurNode].Pivot.Translation.X = trans.x;
  646. Node[CurNode].Pivot.Translation.Y = trans.y;
  647. Node[CurNode].Pivot.Translation.Z = trans.z;
  648. Node[CurNode].Pivot.Rotation.Q[0] = -rot[0];
  649. Node[CurNode].Pivot.Rotation.Q[1] = -rot[1];
  650. Node[CurNode].Pivot.Rotation.Q[2] = -rot[2];
  651. Node[CurNode].Pivot.Rotation.Q[3] = rot[3];
  652. /*
  653. ** Compute the Euler angles and set them.
  654. */
  655. Matrix3 rotmat;
  656. rot.MakeMatrix(rotmat);
  657. EulerAnglesClass eangs(rotmat,EulerOrderXYZr);
  658. Node[CurNode].Pivot.EulerAngles.X = eangs.Get_Angle(0);
  659. Node[CurNode].Pivot.EulerAngles.Y = eangs.Get_Angle(1);
  660. Node[CurNode].Pivot.EulerAngles.Z = eangs.Get_Angle(2);
  661. return CurNode++;
  662. }
  663. /***********************************************************************************************
  664. * HierarchySaveClass::Get_Fixup_Transform -- gets the "fixup" transform for a node *
  665. * *
  666. * INPUT: *
  667. * *
  668. * OUTPUT: *
  669. * *
  670. * WARNINGS: *
  671. * *
  672. * HISTORY: *
  673. * 10/26/1997 GH : Created. *
  674. *=============================================================================================*/
  675. Matrix3 HierarchySaveClass::Get_Fixup_Transform(int node) const
  676. {
  677. assert(node >= 0);
  678. assert(node < CurNode);
  679. Matrix3 m;
  680. for (int j=0;j<4;j++) {
  681. m.SetRow(j,Point3(Node[node].Fixup.TM[j][0],Node[node].Fixup.TM[j][1],Node[node].Fixup.TM[j][2]));
  682. }
  683. return m;
  684. }
  685. /***********************************************************************************************
  686. * HierarchySaveClass::fixup_matrix -- conditions a matrix *
  687. * *
  688. * INPUT: *
  689. * *
  690. * OUTPUT: *
  691. * *
  692. * WARNINGS: *
  693. * *
  694. * HISTORY: *
  695. * 10/26/1997 GH : Created. *
  696. *=============================================================================================*/
  697. Matrix3 HierarchySaveClass::fixup_matrix(const Matrix3 & csrc) const
  698. {
  699. Matrix3 src = csrc; // the GetTrans function is not const correct...
  700. Matrix3 newtm(1);
  701. Point3 trans;
  702. Quat rot;
  703. Point3 scale;
  704. switch (FixupType) {
  705. case MATRIX_FIXUP_NONE:
  706. newtm = src;
  707. break;
  708. case MATRIX_FIXUP_TRANS:
  709. newtm.SetTrans(src.GetTrans());
  710. newtm = Cleanup_Orthogonal_Matrix(newtm);
  711. break;
  712. case MATRIX_FIXUP_TRANS_ROT:
  713. DecomposeMatrix(src,trans,rot,scale);
  714. rot.MakeMatrix(newtm);
  715. newtm.SetTrans(trans);
  716. newtm = Cleanup_Orthogonal_Matrix(newtm);
  717. break;
  718. };
  719. return newtm;
  720. }
  721. /***********************************************************************************************
  722. * HierarchySaveClass::save_header -- writes the header into a W3D file *
  723. * *
  724. * INPUT: *
  725. * *
  726. * OUTPUT: *
  727. * *
  728. * WARNINGS: *
  729. * *
  730. * HISTORY: *
  731. * 10/26/1997 GH : Created. *
  732. *=============================================================================================*/
  733. bool HierarchySaveClass::save_header(ChunkSaveClass & csave)
  734. {
  735. if (!csave.Begin_Chunk(W3D_CHUNK_HIERARCHY_HEADER)) {
  736. return false;
  737. }
  738. if (csave.Write(&HierarchyHeader,sizeof(HierarchyHeader)) != sizeof(HierarchyHeader)) {
  739. return false;
  740. }
  741. if (!csave.End_Chunk()) {
  742. return false;
  743. }
  744. return true;
  745. }
  746. /***********************************************************************************************
  747. * HierarchySaveClass::save_pivots -- writes the pivots into a W3D file *
  748. * *
  749. * INPUT: *
  750. * *
  751. * OUTPUT: *
  752. * *
  753. * WARNINGS: *
  754. * *
  755. * HISTORY: *
  756. * 10/26/1997 GH : Created. *
  757. *=============================================================================================*/
  758. bool HierarchySaveClass::save_pivots(ChunkSaveClass & csave)
  759. {
  760. if (!csave.Begin_Chunk(W3D_CHUNK_PIVOTS)) {
  761. return false;
  762. }
  763. for (uint32 i=0; i<HierarchyHeader.NumPivots; i++) {
  764. if (csave.Write(&Node[i].Pivot,sizeof(W3dPivotStruct)) != sizeof(W3dPivotStruct)) {
  765. return false;
  766. }
  767. }
  768. if (!csave.End_Chunk()) {
  769. return false;
  770. }
  771. return true;
  772. }
  773. /***********************************************************************************************
  774. * HierarchySaveClass::save_fixups -- writes the fixup transforms into a W3D file *
  775. * *
  776. * INPUT: *
  777. * *
  778. * OUTPUT: *
  779. * *
  780. * WARNINGS: *
  781. * *
  782. * HISTORY: *
  783. * 10/26/1997 GH : Created. *
  784. *=============================================================================================*/
  785. bool HierarchySaveClass::save_fixups(ChunkSaveClass & csave)
  786. {
  787. if (!csave.Begin_Chunk(W3D_CHUNK_PIVOT_FIXUPS)) {
  788. return false;
  789. }
  790. for (uint32 i=0; i<HierarchyHeader.NumPivots; i++) {
  791. if (csave.Write(&Node[i].Fixup,sizeof(W3dPivotFixupStruct)) != sizeof(W3dPivotFixupStruct)) {
  792. return false;
  793. }
  794. }
  795. if (!csave.End_Chunk()) {
  796. return false;
  797. }
  798. return true;
  799. }
  800. /***********************************************************************************************
  801. * HierarchySaveClass::load_header -- reads the header from a W3D file *
  802. * *
  803. * INPUT: *
  804. * *
  805. * OUTPUT: *
  806. * *
  807. * WARNINGS: *
  808. * *
  809. * HISTORY: *
  810. * 10/26/1997 GH : Created. *
  811. *=============================================================================================*/
  812. bool HierarchySaveClass::load_header(ChunkLoadClass & cload)
  813. {
  814. /*
  815. ** Load the header
  816. */
  817. if (cload.Read(&HierarchyHeader,sizeof(HierarchyHeader)) != sizeof(HierarchyHeader)) {
  818. return false;
  819. }
  820. /*
  821. ** Reset the current node count
  822. */
  823. CurNode = 0;
  824. Node.Resize(HierarchyHeader.NumPivots);
  825. /*
  826. ** Initialize everything to a default state (particularly the
  827. ** fixup matrices to identity...)
  828. */
  829. for (unsigned i=0; i < HierarchyHeader.NumPivots; i++) {
  830. memset(&(Node[i]),0,sizeof(HierarchyNodeStruct));
  831. Matrix3 ident(1);
  832. for (int j=0; j<3; j++) {
  833. Point3 row = ident.GetRow(j);
  834. Node[i].Fixup.TM[j][0] = row.x;
  835. Node[i].Fixup.TM[j][1] = row.y;
  836. Node[i].Fixup.TM[j][2] = row.z;
  837. }
  838. }
  839. return true;
  840. }
  841. /***********************************************************************************************
  842. * HierarchySaveClass::load_pivots -- reads the pivots from a W3D file *
  843. * *
  844. * INPUT: *
  845. * *
  846. * OUTPUT: *
  847. * *
  848. * WARNINGS: *
  849. * *
  850. * HISTORY: *
  851. * 10/26/1997 GH : Created. *
  852. *=============================================================================================*/
  853. bool HierarchySaveClass::load_pivots(ChunkLoadClass & cload)
  854. {
  855. for (uint32 i=0; i<HierarchyHeader.NumPivots; i++) {
  856. Node[i].MaxNode = NULL;
  857. if (cload.Read(&Node[i].Pivot,sizeof(W3dPivotStruct)) != sizeof(W3dPivotStruct)) {
  858. return false;
  859. }
  860. }
  861. return true;
  862. }
  863. /***********************************************************************************************
  864. * HierarchySaveClass::load_fixups -- reads the fixup transforms from a W3D file *
  865. * *
  866. * INPUT: *
  867. * *
  868. * OUTPUT: *
  869. * *
  870. * WARNINGS: *
  871. * *
  872. * HISTORY: *
  873. * 10/26/1997 GH : Created. *
  874. *=============================================================================================*/
  875. bool HierarchySaveClass::load_fixups(ChunkLoadClass & cload)
  876. {
  877. for (uint32 i=0; i<HierarchyHeader.NumPivots; i++) {
  878. if (cload.Read(&Node[i].Fixup,sizeof(W3dPivotFixupStruct)) != sizeof(W3dPivotFixupStruct)) {
  879. return false;
  880. }
  881. }
  882. return true;
  883. }