w3dexp.cpp 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235
  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/w3dexp.cpp 78 1/03/01 11:06a Greg_h $ */
  19. /***********************************************************************************************
  20. *** Confidential - Westwood Studios ***
  21. ***********************************************************************************************
  22. * *
  23. * Project Name : Commando Tools - W3D export *
  24. * *
  25. * $Archive:: /Commando/Code/Tools/max2w3d/w3dexp.cpp $*
  26. * *
  27. * $Author:: Greg_h $*
  28. * *
  29. * $Modtime:: 1/03/01 11:03a $*
  30. * *
  31. * $Revision:: 78 $*
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * W3dExportClass::W3dExportClass -- constructor *
  36. * W3dExportClass::~W3dExportClass -- destructor *
  37. * W3dExportClass::Export_Hierarchy -- Export the hierarchy tree *
  38. * W3dExportClass::Export_Animation -- Export animation data *
  39. * W3dExportClass::Export_Damage_Animations -- Exports damage animations for the model *
  40. * W3dExportClass::Export_Geometry -- Export the geometry data *
  41. * W3dExportClass::get_hierarchy_tree -- get a pointer to the hierarchy tree *
  42. * W3dExportClass::get_export_options -- get the export options *
  43. * W3dExportClass::Start_Progress_Bar -- start the MAX progress meter *
  44. * W3dExportClass::End_Progress_Bar -- end the progress meter *
  45. * W3dExportClass::get_damage_root_list -- gets the list of damage root nodes *
  46. * W3dExportClass::Export_HLod -- Export an HLOD description *
  47. * W3dExportClass::Export_Collection -- exports a collection chunk *
  48. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  49. #include "rawfile.h"
  50. #include "chunkio.h"
  51. #include "w3dexp.h"
  52. #include "w3dutil.h"
  53. #include "nodelist.h"
  54. #include "meshsave.h"
  55. #include "hiersave.h"
  56. #include "hlodsave.h"
  57. #include "meshcon.h"
  58. #include "SnapPoints.h"
  59. #include "w3ddlg.h"
  60. #include "progress.h"
  61. #include "errclass.h"
  62. #include "motion.h"
  63. #include "util.h"
  64. #include "w3ddesc.h"
  65. #include "colboxsave.h"
  66. #include "nullsave.h"
  67. #include "dazzlesave.h"
  68. #include "maxworldinfo.h"
  69. #include "exportlog.h"
  70. #include "geometryexporttask.h"
  71. #include "geometryexportcontext.h"
  72. #include <direct.h>
  73. // Used to communicate from the exporter to the dialog.
  74. char W3dExportClass::CurrentExportPath[_MAX_DRIVE + _MAX_DIR + 1] = { '\000' };
  75. /* local functions */
  76. static DWORD WINAPI progress_callback( LPVOID arg);
  77. static HierarchySaveClass * load_hierarchy_file(char * filename);
  78. static bool dupe_check(const INodeListClass & list);
  79. static bool check_lod_extensions (INodeListClass &list, INode *origin);
  80. /*
  81. ** Struct for export info (AppDataChunk hung off the scene pointer)
  82. ** This includes the export info struct and some padding.
  83. ** NOTE: to avoid file versioning issues, new data should be added after
  84. ** existing data in this struct, the padding array should be made smaller so
  85. ** the total size remains the same, and the new data should give reasonable
  86. ** results with a default content of zeros (which is what it will contain if
  87. ** an older file is loaded).
  88. */
  89. struct ExportInfoAppDataChunkStruct {
  90. W3dExportOptionsStruct ExportOptions;
  91. unsigned char Padding[93];
  92. };
  93. /************************************************************************************************
  94. **
  95. ** GeometryFilterClass - filters out nodes which are not marked for W3D geometry export
  96. **
  97. ************************************************************************************************/
  98. class GeometryFilterClass : public INodeFilterClass
  99. {
  100. public:
  101. virtual BOOL Accept_Node(INode * node, TimeValue time)
  102. {
  103. Object * obj = node->EvalWorldState(time).obj;
  104. if
  105. (
  106. obj
  107. // && !Is_Proxy (*node)
  108. && !Is_Origin(node)
  109. && !node->IsHidden()
  110. && Is_Geometry(node)
  111. )
  112. {
  113. return TRUE;
  114. } else {
  115. return FALSE;
  116. }
  117. }
  118. };
  119. /************************************************************************************************
  120. **
  121. ** OriginFilterClass - Filters out nodes which are not "origin" objects. Origins are MAX dummy
  122. ** objects whose parents are the scene root, and are named "origin.*" (ie. first 7 characters
  123. ** in the name are "origin.". These origin objects will be (0,0,0) for all of its descendants.
  124. ** This allows an artist to create multiple models within one MAX scene but still have each
  125. ** mesh's coordinates equal without needing to stack all the models on the world origin.
  126. ** We iterate through the origin objects when exporting a scene containing multiple LODs of
  127. ** one model.
  128. **
  129. ************************************************************************************************/
  130. class OriginFilterClass : public INodeFilterClass
  131. {
  132. public:
  133. virtual BOOL Accept_Node(INode * node, TimeValue time) { return Is_Origin(node); }
  134. };
  135. /************************************************************************************************
  136. **
  137. ** DamageRootFilterClass - Filters out all nodes which are not "damage root" objects. These nodes
  138. ** are MAX dummy objects whose parents are the scene root, and are named "damage.*" (ie. first 7
  139. ** characters in the name are "damage.". These damage roots mean that all of its children
  140. ** represent a damaged model.
  141. **
  142. ************************************************************************************************/
  143. class DamageRootFilterClass : public INodeFilterClass
  144. {
  145. public:
  146. virtual BOOL Accept_Node(INode * node, TimeValue time) { return Is_Damage_Root(node); }
  147. };
  148. /************************************************************************************************
  149. **
  150. ** DamageRegionFilterClass - Filters out all node that are not a bone which is part of a certain
  151. ** deformation region. Pass the region ID to the constructor.
  152. **
  153. ************************************************************************************************/
  154. class DamageRegionFilterClass : public INodeFilterClass
  155. {
  156. public:
  157. DamageRegionFilterClass(int region_id) { RegionId = region_id; }
  158. virtual BOOL Accept_Node(INode * node, TimeValue time)
  159. {
  160. if (!Is_Bone(node)) return FALSE;
  161. // Check it's damage region ID (if it has one).
  162. AppDataChunk * appdata = node->GetAppDataChunk(W3DUtilityClassID,UTILITY_CLASS_ID,1);
  163. if (!appdata) return FALSE;
  164. W3DAppData1Struct *wdata = (W3DAppData1Struct*)(appdata->data);
  165. return wdata->DamageRegion == RegionId;
  166. }
  167. protected:
  168. int RegionId;
  169. };
  170. /***********************************************************************************************
  171. * W3dExportClass::DoExport -- This method is called for the plug-in to perform it's file expo *
  172. * *
  173. * INPUT: *
  174. * name - filename to use *
  175. * export - A pointer the plug-in may use to call methods to enumerate the scene *
  176. * max - An interface pointer the plug-in may use to call methods of MAX. *
  177. * *
  178. * OUTPUT: *
  179. * Nonzero on successful export; otherwise 0. *
  180. * *
  181. * WARNINGS: *
  182. * *
  183. * HISTORY: *
  184. * 06/09/1997 GH : Created. *
  185. * 10/17/2000 gth : Removed the old export code-path, everything goes through an origin now *
  186. *=============================================================================================*/
  187. int W3dExportClass::DoExport
  188. (
  189. const TCHAR *filename,
  190. ExpInterface *export,
  191. Interface *max,
  192. BOOL suppressPrompts,
  193. DWORD options
  194. )
  195. {
  196. ExportInterface = export;
  197. MaxInterface = max;
  198. RootNode = NULL;
  199. OriginList = NULL;
  200. DamageRootList = NULL;
  201. HierarchyTree = NULL;
  202. try {
  203. CurTime = MaxInterface->GetTime();
  204. FrameRate = GetFrameRate();
  205. FixupType = HierarchySaveClass::MATRIX_FIXUP_TRANS_ROT;
  206. /*
  207. ** The Animation and the Hierarchy will be named with the root portion of the W3D filename
  208. ** and the path is used by the options dialog
  209. */
  210. char rootname[_MAX_FNAME + 1];
  211. char drivename[_MAX_DRIVE + 1];
  212. char dirname[_MAX_DIR + 1];
  213. _splitpath(filename, drivename, dirname, rootname, NULL);
  214. sprintf(CurrentExportPath, "%s%s", drivename, dirname);
  215. /*
  216. ** The batch export process (suppressPrompt == TRUE) needs to know the directory of the
  217. ** MAX file being exported. This is so that it can use the old relative pathname of the
  218. ** W3D file containing the hierarchy.
  219. */
  220. _splitpath(max->GetCurFilePath(), drivename, dirname, NULL, NULL);
  221. sprintf(CurrentScenePath, "%s%s", drivename, dirname);
  222. /*
  223. ** Get export options
  224. */
  225. if (!get_export_options(suppressPrompts)) {
  226. return 1;
  227. }
  228. /*
  229. ** If no data is going to be exported just bail
  230. */
  231. if ((!ExportOptions.ExportHierarchy) && (!ExportOptions.ExportAnimation) && (!ExportOptions.ExportGeometry)) {
  232. return 1;
  233. }
  234. /*
  235. ** Initialize the logging system
  236. */
  237. ExportLog::Init(NULL);
  238. /*
  239. ** Create a chunk saver to write the w3d file with
  240. */
  241. RawFileClass stream(filename);
  242. if (!stream.Open(FileClass::WRITE)) {
  243. MessageBox(NULL,"Unable to open file.","Error",MB_OK | MB_SETFOREGROUND);
  244. return 1;
  245. }
  246. ChunkSaveClass csave(&stream);
  247. /*
  248. ** Export data from the scene.
  249. **
  250. ** Are we doing an old export (one model/LOD per scene) or a new export (multiple LODs
  251. ** for one model in a scene)?
  252. */
  253. if (get_origin_list())
  254. {
  255. DoOriginBasedExport(rootname, csave);
  256. }
  257. /*
  258. ** Done!
  259. */
  260. stream.Close();
  261. if (HierarchyTree != NULL) {
  262. delete HierarchyTree;
  263. HierarchyTree = NULL;
  264. }
  265. if (OriginList != NULL) {
  266. delete OriginList;
  267. OriginList = NULL;
  268. }
  269. if (DamageRootList != NULL) {
  270. delete DamageRootList;
  271. DamageRootList = NULL;
  272. }
  273. } catch (ErrorClass error) {
  274. MessageBox(NULL,error.error_message,"Error",MB_OK | MB_SETFOREGROUND);
  275. }
  276. ExportLog::Shutdown(ExportOptions.ReviewLog);
  277. MaxInterface->RedrawViews(MaxInterface->GetTime());
  278. return 1;
  279. }
  280. /***********************************************************************************************
  281. * W3dExportClass::DoOriginBasedExport -- New export codepath. Exports any objects linked to *
  282. * an origin object. Assumes origins named "origin.01" and greater represent LODs of the *
  283. * original object ("origin.00"). Also assumes "damage.01" and greater represent damaged *
  284. * versions of the original object. *
  285. * *
  286. * INPUT: *
  287. * *
  288. * OUTPUT: *
  289. * *
  290. * WARNINGS: *
  291. * *
  292. * HISTORY: *
  293. * 9/13/1999 AJA : Created. *
  294. * 9/21/1999 AJA : Added support for the new animation exporting process (damage-related). *
  295. *=============================================================================================*/
  296. void W3dExportClass::DoOriginBasedExport(char *rootname, ChunkSaveClass &csave)
  297. {
  298. /*
  299. ** Build the damage root list.
  300. */
  301. INodeListClass *damage_list = get_damage_root_list();
  302. assert(damage_list != NULL);
  303. /*
  304. ** Start the progress meter
  305. */
  306. Start_Progress_Bar();
  307. Progress_Meter_Class meter(MaxInterface,0.0f,100.0f);
  308. int steps = 0;
  309. steps++; // Base Pose
  310. steps+= OriginList->Num_Nodes(); // n Origins
  311. steps++; // Basic Anim OR Damage Anims
  312. steps++; // HLOD OR Collection
  313. meter.Finish_In_Steps(steps);
  314. /*
  315. ** Find the base object's origin.
  316. */
  317. bool is_base_object = false;
  318. INodeListClass *origin_list = get_origin_list();
  319. unsigned int i, count = origin_list->Num_Nodes();
  320. INode *base_origin = NULL;
  321. for (i = 0; i < count; i++)
  322. {
  323. INode *node = (*origin_list)[i];
  324. if (Is_Base_Origin(node))
  325. {
  326. base_origin = node;
  327. break;
  328. }
  329. }
  330. /*
  331. ** Write the Hierarchy Tree (if needed)
  332. */
  333. Progress_Meter_Class treemeter(meter, meter.Increment);
  334. if (!Export_Hierarchy(rootname, csave, treemeter, base_origin))
  335. {
  336. MessageBox(NULL,"Hierarchy Export Failure!","Error",MB_OK | MB_SETFOREGROUND);
  337. End_Progress_Bar();
  338. return;
  339. }
  340. meter.Add_Increment();
  341. if (damage_list->Num_Nodes() <= 0)
  342. {
  343. /*
  344. ** Write the Base Animation (if needed)
  345. */
  346. Progress_Meter_Class animmeter(meter, meter.Increment);
  347. if (!Export_Animation(rootname, csave, animmeter, base_origin))
  348. {
  349. MessageBox(NULL,"Animation Export Failure!","Error",MB_OK | MB_SETFOREGROUND);
  350. End_Progress_Bar();
  351. return;
  352. }
  353. meter.Add_Increment();
  354. }
  355. else
  356. {
  357. /*
  358. ** Write the damage animations.
  359. */
  360. Progress_Meter_Class damagemeter(meter, meter.Increment);
  361. for (i = 0; i < damage_list->Num_Nodes(); i++)
  362. {
  363. if (!Export_Damage_Animations(rootname, csave, damagemeter, (*damage_list)[i]))
  364. {
  365. MessageBox(NULL, "Damage Animation Export Failure!", "Error", MB_OK | MB_SETFOREGROUND);
  366. End_Progress_Bar();
  367. return;
  368. }
  369. }
  370. meter.Add_Increment();
  371. }
  372. /*
  373. ** Create an array of pointers to MeshConnectionsClass objects. These objects
  374. ** will be created below, and will be used to generate the HLOD with the
  375. ** geometry of all models in the scene.
  376. */
  377. MeshConnectionsClass **connections = new MeshConnectionsClass*[count];
  378. if (!connections)
  379. {
  380. MessageBox(NULL, "Memory allocation failure!", "Error", MB_OK | MB_SETFOREGROUND);
  381. End_Progress_Bar();
  382. return;
  383. }
  384. memset(connections, 0, sizeof(MeshConnectionsClass*) * count);
  385. /*
  386. ** For each model in the scene, write its animation and geometry (if needed).
  387. ** All models share the above hierarchy tree.
  388. */
  389. int idx = strlen(rootname);
  390. rootname[idx+1] = '\0';
  391. /*
  392. ** If we're not exporting a hierarchical model, only export the "origin.00"
  393. */
  394. if (!ExportOptions.LoadHierarchy && !ExportOptions.ExportHierarchy) {
  395. count = 1;
  396. }
  397. for (i = 0; i < count; i++)
  398. {
  399. /*
  400. ** Get the current origin.
  401. */
  402. INode *origin = (*origin_list)[i];
  403. /*
  404. ** Write each mesh (if needed)
  405. */
  406. MeshConnectionsClass *meshcon = NULL;
  407. Progress_Meter_Class meshmeter(meter, meter.Increment);
  408. if (!Export_Geometry(rootname, csave, meshmeter, origin, &meshcon))
  409. {
  410. MessageBox(NULL, "Geometry Export Failure!", "Error", MB_OK | MB_SETFOREGROUND);
  411. End_Progress_Bar();
  412. return;
  413. }
  414. meter.Add_Increment();
  415. /*
  416. ** Put the MeshConnectionsClass object for this model into
  417. ** the array in order of LOD (top-level last).
  418. */
  419. int lod_level = Get_Lod_Level(origin);
  420. if (lod_level >= count || connections[count - lod_level - 1] != NULL)
  421. {
  422. char text[256];
  423. sprintf(text, "Origin Naming Error! There are %d models defined in this "
  424. "scene, therefore your origin names should be\n\"Origin.00\" through "
  425. "\"Origin.%02d\", 00 being the high-poly model and %02d being the "
  426. "lowest detail LOD.", count, count-1, count-1);
  427. MessageBox(NULL, text, "Error", MB_OK | MB_SETFOREGROUND);
  428. End_Progress_Bar();
  429. return;
  430. }
  431. connections[count - lod_level - 1] = meshcon;
  432. }
  433. /*
  434. ** Generate the HLOD based on all the mesh connections.
  435. */
  436. if (ExportOptions.LoadHierarchy || ExportOptions.ExportHierarchy) {
  437. rootname[idx] = '\0'; // remove the trailing character (signifies which lod level)
  438. HierarchySaveClass *htree = get_hierarchy_tree();
  439. if (htree)
  440. {
  441. Progress_Meter_Class hlod_meter(meter, meter.Increment);
  442. if (!Export_HLod(rootname, htree->Get_Name(), csave, hlod_meter, connections, count))
  443. {
  444. MessageBox(NULL, "HLOD Generation Failure!", "Error", MB_OK | MB_SETFOREGROUND);
  445. End_Progress_Bar();
  446. return;
  447. }
  448. meter.Add_Increment();
  449. }
  450. }
  451. /*
  452. ** Deallocate the array of mesh connections.
  453. */
  454. for (i = 0; i < count; i++)
  455. {
  456. if (connections[i] != NULL)
  457. delete connections[i];
  458. }
  459. delete []connections;
  460. End_Progress_Bar();
  461. }
  462. /***********************************************************************************************
  463. * W3dExportClass::Export_Hierarchy -- Export the hierarchy tree *
  464. * *
  465. * INPUT: *
  466. * *
  467. * OUTPUT: *
  468. * *
  469. * WARNINGS: *
  470. * *
  471. * HISTORY: *
  472. * 10/16/1997 GH : Created. *
  473. * 13/9/1999 AJA : Split into two calls, one that takes a node list and one that takes a *
  474. * single root node. *
  475. * 10/17/2000 gth : Removed the old code-path, we always use an origin now *
  476. *=============================================================================================*/
  477. bool W3dExportClass::Export_Hierarchy(char *name,ChunkSaveClass & csave,Progress_Meter_Class & meter,
  478. INode *root)
  479. {
  480. if (!ExportOptions.ExportHierarchy) return true;
  481. HierarchySaveClass::Enable_Terrain_Optimization(ExportOptions.EnableTerrainMode);
  482. if (root == NULL) return false;
  483. try {
  484. HierarchyTree = new HierarchySaveClass(root,CurTime,meter,name,FixupType);
  485. } catch (ErrorClass err) {
  486. MessageBox(NULL, err.error_message,"Error!",MB_OK | MB_SETFOREGROUND);
  487. return false;
  488. }
  489. HierarchyTree->Save(csave);
  490. return true;
  491. }
  492. /***********************************************************************************************
  493. * W3dExportClass::Export_Animation -- Export animation data *
  494. * *
  495. * INPUT: *
  496. * *
  497. * OUTPUT: *
  498. * *
  499. * WARNINGS: *
  500. * *
  501. * HISTORY: *
  502. * 10/16/1997 GH : Created. *
  503. * 13/9/1999 AJA : Split into two calls, one that takes a node list and one that takes a *
  504. * single root node. *
  505. * 10/17/2000 gth : Removed the old code-path, we always use an origin now *
  506. *=============================================================================================*/
  507. bool W3dExportClass::Export_Animation(char * name,ChunkSaveClass & csave,Progress_Meter_Class & meter,
  508. INode *root)
  509. {
  510. if (!ExportOptions.ExportAnimation) return true;
  511. HierarchySaveClass * htree = get_hierarchy_tree();
  512. if ((root == NULL) || (htree == NULL)) {
  513. return false;
  514. }
  515. MotionClass * motion = NULL;
  516. try {
  517. motion = new MotionClass( ExportInterface->theScene,
  518. root,
  519. htree,
  520. ExportOptions,
  521. FrameRate,
  522. &meter,
  523. MaxInterface->GetMAXHWnd(),
  524. name);
  525. } catch (ErrorClass err) {
  526. MessageBox(NULL,err.error_message,"Error!",MB_OK | MB_SETFOREGROUND);
  527. return false;
  528. }
  529. motion->Save(csave);
  530. delete motion;
  531. return true;
  532. }
  533. /***********************************************************************************************
  534. * W3dExportClass::Export_Damage_Animations -- Exports damage animations for the model *
  535. * *
  536. * INPUT: *
  537. * *
  538. * OUTPUT: *
  539. * *
  540. * WARNINGS: *
  541. * *
  542. * HISTORY: *
  543. * 1999 AJA : Created. *
  544. *=============================================================================================*/
  545. bool W3dExportClass::Export_Damage_Animations(char *name, ChunkSaveClass &csave,
  546. Progress_Meter_Class &meter,
  547. INode *damage_root)
  548. {
  549. if (!ExportOptions.ExportAnimation) return true;
  550. HierarchySaveClass *htree = get_hierarchy_tree();
  551. if ((damage_root == NULL) || (htree == NULL))
  552. return false;
  553. int damage_state = Get_Damage_State(damage_root);
  554. /*
  555. ** While exporting damage animations, we need the offset from our origin to the real
  556. ** scene origin.
  557. */
  558. Matrix3 originoffset = Inverse(damage_root->GetNodeTM(CurTime));
  559. /*
  560. ** For every damage region we find, export an animation.
  561. */
  562. bool done = false;
  563. int current_region = 0;
  564. int num_damage_bones = 0; // number of bones assigned to a damage region
  565. for (current_region = 0; current_region < MAX_DAMAGE_REGIONS; current_region++)
  566. {
  567. DamageRegionFilterClass region_filter(current_region);
  568. INodeListClass bone_list(damage_root, CurTime, &region_filter);
  569. num_damage_bones += bone_list.Num_Nodes();
  570. // Move to the next region if there aren't any bones in this one.
  571. if (bone_list.Num_Nodes() <= 0)
  572. continue;
  573. // Put together an animation name for this damage region.
  574. char anim_name[W3D_NAME_LEN];
  575. sprintf(anim_name, "damage%d-%d", current_region, damage_state);
  576. // Export an animation for this damage region.
  577. MotionClass *motion = NULL;
  578. try
  579. {
  580. motion = new MotionClass( ExportInterface->theScene,
  581. &bone_list,
  582. htree,
  583. ExportOptions,
  584. FrameRate,
  585. &meter,
  586. MaxInterface->GetMAXHWnd(),
  587. anim_name,
  588. originoffset);
  589. }
  590. catch (ErrorClass err)
  591. {
  592. MessageBox(NULL, err.error_message, "Error!", MB_OK | MB_SETFOREGROUND);
  593. return false;
  594. }
  595. assert(motion != NULL);
  596. motion->Save(csave);
  597. delete motion;
  598. }
  599. if (num_damage_bones <= 0)
  600. {
  601. MessageBox(NULL, "Warning: Your damage bones need to be given damage region numbers. "
  602. "You can do this in the W3D Tools panel.", name, MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND);
  603. }
  604. return true;
  605. }
  606. /***********************************************************************************************
  607. * W3dExportClass::Export_Geometry -- Export the geometry data *
  608. * *
  609. * INPUT: *
  610. * *
  611. * OUTPUT: *
  612. * *
  613. * WARNINGS: *
  614. * *
  615. * HISTORY: *
  616. * 10/16/1997 GH : Created. *
  617. * 13/9/1999 AJA : Added an optional "root" parameter to export geometry of the node's *
  618. * descendants. *
  619. * 10/17/2000 gth : Made the "root" parameter a requirement, just pass in the scene root *
  620. * if you want to export all geometry in the scene. *
  621. * 10/30/2000 gth : If exporting only geometry, only export the first mesh *
  622. *=============================================================================================*/
  623. bool W3dExportClass::Export_Geometry(char * name,ChunkSaveClass & csave,Progress_Meter_Class & meter,
  624. INode *root,MeshConnectionsClass **out_connection)
  625. {
  626. unsigned int i;
  627. assert(root != NULL);
  628. if (!ExportOptions.ExportGeometry) return true;
  629. /*
  630. ** If we're attaching the meshes to a hierarchy, get the tree
  631. */
  632. HierarchySaveClass * htree = NULL;
  633. if (ExportOptions.LoadHierarchy || ExportOptions.ExportHierarchy) {
  634. htree = get_hierarchy_tree();
  635. if (htree == NULL) {
  636. return false;
  637. }
  638. }
  639. DynamicVectorClass<GeometryExportTaskClass *> export_tasks;
  640. INodeListClass *geometry_list = NULL;
  641. /*
  642. ** Create the lists of nodes that we're going to work with
  643. */
  644. GeometryFilterClass geometryfilter;
  645. geometry_list = new INodeListClass(root,CurTime,&geometryfilter);
  646. if (dupe_check(*geometry_list)) {
  647. return false;
  648. }
  649. MaxWorldInfoClass world_info(export_tasks);
  650. world_info.Allow_Mesh_Smoothing (ExportOptions.SmoothBetweenMeshes);
  651. /*
  652. ** Initialize the context object for exporting geometry
  653. */
  654. GeometryExportContextClass context( name,
  655. csave,
  656. world_info,
  657. ExportOptions,
  658. htree,
  659. root,
  660. get_origin_list(),
  661. CurTime );
  662. /*
  663. ** Initialize a list of geometry export tasks containing all nodes marked for geometry export.
  664. ** If we're only exporting geometry, only export the first mesh. (no more collections)
  665. */
  666. int geometry_count = geometry_list->Num_Nodes();
  667. if ((htree == NULL) && (geometry_list->Num_Nodes() > 1)) {
  668. geometry_count = MIN(geometry_count,1);
  669. ExportLog::printf("\nDiscarding extra meshes since we are not exporting a hierarchical model.\n");
  670. }
  671. for (i=0; i<geometry_count; i++) {
  672. GeometryExportTaskClass * export_task = GeometryExportTaskClass::Create_Task((*geometry_list)[i],context);
  673. if (export_task != NULL) {
  674. export_tasks.Add(export_task);
  675. }
  676. }
  677. meter.Finish_In_Steps(export_tasks.Count());
  678. /*
  679. ** Optimize the mesh data if the user desired, modifying the list of geometry export tasks.
  680. */
  681. if (ExportOptions.EnableOptimizeMeshData) {
  682. GeometryExportTaskClass::Optimize_Geometry(export_tasks,context);
  683. }
  684. /*
  685. ** If there is only one piece of geometry to export and no place-holders, and we're not
  686. ** exporting a hierarchical model, then we force the name to match the filename
  687. */
  688. if ((export_tasks.Count() == 1) && (htree == NULL))
  689. {
  690. export_tasks[0]->Set_Name(name);
  691. export_tasks[0]->Set_Container_Name("");
  692. }
  693. /*
  694. ** Generate the mesh-connections object to return to the caller
  695. */
  696. MeshConnectionsClass * meshcon = NULL;
  697. if (htree != NULL) {
  698. Progress_Meter_Class mcmeter(meter,meter.Increment);
  699. try {
  700. meshcon = new MeshConnectionsClass(export_tasks,context);
  701. } catch (ErrorClass err) {
  702. MessageBox(NULL,err.error_message,"Error!",MB_OK | MB_SETFOREGROUND);
  703. return false;
  704. }
  705. *out_connection = meshcon;
  706. meter.Add_Increment();
  707. }
  708. /*
  709. ** Export each piece of geometry
  710. */
  711. for (i=0; i<export_tasks.Count(); i++) {
  712. Progress_Meter_Class meshmeter(meter,meter.Increment);
  713. context.ProgressMeter = &meshmeter;
  714. try {
  715. export_tasks[i]->Export_Geometry(context);
  716. } catch (ErrorClass err) {
  717. MessageBox(MaxInterface->GetMAXHWnd(),err.error_message,"Error!",MB_OK | MB_SETFOREGROUND);
  718. continue;
  719. }
  720. meter.Add_Increment();
  721. }
  722. /*
  723. ** Cleanup
  724. */
  725. for (i=0; i<export_tasks.Count(); i++) {
  726. delete export_tasks[i];
  727. }
  728. export_tasks.Delete_All();
  729. delete geometry_list;
  730. return true;
  731. }
  732. /***********************************************************************************************
  733. * W3dExportClass::Export_HLod -- Export an HLOD description *
  734. * *
  735. * INPUT: *
  736. * *
  737. * OUTPUT: *
  738. * *
  739. * WARNINGS: *
  740. * *
  741. * HISTORY: *
  742. * 10/17/2000 gth : Created. *
  743. *=============================================================================================*/
  744. bool W3dExportClass::Export_HLod( char *name, const char *htree_name, ChunkSaveClass &csave,
  745. Progress_Meter_Class &meter, MeshConnectionsClass **connections,
  746. int lod_count)
  747. {
  748. if (!ExportOptions.ExportGeometry) return true;
  749. HLodSaveClass hlod_save(connections, lod_count, CurTime, name, htree_name, meter, get_origin_list());
  750. if (!hlod_save.Save(csave))
  751. return false;
  752. return true;
  753. }
  754. /***********************************************************************************************
  755. * W3dExportClass::get_hierarchy_tree -- get a pointer to the hierarchy tree *
  756. * *
  757. * INPUT: *
  758. * *
  759. * OUTPUT: *
  760. * *
  761. * WARNINGS: *
  762. * *
  763. * HISTORY: *
  764. * 10/16/1997 GH : Created. *
  765. *=============================================================================================*/
  766. HierarchySaveClass * W3dExportClass::get_hierarchy_tree(void)
  767. {
  768. /*
  769. ** If the hierarchy tree pointer has been initialized, just return it
  770. */
  771. if (HierarchyTree != NULL) return HierarchyTree;
  772. /*
  773. ** If we are supposed to be loading a hierarchy from disk, then
  774. ** load it
  775. */
  776. if (!ExportOptions.ExportHierarchy) {
  777. HierarchyTree = load_hierarchy_file(HierarchyFilename);
  778. if (HierarchyTree) {
  779. return HierarchyTree;
  780. } else {
  781. char buf[256];
  782. sprintf(buf,"Unable to load hierarchy file: %s\nIf this Max file has been moved, please re-select the hierarchy file.",HierarchyFilename);
  783. MessageBox(MaxInterface->GetMAXHWnd(),buf,"Error",MB_OK | MB_SETFOREGROUND);
  784. return NULL;
  785. }
  786. }
  787. /*
  788. ** Should never fall through to here...
  789. ** This would only happen if ExportHierarchy was true and the Export_Hierarchy
  790. ** function failed to create a hierarchy tree for us.
  791. */
  792. assert(0);
  793. return NULL;
  794. }
  795. /***********************************************************************************************
  796. * W3dExportClass::get_damage_root_list -- gets the list of damage root nodes *
  797. * *
  798. * INPUT: *
  799. * *
  800. * OUTPUT: *
  801. * *
  802. * WARNINGS: *
  803. * *
  804. * HISTORY: *
  805. * 10/17/2000 gth : Created. *
  806. *=============================================================================================*/
  807. INodeListClass * W3dExportClass::get_damage_root_list(void)
  808. {
  809. if (DamageRootList != NULL) return DamageRootList;
  810. /*
  811. ** Create a list of all damage root objects in the scene.
  812. */
  813. DamageRootFilterClass nodefilter;
  814. DamageRootList = new INodeListClass(ExportInterface->theScene, CurTime, &nodefilter);
  815. return DamageRootList;
  816. }
  817. /***********************************************************************************************
  818. * get_origin_list -- get the list of origin nodes *
  819. * *
  820. * INPUT: *
  821. * *
  822. * OUTPUT: *
  823. * *
  824. * WARNINGS: *
  825. * *
  826. * HISTORY: *
  827. * 9/13/1999 AJA : Created. *
  828. *=============================================================================================*/
  829. INodeListClass * W3dExportClass::get_origin_list(void)
  830. {
  831. if (OriginList != NULL) return OriginList;
  832. /*
  833. ** Create a list of all origins in the scene.
  834. */
  835. static OriginFilterClass originfilter;
  836. OriginList = new INodeListClass (ExportInterface->theScene, CurTime, &originfilter);
  837. /*
  838. ** If we didn't find any origins, add the scene root as an origin.
  839. ** NOTE: it would also be a problem if the origin list contained both the scene root
  840. ** and the user placed origins. Thats not happening now because the OriginList
  841. ** does not collect the scene root... were that to change we'd have to update this
  842. ** code as well.
  843. */
  844. if (OriginList->Num_Nodes() == 0) {
  845. OriginList->Insert(MaxInterface->GetRootNode());
  846. }
  847. return OriginList;
  848. }
  849. /***********************************************************************************************
  850. * W3dExportClass::get_export_options -- get the export options *
  851. * *
  852. * INPUT: *
  853. * *
  854. * OUTPUT: *
  855. * *
  856. * WARNINGS: *
  857. * *
  858. * HISTORY: *
  859. * 10/16/1997 GH : Created. *
  860. * 9/30/1999 AJA : Added support for the MAX suppress_prompts flag. *
  861. *=============================================================================================*/
  862. bool W3dExportClass::get_export_options(BOOL suppress_prompts)
  863. {
  864. int ticksperframe = GetTicksPerFrame();
  865. // Get the last export settings from the AppDataChunk attached to the
  866. // scene pointer. If there is no such AppDataChunk create one and set it
  867. // to default values.
  868. W3dExportOptionsStruct *options = NULL;
  869. AppDataChunk * appdata = MaxInterface->GetScenePointer()->GetAppDataChunk(W3D_EXPORTER_CLASS_ID,SCENE_EXPORT_CLASS_ID,0);
  870. if (appdata) {
  871. options = &(((ExportInfoAppDataChunkStruct *)(appdata->data))->ExportOptions);
  872. } else {
  873. ExportInfoAppDataChunkStruct *appdata_struct =
  874. (ExportInfoAppDataChunkStruct *)malloc(sizeof(ExportInfoAppDataChunkStruct));
  875. options = &(appdata_struct->ExportOptions);
  876. options->ExportHierarchy = true;
  877. options->LoadHierarchy = false;
  878. options->ExportAnimation = true;
  879. options->EnableTerrainMode = false;
  880. options->ReduceAnimation = false;
  881. options->ReduceAnimationPercent = 50;
  882. options->CompressAnimation = false;
  883. options->CompressAnimationFlavor = ANIM_FLAVOR_TIMECODED;
  884. options->CompressAnimationTranslationError = 0.001f; //DEFAULT_LOSSY_ERROR_TOLERANCE;
  885. options->CompressAnimationRotationError = 0.050f; //DEFAULT_LOSSY_ERROR_TOLERANCE;
  886. options->ReviewLog = false;
  887. options->ExportGeometry = true;
  888. options->TranslationOnly = false;
  889. options->SmoothBetweenMeshes = true;
  890. strcpy(options->HierarchyFilename,"");
  891. strcpy(options->RelativeHierarchyFilename,"");
  892. options->StartFrame = MaxInterface->GetAnimRange().Start() / ticksperframe;
  893. options->EndFrame = MaxInterface->GetAnimRange().End() / ticksperframe;
  894. options->UseVoxelizer = false;
  895. options->DisableExportAABTrees = true;
  896. options->EnableOptimizeMeshData = false;
  897. memset(&(appdata_struct->Padding), 0, sizeof(appdata_struct->Padding));
  898. MaxInterface->GetScenePointer()->AddAppDataChunk(W3D_EXPORTER_CLASS_ID,
  899. SCENE_EXPORT_CLASS_ID, 0, sizeof(ExportInfoAppDataChunkStruct),
  900. appdata_struct);
  901. }
  902. // (gth) disabling the 'optimize mesh data' feature due to problems with external tools
  903. options->EnableOptimizeMeshData = false;
  904. bool retval = true;
  905. if (suppress_prompts == FALSE)
  906. {
  907. W3dOptionsDialogClass dialog(MaxInterface,ExportInterface);
  908. retval = dialog.Get_Export_Options(options);
  909. }
  910. if (suppress_prompts || retval) {
  911. ExportOptions = *options;
  912. if ( (suppress_prompts == TRUE) && (options->RelativeHierarchyFilename[0] != 0) )
  913. {
  914. // Use the relative pathname WRT the max scene's directory to
  915. // figure out the absolute directory where the hierarchy file
  916. // is stored.
  917. char curdir[_MAX_DRIVE + _MAX_DIR + 1];
  918. assert(_getcwd(curdir, sizeof(curdir)));
  919. assert(_chdir(CurrentScenePath) != -1);
  920. assert(_fullpath(HierarchyFilename, options->RelativeHierarchyFilename,
  921. sizeof(HierarchyFilename)));
  922. assert(_chdir(curdir) != -1);
  923. }
  924. else
  925. strcpy(HierarchyFilename,options->HierarchyFilename);
  926. if (ExportOptions.TranslationOnly) {
  927. FixupType = HierarchySaveClass::MATRIX_FIXUP_TRANS;
  928. } else {
  929. FixupType = HierarchySaveClass::MATRIX_FIXUP_TRANS_ROT;
  930. }
  931. }
  932. return retval;
  933. }
  934. /***********************************************************************************************
  935. * W3dExportClass::Start_Progress_Bar -- start the MAX progress meter *
  936. * *
  937. * INPUT: *
  938. * *
  939. * OUTPUT: *
  940. * *
  941. * WARNINGS: *
  942. * *
  943. * HISTORY: *
  944. * 10/16/1997 GH : Created. *
  945. *=============================================================================================*/
  946. void W3dExportClass::Start_Progress_Bar(void)
  947. {
  948. MaxInterface->ProgressStart(
  949. "Processing Triangle Mesh",
  950. TRUE,
  951. progress_callback,
  952. NULL);
  953. }
  954. /***********************************************************************************************
  955. * W3dExportClass::End_Progress_Bar -- end the progress meter *
  956. * *
  957. * INPUT: *
  958. * *
  959. * OUTPUT: *
  960. * *
  961. * WARNINGS: *
  962. * *
  963. * HISTORY: *
  964. * 10/16/1997 GH : Created. *
  965. *=============================================================================================*/
  966. void W3dExportClass::End_Progress_Bar(void)
  967. {
  968. MaxInterface->ProgressUpdate( 100);
  969. MaxInterface->ProgressEnd();
  970. }
  971. static bool dupe_check(const INodeListClass & list)
  972. {
  973. for (unsigned i=0; i<list.Num_Nodes(); i++) {
  974. /*
  975. ** Don't check aggregate objects, they are allowed to have the same name
  976. */
  977. if (!Is_Aggregate(list[i])) {
  978. for (unsigned j = i+1; j<list.Num_Nodes(); j++) {
  979. if (stricmp(list[i]->GetName(),list[j]->GetName()) == 0) {
  980. char buf[256];
  981. sprintf(buf,"Geometry Nodes with duplicated names found!\nDuplicated Name: %s\n",list[i]->GetName());
  982. MessageBox(NULL,buf,"Error",MB_OK | MB_SETFOREGROUND);
  983. return true;
  984. }
  985. }
  986. }
  987. }
  988. return false;
  989. }
  990. static bool check_lod_extensions (INodeListClass &list, INode *origin)
  991. {
  992. /*
  993. ** Assumptions:
  994. ** - If origin == NULL, then we're just exporting a single model and don't need to
  995. ** worry about lod extensions at all.
  996. ** - If origin is the root of the scene, then we're just exporting a single model as well.
  997. ** - Otherwise origin actually points to an Origin and not just any INode.
  998. */
  999. if (origin == NULL) return true;
  1000. if (origin->IsRootNode()) return true;
  1001. char *extension = strrchr(origin->GetName(), '.');
  1002. int ext_len = strlen(extension);
  1003. for (unsigned i = 0; i < list.Num_Nodes(); i++)
  1004. {
  1005. char *this_ext = strrchr(list[i]->GetName(), '.');
  1006. // Check for the existance of an extension in this node.
  1007. if (this_ext == NULL)
  1008. return false;
  1009. // Check that the extensions are the same.
  1010. if (strcmp(this_ext, extension) != 0)
  1011. return false;
  1012. }
  1013. return true;
  1014. }
  1015. bool W3dExportClass::get_base_object_tm (Matrix3 &tm)
  1016. {
  1017. INodeListClass *origin_list = get_origin_list();
  1018. if (!origin_list)
  1019. return false;
  1020. unsigned int i, count = origin_list->Num_Nodes();
  1021. INode *base_origin = NULL;
  1022. for (i = 0; i < count; i++)
  1023. {
  1024. INode *node = (*origin_list)[i];
  1025. if (Is_Base_Origin(node))
  1026. {
  1027. // we found origin.00, fall through
  1028. base_origin = node;
  1029. break;
  1030. }
  1031. }
  1032. if (!base_origin)
  1033. return false;
  1034. tm = base_origin->GetNodeTM(CurTime);
  1035. return true;
  1036. }
  1037. static DWORD WINAPI progress_callback( LPVOID arg )
  1038. {
  1039. return 0;
  1040. }
  1041. static HierarchySaveClass * load_hierarchy_file(char * filename)
  1042. {
  1043. HierarchySaveClass * hier = NULL;
  1044. RawFileClass file(filename);
  1045. if (!file.Open()) {
  1046. return NULL;
  1047. }
  1048. ChunkLoadClass cload(&file);
  1049. cload.Open_Chunk();
  1050. if (cload.Cur_Chunk_ID() == W3D_CHUNK_HIERARCHY) {
  1051. hier = new HierarchySaveClass();
  1052. hier->Load(cload);
  1053. } else {
  1054. hier = NULL;
  1055. file.Close();
  1056. return NULL;
  1057. }
  1058. cload.Close_Chunk();
  1059. file.Close();
  1060. return hier;
  1061. }