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