Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
geometryexporttask.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/***********************************************************************************************
20 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
21 ***********************************************************************************************
22 * *
23 * Project Name : Max2W3D *
24 * *
25 * $Archive:: /Commando/Code/Tools/max2w3d/geometryexporttask.cpp $*
26 * *
27 * Original Author:: Greg Hjelstrom *
28 * *
29 * $Author:: Greg_h $*
30 * *
31 * $Modtime:: 10/31/00 10:35a $*
32 * *
33 * $Revision:: 7 $*
34 * *
35 *---------------------------------------------------------------------------------------------*
36 * Functions: *
37 * GeometryExportTaskClass::GeometryExportTaskClass -- Constructor *
38 * GeometryExportTaskClass::GeometryExportTaskClass -- Copy Constructor *
39 * GeometryExportTaskClass::~GeometryExportTaskClass -- Destructor *
40 * GeometryExportTaskClass::Get_Full_Name -- Composes the full name of this robj *
41 * GeometryExportTaskClass::Create_Task -- virtual constructor for export tasks *
42 * GeometryExportTaskClass::Optimize_Geometry -- Optimizes the export tasks *
43 * GeometryExportTaskClass::Generate_Unique_Name -- create a unique name for this object *
44 * MeshGeometryExportTaskClass::Is_Single_Material -- Tests if this mesh uses a single mater *
45 * MeshGeometryExportTaskClass::Get_Single_Material -- returns pointer to the material *
46 * MeshGeometryExportTaskClass::Cache_Single_Material -- updates the cached material pointer *
47 * MeshGeometryExportTaskClass::Split -- Splits into single material meshes *
48 * MeshGeometryExportTaskClass::Reduce_To_Single_Material -- deletes polys *
49 * MeshGeometryExportTaskClass::Can_Combine -- can this mesh combine with anything *
50 * MeshGeometryExportTaskClass::Can_Combine_With -- can this mesh be combined with the given *
51 * MeshGeometryExportTaskClass::Combine_Mesh -- Add the given mesh into this mesh *
52 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
53
54#include "geometryexporttask.h"
56#include "util.h"
57#include "w3dutil.h"
58#include "w3dappdata.h"
59#include "hiersave.h"
60#include "maxworldinfo.h"
61#include "meshsave.h"
62#include "colboxsave.h"
63#include "dazzlesave.h"
64#include <bitarray.h>
65
66
67const int OPTIMIZATION_FACECOUNT_LIMIT = 256; // TODO: what should this number be...
68const float OPTIMIZATION_COMBINING_DISTANCE = 20.0f; // TODO: need a smarter method for combining...
69
70
76{
77public:
78
80 GeometryExportTaskClass(node,context),
83 {
84 /*
85 ** Copy the export options
86 */
88
89 /*
90 ** Copy the mesh
91 */
92 Object * obj = Node->EvalWorldState(CurTime).obj;
93 TriObject * tri = (TriObject *)obj->ConvertToType(CurTime, triObjectClassID);
94 MeshData = tri->mesh;
95
96 /*
97 ** Store a pointer to the material if this mesh uses only one material (even inside a Multi-Sub)
98 */
100 }
101
109
111 {
112 }
113
115 {
116 /*
117 ** Create the mesh
118 */
119 context.WorldInfo.Set_Current_Task(this);
121
122 MeshSaveClass * mesh = new MeshSaveClass( Name,
124 Node,
125 &MeshData,
128 context.HTree,
129 context.CurTime,
130 *context.ProgressMeter,
131 context.materialColors,
132 context.numMaterialColors,
133 context.numHouseColors,
134 context.materialColorTexture,
135 &context.WorldInfo
136 );
137
138 /*
139 ** Export It
140 */
141 mesh->Write_To_File(context.CSave,!context.Options.DisableExportAABTrees);
142 delete mesh;
143 context.ProgressMeter->Add_Increment();
144 };
145
146 /*
147 ** Naming. During the optimization phase, sometimes new meshes are created and require
148 ** new unique names. These meshes are flagged and then new names are generated prior
149 ** to exporting.
150 */
151 bool Is_Name_Dirty(void) { return NameDirty; }
152 void Set_Name_Dirty(bool onoff) { NameDirty = onoff; }
153
154 /*
155 ** Vertex Normal smoothing support!
156 */
157 virtual Point3 Get_Shared_Vertex_Normal(const Point3 & pos,int smgroup);
158
159 /*
160 ** Optimization functions
161 */
162 bool Is_Single_Material(void);
163 Mtl * Get_Single_Material(void);
165 void Reduce_To_Single_Material(int mat_id);
166 bool Can_Combine(void);
168 void Combine_Mesh(MeshGeometryExportTaskClass * other_mesh);
169
170protected:
171
172 virtual int Get_Geometry_Type(void) { return MESH; }
173 void Update_Cached_Data(void);
174
175 Mesh MeshData; // Copy of the mesh data to be exported.
176 W3DAppData2Struct ExportOptions; // Copy of the export options in case we want to change them during optimization
178
179 // Cached Data about the Node/Mesh. Updated by calling Update_Cached_Data whenever the mesh changes.
180 Mtl * SingleMtl; // Pointer to the single material (if the mesh uses only one, even in a multi-mtl)
181 Point3 BoxCenter; // Center of the bounding box (in object space)
182 Point3 BoxExtent; // Extent of the bounding box (in object space)
183 Box3 WorldBounds; // World-space bounding box
184};
185
186
187
188
189
195{
196public:
198 GeometryExportTaskClass(node,context)
199 {
200 }
201
203 {
204 /*
205 ** Create the collision box
206 */
209 Node,
211 context.CurTime,
212 *context.ProgressMeter);
213
214 /*
215 ** Export it
216 */
217 colbox->Write_To_File(context.CSave);
218 delete colbox;
219 context.ProgressMeter->Add_Increment();
220 };
221
222protected:
223
224 virtual int Get_Geometry_Type(void) { return COLLISIONBOX; }
225
226};
227
228
229
230
231
237{
238public:
240 GeometryExportTaskClass(node,context)
241 {
242 }
243
245 {
246 /*
247 ** Create the dazzle object
248 */
249 DazzleSaveClass * dazzle = new DazzleSaveClass( Name,
251 Node,
253 context.CurTime,
254 *context.ProgressMeter);
255 /*
256 ** Export it.
257 */
258 dazzle->Write_To_File(context.CSave);
259 delete dazzle;
260 context.ProgressMeter->Add_Increment();
261 };
262
263protected:
264
265 virtual int Get_Geometry_Type(void) { return DAZZLE; }
266
267};
268
269
270
271
279{
280public:
282 GeometryExportTaskClass(node,context)
283 {
284 memset(ContainerName,0,sizeof(ContainerName));
285 memset(Name,0,sizeof(Name));
286 strcpy(Name,"NULL");
287 }
288
290 {
291 context.ProgressMeter->Add_Increment();
292 };
293
294protected:
295
296 virtual int Get_Geometry_Type(void) { return NULLOBJ; }
297
298};
299
300
310{
311public:
313 GeometryExportTaskClass(node,context)
314 {
315 memset(ContainerName,0,sizeof(ContainerName));
316 }
317
319 {
320 context.ProgressMeter->Add_Increment();
321 };
322
323 virtual bool Is_Aggregate(void)
324 {
325 return true;
326 }
327
328protected:
329
330 virtual int Get_Geometry_Type(void) { return AGGREGATE; }
331
332};
333
334
344{
345public:
346
348 GeometryExportTaskClass(node,context)
349 {
350 /*
351 ** clear the container name
352 */
353 memset(ContainerName,0,sizeof(ContainerName));
354
355 /*
356 ** strip the trailing ~
357 */
358 char *tilda = ::strchr(Name, '~');
359 memset(tilda,0,sizeof(Name) - ((int)tilda - (int)Name));
360
361 }
362
364 {
365 context.ProgressMeter->Add_Increment();
366 };
367
368 virtual bool Is_Proxy(void) { return true; }
369
370protected:
371
372 virtual int Get_Geometry_Type(void) { return PROXY; }
373
374};
375
376
377
378
379
380/***********************************************************************************************
381**
382** Implementations
383**
384***********************************************************************************************/
385
386/***********************************************************************************************
387 * GeometryExportTaskClass::GeometryExportTaskClass -- Constructor *
388 * *
389 * INPUT: *
390 * *
391 * OUTPUT: *
392 * *
393 * WARNINGS: *
394 * *
395 * HISTORY: *
396 * 10/20/2000 gth : Created. *
397 *=============================================================================================*/
399 BoneIndex(0),
400 ExportSpace(1),
401 CurTime(context.CurTime),
402 Node(node)
403{
404 /*
405 ** Set up the names
406 */
407 Set_W3D_Name(Name,Node->GetName());
410
411 /*
412 ** Set up the bone index and export coordinate system.
413 */
414 if (context.HTree != NULL) {
415 if (!Is_Skin(node)) {
417 } else {
418 BoneIndex = 0;
420 }
421 }
422}
423
424
425/***********************************************************************************************
426 * GeometryExportTaskClass::GeometryExportTaskClass -- Copy Constructor *
427 * *
428 * INPUT: *
429 * *
430 * OUTPUT: *
431 * *
432 * WARNINGS: *
433 * *
434 * HISTORY: *
435 * 10/20/2000 gth : Created. *
436 *=============================================================================================*/
446
447
448/***********************************************************************************************
449 * GeometryExportTaskClass::~GeometryExportTaskClass -- Destructor *
450 * *
451 * INPUT: *
452 * *
453 * OUTPUT: *
454 * *
455 * WARNINGS: *
456 * *
457 * HISTORY: *
458 *=============================================================================================*/
462
463
464/***********************************************************************************************
465 * GeometryExportTaskClass::Get_Full_Name -- Composes the full name of this robj *
466 * *
467 * INPUT: *
468 * *
469 * OUTPUT: *
470 * *
471 * WARNINGS: *
472 * *
473 * HISTORY: *
474 * 10/24/2000 gth : Created. *
475 *=============================================================================================*/
476void GeometryExportTaskClass::Get_Full_Name(char * buffer,int size)
477{
478 char tmp[128];
479 memset(tmp,0,sizeof(tmp));
480 if (strlen(ContainerName) > 0) {
481 strcat(tmp,ContainerName);
482 strcat(tmp,".");
483 }
484 strcat(tmp,Name);
485
486 strncpy(buffer,tmp,size);
487}
488
489
490/***********************************************************************************************
491 * GeometryExportTaskClass::Create_Task -- virtual constructor for export tasks *
492 * *
493 * Virtual constructor for geometry export tasks. Will create the proper task *
494 * type depending on the W3D flag settings. *
495 * *
496 * INPUT: *
497 * *
498 * OUTPUT: *
499 * *
500 * WARNINGS: *
501 * *
502 * HISTORY: *
503 * 10/20/2000 gth : Created. *
504 *=============================================================================================*/
507{
508 if (!::Is_Geometry(node)) {
509 return NULL;
510 }
511
512 // NOTE: we *have* to check Is_Proxy first because it is tied to a naming convention
513 // rather than an explicit UI setting like the rest of the types.
514 if (::Is_Proxy(*node)) {
515 return new ProxyExportTaskClass(node,context);
516 }
517
518 if (::Is_Normal_Mesh(node) || Is_Camera_Aligned_Mesh(node) || Is_Camera_Oriented_Mesh(node) || Is_Skin(node)) {
519 return new MeshGeometryExportTaskClass(node,context);
520 }
521
522 if (::Is_Collision_AABox(node) || Is_Collision_OBBox(node)) {
523 return new CollisionBoxGeometryExportTaskClass(node,context);
524 }
525
526 if (::Is_Null_Object(node)) {
527 return new NullGeometryExportTaskClass(node,context);
528 }
529
530 if (::Is_Dazzle(node)) {
531 return new DazzleGeometryExportTaskClass(node,context);
532 }
533
534 if (::Is_Aggregate(node)) {
535 return new AggregateGeometryExportTaskClass(node,context);
536 }
537
538 return NULL;
539}
540
541
542/***********************************************************************************************
543 * GeometryExportTaskClass::Optimize_Geometry -- Optimizes the export tasks *
544 * *
545 * This function will attempt to split meshes so that they use only a single material and *
546 * then try to combine small meshes that use the same material. Export tasks may be *
547 * removed and new ones added. *
548 * *
549 * INPUT: *
550 * tasks - dynamic vector of export task pointers. Some tasks may be deleted, some added *
551 * *
552 * OUTPUT: *
553 * *
554 * WARNINGS: *
555 * *
556 * HISTORY: *
557 * 10/20/2000 gth : Created. *
558 *=============================================================================================*/
560(
563)
564{
565 int j=0,i=0;
566
567 /*
568 ** Pass 1: Extract all mesh geometry tasks from the input task array.
569 ** NOTE: We're not optimizing Skin meshes so we leave them in the task array.
570 */
572 while (i<tasks.Count()) {
573 if ( (tasks[i]->Get_Geometry_Type() == GeometryExportTaskClass::MESH) &&
574 (!Is_Skin(tasks[i]->Get_Object_Node())) )
575 {
576 /*
577 ** Add to the mesh array, remove from the tasks array
578 */
579 meshes.Add((MeshGeometryExportTaskClass *)(tasks[i]));
580 tasks.Delete(i);
581 } else {
582
583 /*
584 ** Leave in the task array and move to the next one.
585 */
586 i++;
587 }
588 }
589
590 /*
591 ** Pass 2: Split all meshes which use more than one material
592 */
594 while (meshes.Count() > 0) {
595
596 int cur_index = meshes.Count() - 1;
597 MeshGeometryExportTaskClass * cur_mesh = meshes[cur_index];
598
599 /*
600 ** If this mesh already uses only one material, just transfer it to the simple_meshes array.
601 ** Otherwise, have it split into new tasks, add them to the simple_meshes array, and delete this task.
602 */
603 if (cur_mesh->Is_Single_Material()) {
604 simple_meshes.Add(cur_mesh);
605 } else {
606 cur_mesh->Split(simple_meshes);
607 delete cur_mesh;
608 }
609 meshes.Delete(cur_index);
610 }
611
612 /*
613 ** Pass 3: Combine meshes which satisfy the following
614 ** - They use the same (single) material
615 ** - They have fewer than 'x' polygons
616 ** - They are 'close' to each other
617 */
618 i=0;
619 while (i < simple_meshes.Count()) {
620
621 if (simple_meshes[i]->Can_Combine()) {
622
623 j=i+1;
624 while (j < simple_meshes.Count()) {
625 if (simple_meshes[i]->Can_Combine_With(simple_meshes[j])) {
626
627 /*
628 ** Add mesh 'j' into mesh 'i', delete its task.
629 */
630 simple_meshes[i]->Combine_Mesh(simple_meshes[j]);
631 delete simple_meshes[j];
632 simple_meshes.Delete(j);
633
634 /*
635 ** If we've just exceeded the max poly count, move to the next mesh
636 */
637 if (simple_meshes[i]->Can_Combine() == false) {
638 j = simple_meshes.Count();
639 }
640
641 } else {
642
643 /*
644 ** Otherwise, move to the next mesh
645 */
646 j++;
647 }
648 }
649 }
650 i++;
651 }
652
653
654
655 /*
656 ** Generate names for each of the meshes that were created
657 */
658 for (i=0; i<simple_meshes.Count(); i++) {
659// if (simple_meshes[i]->Is_Name_Dirty()) {
660 simple_meshes[i]->Generate_Name("MESH",i,context);
661// }
662 }
663
664 /*
665 ** Finally, transfer all of the optimized tasks into the big task array
666 */
667 for (i=0; i<simple_meshes.Count(); i++) {
668 tasks.Add(simple_meshes[i]);
669 }
670 simple_meshes.Delete_All();
671
672}
673
674
675/***********************************************************************************************
676 * GeometryExportTaskClass::Generate_Name -- create a name for this object *
677 * *
678 * During optimization, we can generate new meshes which need unique names. *
679 * *
680 * INPUT: *
681 * index - will be used to create the name, don't re-use the same index within an LOD *
682 * context - geometry export context *
683 * *
684 * OUTPUT: *
685 * *
686 * WARNINGS: *
687 * *
688 * HISTORY: *
689 * 10/23/2000 gth : Created. *
690 *=============================================================================================*/
692{
693 /*
694 ** Check if the original had a Renegade "building prefix" in its name. Building meshes
695 ** are named with a 2-3 letter user specified prefix followed by either '^' or '#'. We
696 ** have to maintain this prefix on the optimized meshes...
697 */
698 char prefix[5];
699 strncpy(prefix,Name,sizeof(prefix));
700 prefix[4] = 0;
701
702 char * interior_prefix = strchr(prefix,'^');
703 char * exterior_prefix = strchr(prefix,'#');
704
705 memset(Name,0,sizeof(Name));
706 if (interior_prefix != NULL) {
707 strncpy(Name,prefix,(int)(interior_prefix - prefix) + 1);
708 } else if (exterior_prefix != NULL) {
709 strncpy(Name,prefix,(int)(exterior_prefix - prefix) + 1);
710 }
711
712 /*
713 ** Append "root"{index} to the name
714 */
715 strcat(Name,root);
716 strcat(Name,"{");
717 sprintf(Name + strlen(Name),"%04d",index);
718 strcat(Name,"}");
719
720 /*
721 ** Append the LOD index so the generated name doesn't collide with meshes in the same
722 ** object but in other LOD's
723 */
724 sprintf(Name + strlen(Name),"%c",'a'+Get_Lod_Level(context.Origin));
725}
726
727
728/***********************************************************************************************
729 * MeshGeometryExportTaskClass::Update_Cached_Data -- updates the cached material pointer, etc *
730 * *
731 * This should be called after the meshdata changes *
732 * *
733 * INPUT: *
734 * *
735 * OUTPUT: *
736 * *
737 * WARNINGS: *
738 * *
739 * HISTORY: *
740 * 10/23/2000 gth : Created. *
741 *=============================================================================================*/
743{
744 SingleMtl = NULL;
745 Mtl * nodemtl = Node->GetMtl();
746
747 /*
748 ** Set the SingleMtl pointer if this mesh uses only one material (again, even if its in a Multi-Sub)
749 */
750 if (nodemtl == NULL) {
751
752 SingleMtl = NULL;
753
754 } else if (nodemtl->NumSubMtls() <= 1) {
755
756 SingleMtl = nodemtl;
757
758 } else {
759
760 int mat_index;
761 int face_index;
762 int sub_mtl_count = nodemtl->NumSubMtls();
763 bool * sub_mtl_flags = new bool[sub_mtl_count];
764
765 /*
766 ** Initialize each sub-material flag to false (indicates that the material is un-used)
767 */
768 for (mat_index=0; mat_index<sub_mtl_count; mat_index++) {
769 sub_mtl_flags[mat_index] = false;
770 }
771
772 /*
773 ** Set a true for each material actually referenced by the mesh
774 */
775 for (face_index=0; face_index<MeshData.getNumFaces(); face_index++) {
776 int max_mat_index = MeshData.faces[face_index].getMatID();
777 int mat_index = (max_mat_index % sub_mtl_count);
778 sub_mtl_flags[mat_index] = true;
779 }
780
781 /*
782 ** Loop over the used materials counting how many 'true's we see
783 */
784 int mat_count = 0;
785 for (mat_index=0; mat_index<sub_mtl_count; mat_index++) {
786 if (sub_mtl_flags[mat_index]) {
787 SingleMtl = nodemtl->GetSubMtl(mat_index);
788 mat_count++;
789 }
790 }
791
792 if (mat_count > 1) {
793 SingleMtl = NULL;
794 }
795 }
796
797 /*
798 ** Update the bounding box
799 */
800 Point3 boxmin(0,0,0);
801 Point3 boxmax(0,0,0);
802
803 if (MeshData.numVerts > 0) {
804 boxmin = MeshData.verts[1];
805 boxmax = MeshData.verts[0];
806 for (int i=0; i<MeshData.numVerts; i++) {
807 boxmin.x = MIN(MeshData.verts[i].x,boxmin.x);
808 boxmin.y = MIN(MeshData.verts[i].y,boxmin.y);
809 boxmin.z = MIN(MeshData.verts[i].z,boxmin.z);
810
811 boxmax.x = MAX(MeshData.verts[i].x,boxmax.x);
812 boxmax.y = MAX(MeshData.verts[i].y,boxmax.y);
813 boxmax.z = MAX(MeshData.verts[i].z,boxmax.z);
814 }
815 }
816
817 boxmax += Point3(0.1f,0.1f,0.1f);
818 boxmin -= Point3(0.1f,0.1f,0.1f);
819
820 BoxCenter = (boxmax + boxmin) * 0.5f;
821 BoxExtent = (boxmax - boxmin) * 0.5f;
822
823 WorldBounds = Box3(boxmin,boxmax) * Node->GetObjectTM(CurTime);
824}
825
826
827/***********************************************************************************************
828 * MeshGeometryExportTaskClass::Is_Single_Material -- Tests if this mesh uses a single materia *
829 * *
830 * INPUT: *
831 * *
832 * OUTPUT: *
833 * *
834 * WARNINGS: *
835 * *
836 * HISTORY: *
837 * 10/20/2000 gth : Created. *
838 *=============================================================================================*/
840{
841 return ((SingleMtl != NULL) || (Node->GetMtl() == NULL));
842}
843
844
845/***********************************************************************************************
846 * MeshGeometryExportTaskClass::Get_Single_Material -- returns pointer to the material *
847 * *
848 * INPUT: *
849 * *
850 * OUTPUT: *
851 * *
852 * WARNINGS: *
853 * *
854 * HISTORY: *
855 * 10/23/2000 gth : Created. *
856 *=============================================================================================*/
861
862/***********************************************************************************************
863 * MeshGeometryExportTaskClass::Split -- Splits into single material meshes *
864 * *
865 * This function will create new export tasks and add them to the supplied array. Each of *
866 * these will be single-material meshes. *
867 * *
868 * INPUT: *
869 * *
870 * OUTPUT: *
871 * *
872 * WARNINGS: *
873 * *
874 * HISTORY: *
875 * 10/20/2000 gth : Created. *
876 *=============================================================================================*/
878{
879 assert(!Is_Single_Material());
880
881 Mtl * nodemtl = Node->GetMtl();
882
883 int mat_index;
884 int face_index;
885 int sub_mtl_count = nodemtl->NumSubMtls();
886 bool * sub_mtl_flags = new bool[sub_mtl_count];
887
888 /*
889 ** Initialize each sub-material flag to false (indicates that the material is un-used)
890 */
891 for (mat_index=0; mat_index<sub_mtl_count; mat_index++) {
892 sub_mtl_flags[mat_index] = false;
893 }
894
895 /*
896 ** Set a true for each material actually referenced by the mesh
897 */
898 for (face_index=0; face_index<MeshData.getNumFaces(); face_index++) {
899 int max_mat_index = MeshData.faces[face_index].getMatID();
900 int mat_index = (max_mat_index % sub_mtl_count);
901 sub_mtl_flags[mat_index] = true;
902 }
903
904 /*
905 ** Loop over the used materials, creating a new MeshGeometryExportTaskClass for each one
906 */
907 for (mat_index=0; mat_index<sub_mtl_count; mat_index++) {
908 if (sub_mtl_flags[mat_index]) {
909
911 new_task->Reduce_To_Single_Material(mat_index);
912 new_task->Set_Name_Dirty(true);
913 simple_meshes.Add(new_task);
914
915 }
916 }
917}
918
919
920/***********************************************************************************************
921 * MeshGeometryExportTaskClass::Reduce_To_Single_Material -- deletes polys *
922 * *
923 * This function deletes all polys (and subsequent un-used vertices) except for the ones *
924 * that use the specified material. *
925 * *
926 * INPUT: *
927 * mat_id - only faces using this material id will remain in the mesh. *
928 * *
929 * OUTPUT: *
930 * *
931 * WARNINGS: *
932 * *
933 * HISTORY: *
934 * 10/20/2000 gth : Created. *
935 *=============================================================================================*/
937{
938 int sub_mtl_count = Node->GetMtl()->NumSubMtls();
939
940 BitArray faces_to_delete(MeshData.getNumFaces());
941 BitArray verts_to_delete(MeshData.getNumVerts());
942
943 faces_to_delete.ClearAll();
944 verts_to_delete.ClearAll();
945
946 for (int i=0; i<MeshData.getNumFaces(); i++) {
947 if ((MeshData.faces[i].getMatID() % sub_mtl_count) != mat_id) {
948 faces_to_delete.Set(i,true);
949 }
950 }
951
952 MeshData.DeleteFaceSet(faces_to_delete,&verts_to_delete);
953 MeshData.DeleteVertSet(verts_to_delete);
954
956}
957
958
959/***********************************************************************************************
960 * MeshGeometryExportTaskClass::Can_Combine -- can this mesh combine with anything *
961 * *
962 * This will return false if the mesh has multiple materials, too many polys, etc. *
963 * *
964 * INPUT: *
965 * *
966 * OUTPUT: *
967 * *
968 * WARNINGS: *
969 * *
970 * HISTORY: *
971 * 10/24/2000 gth : Created. *
972 *=============================================================================================*/
974{
975 /*
976 ** This mesh can't combine at all if:
977 ** - it has multiple materials
978 ** - its polygon count is too high
979 ** - (Renegade) VIS is enabled (don't want to create really wierd shaped vis-sectors...)
980 */
981 if (!Is_Single_Material()) {
982 return false;
983 }
984 if (MeshData.numFaces > OPTIMIZATION_FACECOUNT_LIMIT) {
985 return false;
986 }
987 if (ExportOptions.Is_Vis_Collision_Enabled()) {
988 return false;
989 }
990 return true;
991}
992
993
994/***********************************************************************************************
995 * MeshGeometryExportTaskClass::Can_Combine_With -- can this mesh be combined with the given m *
996 * *
997 * INPUT: *
998 * *
999 * OUTPUT: *
1000 * *
1001 * WARNINGS: *
1002 * *
1003 * HISTORY: *
1004 * 10/24/2000 gth : Created. *
1005 *=============================================================================================*/
1007{
1008 /*
1009 ** Does the mesh attach to the same W3D bone that we do?
1010 */
1011 if (other_mesh->BoneIndex != BoneIndex) {
1012 return false;
1013 }
1014
1015 /*
1016 ** Does the mesh use the same (single) material that we do?
1017 */
1018 Mtl * other_mtl = other_mesh->Get_Single_Material();
1019 if (other_mtl == NULL) {
1020 return false;
1021 }
1022
1023 Mtl * my_mtl = Get_Single_Material();
1024 if (my_mtl != other_mtl) {
1025 return false;
1026 }
1027
1028 /*
1029 ** Are its relevant W3D options the same as ours?
1030 */
1031 if (ExportOptions.Geometry_Options_Match(other_mesh->ExportOptions)) {
1032 return false;
1033 }
1034
1035 /*
1036 ** Would our combined polygon count be reasonable
1037 */
1038 if (MeshData.numFaces + other_mesh->MeshData.numFaces > OPTIMIZATION_FACECOUNT_LIMIT) {
1039 return false;
1040 }
1041
1042 /*
1043 ** Is the other mesh near me?
1044 */
1045 Point3 my_center = Node->GetObjectTM(CurTime) * BoxCenter;
1046 Point3 other_center = other_mesh->Node->GetObjectTM(CurTime) * BoxCenter;
1047 float dist = ::FLength(my_center - other_center);
1049 return false;
1050 }
1051
1052 return true;
1053}
1054
1055
1056/***********************************************************************************************
1057 * MeshGeometryExportTaskClass::Combine_Mesh -- Add the given mesh into this mesh *
1058 * *
1059 * INPUT: *
1060 * *
1061 * OUTPUT: *
1062 * *
1063 * WARNINGS: *
1064 * *
1065 * HISTORY: *
1066 * 10/24/2000 gth : Created. *
1067 *=============================================================================================*/
1069{
1070 /*
1071 ** Compute the transform from other_mesh's coordinate system to ours so that
1072 ** its polygons can be combined with ours (by calling CombineMeshes)
1073 */
1074 Matrix3 our_tm = Node->GetObjectTM(CurTime);
1075 Matrix3 his_tm = other_mesh->Node->GetObjectTM(CurTime);
1076 Matrix3 tm = Inverse(our_tm) * his_tm;
1077
1078 /*
1079 ** Store our current material index
1080 */
1081 int matid = MeshData.faces[0].getMatID();
1082 if (Node->GetMtl()->NumSubMtls() > 1) {
1083 matid = matid % Node->GetMtl()->NumSubMtls();
1084 }
1085
1086 /*
1087 ** Combine the meshes
1088 */
1089 Mesh new_mesh;
1090 ::CombineMeshes(new_mesh,MeshData,other_mesh->MeshData,&our_tm,&his_tm,0);
1091 MeshData = new_mesh;
1092
1093 /*
1094 ** Set all material ID's
1095 */
1096 for (int i=0; i<MeshData.numFaces; i++) {
1097 MeshData.faces[i].setMatID(matid);
1098 }
1099}
1100
1101Point3 MeshGeometryExportTaskClass::Get_Shared_Vertex_Normal(const Point3 & world_pos,int smgroup)
1102{
1103 const float EPSILON = 0.001f;
1104 Point3 normal(0,0,0);
1105
1106 /*
1107 ** Does the bounding box for this node even contain the point
1108 ** we are looking for?
1109 */
1110 if (WorldBounds.Contains(world_pos) != 0) {
1111
1112 /*
1113 ** Transform the query point into object space
1114 */
1115 Matrix3 tm = Node->GetObjectTM(CurTime);
1116 Point3 obj_pos = world_pos * Inverse(tm);
1117
1118 /*
1119 ** Loop through all the faces in this mesh and find out which ones
1120 ** share the same smoothing group as the vertex we are looking for.
1121 */
1122 for (int face_index = 0; face_index < MeshData.numFaces; face_index ++) {
1123 Face &maxface = MeshData.faces[face_index];
1124 int face_smgroup = maxface.getSmGroup();
1125 if ((face_smgroup & smgroup) || (face_smgroup == smgroup)) {
1126
1127 /*
1128 ** Find out if any of the verticies of this face share the
1129 ** same space as the vertex we are looking for.
1130 */
1131 bool found = false;
1132 for (int vert_index = 0; (vert_index < 3) && !found; vert_index ++) {
1133 int max_vert_index = maxface.v[vert_index];
1134 Point3 delta = obj_pos - MeshData.verts[max_vert_index];
1135 if ((fabs (delta.x) < EPSILON) &&
1136 (fabs (delta.y) < EPSILON) &&
1137 (fabs (delta.z) < EPSILON))
1138 {
1139
1140 /*
1141 ** Compute the normal for this face
1142 */
1143 Point3 v0 = MeshData.verts[maxface.v[0]];
1144 Point3 v1 = MeshData.verts[maxface.v[1]];
1145 Point3 v2 = MeshData.verts[maxface.v[2]];
1146 Point3 face_normal = (v1-v0)^(v2-v1);
1147 face_normal = ::Normalize(face_normal);
1148
1149 /*
1150 ** Add this face normal to the sum
1151 */
1152 normal.x += face_normal.x;
1153 normal.y += face_normal.y;
1154 normal.z += face_normal.z;
1155
1156 /*
1157 ** Done with this face, look for more
1158 */
1159 found = true;
1160 }
1161 }
1162 }
1163 }
1164
1165 /*
1166 ** Transform the "normal" to world space. Note that this vector isn't
1167 ** normalized because we are basically summing the contributions of each
1168 ** face in each mesh which shares this normal. The final normal
1169 ** will be normalized in the MeshBuilderClass.
1170 */
1171 tm.NoTrans();
1172 normal = tm.PointTransform(normal);
1173 }
1174
1175 return normal;
1176}
#define NULL
Definition BaseType.h:92
Real Normalize(Real valueToNormalize, Real minRange, Real maxRange)
const float EPSILON
Definition meshbuild.cpp:68
#define MIN(a, b)
Definition always.h:189
#define MAX(a, b)
Definition always.h:185
@ false
Definition bool.h:59
AggregateGeometryExportTaskClass(INode *node, GeometryExportContextClass &context)
virtual void Export_Geometry(GeometryExportContextClass &context)
CollisionBoxGeometryExportTaskClass(INode *node, GeometryExportContextClass &context)
virtual void Export_Geometry(GeometryExportContextClass &context)
int Write_To_File(ChunkSaveClass &csave)
virtual void Export_Geometry(GeometryExportContextClass &context)
DazzleGeometryExportTaskClass(INode *node, GeometryExportContextClass &context)
int Write_To_File(ChunkSaveClass &csave)
void Delete_All(void)
Definition Vector.H:892
int Count(void) const
Definition Vector.H:507
bool Delete(T const &object)
Definition Vector.H:820
bool Add(T const &object)
Definition Vector.H:671
Progress_Meter_Class * ProgressMeter
char * materialColorTexture
MW: number of used house colors.
W3dExportOptionsStruct & Options
int numHouseColors
MW: number of used material colors.
int numMaterialColors
MW: holds all used material colors.
void Generate_Name(char *root, int index, GeometryExportContextClass &context)
GeometryExportTaskClass(INode *node, GeometryExportContextClass &context)
static GeometryExportTaskClass * Create_Task(INode *node, GeometryExportContextClass &context)
virtual bool Is_Proxy(void)
virtual int Get_Geometry_Type(void)=0
static void Optimize_Geometry(DynamicVectorClass< GeometryExportTaskClass * > &tasks, GeometryExportContextClass &context)
virtual bool Is_Aggregate(void)
void Get_Full_Name(char *buffer, int size)
char ContainerName[W3D_NAME_LEN]
void Get_Export_Coordinate_System(INode *node, int *set_bone_index, INode **set_bone_node, Matrix3 *set_transform)
Definition hiersave.cpp:409
virtual void Set_Current_Task(GeometryExportTaskClass *task)
virtual void Set_Export_Transform(const Matrix3 &matrix)
void Split(DynamicVectorClass< MeshGeometryExportTaskClass * > &simple_meshes)
void Combine_Mesh(MeshGeometryExportTaskClass *other_mesh)
virtual void Export_Geometry(GeometryExportContextClass &context)
bool Can_Combine_With(MeshGeometryExportTaskClass *other_mesh)
MeshGeometryExportTaskClass(const MeshGeometryExportTaskClass &that)
MeshGeometryExportTaskClass(INode *node, GeometryExportContextClass &context)
virtual Point3 Get_Shared_Vertex_Normal(const Point3 &pos, int smgroup)
int Write_To_File(ChunkSaveClass &csave, bool export_aabtrees=false)
Definition meshsave.cpp:935
NullGeometryExportTaskClass(INode *node, GeometryExportContextClass &context)
virtual void Export_Geometry(GeometryExportContextClass &context)
virtual int Get_Geometry_Type(void)
virtual void Export_Geometry(GeometryExportContextClass &context)
virtual bool Is_Proxy(void)
ProxyExportTaskClass(INode *node, GeometryExportContextClass &context)
const int OPTIMIZATION_FACECOUNT_LIMIT
const float OPTIMIZATION_COMBINING_DISTANCE
WWINLINE Quaternion Inverse(const Quaternion &a)
Definition quat.h:117
static W3DAppData2Struct * Get_App_Data(INode *node, bool create_if_missing=true)
int Get_Lod_Level(INode *node)
Definition util.cpp:563
void Set_W3D_Name(char *set_name, const char *src)
Definition util.cpp:112
bool Append_Lod_Character(char *meshname, int lod_level, INodeListClass *origin_list)
Definition util.cpp:188
bool Is_Collision_OBBox(INode *node)
bool Is_Dazzle(INode *node)
bool Is_Geometry(INode *node)
bool Is_Normal_Mesh(INode *node)
bool Is_Camera_Oriented_Mesh(INode *node)
bool Is_Camera_Aligned_Mesh(INode *node)
bool Is_Skin(INode *node)
bool Is_Null_Object(INode *node)
bool Is_Collision_AABox(INode *node)