w3dexp.cpp 49 KB

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