geometryexporttask.cpp 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176
  1. /*
  2. ** Command & Conquer Generals(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. *** 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 ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Max2W3D *
  23. * *
  24. * $Archive:: /Commando/Code/Tools/max2w3d/geometryexporttask.cpp $*
  25. * *
  26. * Original Author:: Greg Hjelstrom *
  27. * *
  28. * $Author:: Greg_h $*
  29. * *
  30. * $Modtime:: 10/31/00 10:35a $*
  31. * *
  32. * $Revision:: 7 $*
  33. * *
  34. *---------------------------------------------------------------------------------------------*
  35. * Functions: *
  36. * GeometryExportTaskClass::GeometryExportTaskClass -- Constructor *
  37. * GeometryExportTaskClass::GeometryExportTaskClass -- Copy Constructor *
  38. * GeometryExportTaskClass::~GeometryExportTaskClass -- Destructor *
  39. * GeometryExportTaskClass::Get_Full_Name -- Composes the full name of this robj *
  40. * GeometryExportTaskClass::Create_Task -- virtual constructor for export tasks *
  41. * GeometryExportTaskClass::Optimize_Geometry -- Optimizes the export tasks *
  42. * GeometryExportTaskClass::Generate_Unique_Name -- create a unique name for this object *
  43. * MeshGeometryExportTaskClass::Is_Single_Material -- Tests if this mesh uses a single mater *
  44. * MeshGeometryExportTaskClass::Get_Single_Material -- returns pointer to the material *
  45. * MeshGeometryExportTaskClass::Cache_Single_Material -- updates the cached material pointer *
  46. * MeshGeometryExportTaskClass::Split -- Splits into single material meshes *
  47. * MeshGeometryExportTaskClass::Reduce_To_Single_Material -- deletes polys *
  48. * MeshGeometryExportTaskClass::Can_Combine -- can this mesh combine with anything *
  49. * MeshGeometryExportTaskClass::Can_Combine_With -- can this mesh be combined with the given *
  50. * MeshGeometryExportTaskClass::Combine_Mesh -- Add the given mesh into this mesh *
  51. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  52. #include "geometryexporttask.h"
  53. #include "geometryexportcontext.h"
  54. #include "util.h"
  55. #include "w3dutil.h"
  56. #include "w3dappdata.h"
  57. #include "hiersave.h"
  58. #include "maxworldinfo.h"
  59. #include "meshsave.h"
  60. #include "colboxsave.h"
  61. #include "dazzlesave.h"
  62. #include <bitarray.h>
  63. const int OPTIMIZATION_FACECOUNT_LIMIT = 256; // TODO: what should this number be...
  64. const float OPTIMIZATION_COMBINING_DISTANCE = 20.0f; // TODO: need a smarter method for combining...
  65. /**
  66. ** MeshGeometryExportTaskClass
  67. ** Export task for INodes which are to generate W3D meshes
  68. */
  69. class MeshGeometryExportTaskClass : public GeometryExportTaskClass
  70. {
  71. public:
  72. MeshGeometryExportTaskClass(INode * node,GeometryExportContextClass & context) :
  73. GeometryExportTaskClass(node,context),
  74. NameDirty(false),
  75. SingleMtl(NULL)
  76. {
  77. /*
  78. ** Copy the export options
  79. */
  80. ExportOptions = *(W3DAppData2Struct::Get_App_Data(Node));
  81. /*
  82. ** Copy the mesh
  83. */
  84. Object * obj = Node->EvalWorldState(CurTime).obj;
  85. TriObject * tri = (TriObject *)obj->ConvertToType(CurTime, triObjectClassID);
  86. MeshData = tri->mesh;
  87. /*
  88. ** Store a pointer to the material if this mesh uses only one material (even inside a Multi-Sub)
  89. */
  90. Update_Cached_Data();
  91. }
  92. MeshGeometryExportTaskClass(const MeshGeometryExportTaskClass & that) :
  93. GeometryExportTaskClass(that),
  94. MeshData(that.MeshData),
  95. ExportOptions(that.ExportOptions),
  96. NameDirty(false)
  97. {
  98. }
  99. virtual ~MeshGeometryExportTaskClass(void)
  100. {
  101. }
  102. virtual void Export_Geometry(GeometryExportContextClass & context)
  103. {
  104. /*
  105. ** Create the mesh
  106. */
  107. context.WorldInfo.Set_Current_Task(this);
  108. context.WorldInfo.Set_Export_Transform(ExportSpace);
  109. MeshSaveClass * mesh = new MeshSaveClass( Name,
  110. ContainerName,
  111. Node,
  112. &MeshData,
  113. ExportSpace,
  114. ExportOptions,
  115. context.HTree,
  116. context.CurTime,
  117. *context.ProgressMeter,
  118. context.materialColors,
  119. context.numMaterialColors,
  120. context.numHouseColors,
  121. context.materialColorTexture,
  122. &context.WorldInfo
  123. );
  124. /*
  125. ** Export It
  126. */
  127. mesh->Write_To_File(context.CSave,!context.Options.DisableExportAABTrees);
  128. delete mesh;
  129. context.ProgressMeter->Add_Increment();
  130. };
  131. /*
  132. ** Naming. During the optimization phase, sometimes new meshes are created and require
  133. ** new unique names. These meshes are flagged and then new names are generated prior
  134. ** to exporting.
  135. */
  136. bool Is_Name_Dirty(void) { return NameDirty; }
  137. void Set_Name_Dirty(bool onoff) { NameDirty = onoff; }
  138. /*
  139. ** Vertex Normal smoothing support!
  140. */
  141. virtual Point3 Get_Shared_Vertex_Normal(const Point3 & pos,int smgroup);
  142. /*
  143. ** Optimization functions
  144. */
  145. bool Is_Single_Material(void);
  146. Mtl * Get_Single_Material(void);
  147. void Split(DynamicVectorClass<MeshGeometryExportTaskClass *> & simple_meshes);
  148. void Reduce_To_Single_Material(int mat_id);
  149. bool Can_Combine(void);
  150. bool Can_Combine_With(MeshGeometryExportTaskClass * other_mesh);
  151. void Combine_Mesh(MeshGeometryExportTaskClass * other_mesh);
  152. protected:
  153. virtual int Get_Geometry_Type(void) { return MESH; }
  154. void Update_Cached_Data(void);
  155. Mesh MeshData; // Copy of the mesh data to be exported.
  156. W3DAppData2Struct ExportOptions; // Copy of the export options in case we want to change them during optimization
  157. bool NameDirty;
  158. // Cached Data about the Node/Mesh. Updated by calling Update_Cached_Data whenever the mesh changes.
  159. Mtl * SingleMtl; // Pointer to the single material (if the mesh uses only one, even in a multi-mtl)
  160. Point3 BoxCenter; // Center of the bounding box (in object space)
  161. Point3 BoxExtent; // Extent of the bounding box (in object space)
  162. Box3 WorldBounds; // World-space bounding box
  163. };
  164. /**
  165. ** CollisionBoxGeometryExportTaskClass
  166. ** Export task for INodes which are to generate W3D AABoxes or OBBoxes
  167. */
  168. class CollisionBoxGeometryExportTaskClass : public GeometryExportTaskClass
  169. {
  170. public:
  171. CollisionBoxGeometryExportTaskClass(INode * node,GeometryExportContextClass & context) :
  172. GeometryExportTaskClass(node,context)
  173. {
  174. }
  175. virtual void Export_Geometry(GeometryExportContextClass & context)
  176. {
  177. /*
  178. ** Create the collision box
  179. */
  180. CollisionBoxSaveClass * colbox = new CollisionBoxSaveClass( Name,
  181. ContainerName,
  182. Node,
  183. ExportSpace,
  184. context.CurTime,
  185. *context.ProgressMeter);
  186. /*
  187. ** Export it
  188. */
  189. colbox->Write_To_File(context.CSave);
  190. delete colbox;
  191. context.ProgressMeter->Add_Increment();
  192. };
  193. protected:
  194. virtual int Get_Geometry_Type(void) { return COLLISIONBOX; }
  195. };
  196. /**
  197. ** DazzleGeometryExportTaskClass
  198. ** Export task for INodes which are to generate W3D Dazzle objects
  199. */
  200. class DazzleGeometryExportTaskClass : public GeometryExportTaskClass
  201. {
  202. public:
  203. DazzleGeometryExportTaskClass(INode * node,GeometryExportContextClass & context) :
  204. GeometryExportTaskClass(node,context)
  205. {
  206. }
  207. virtual void Export_Geometry(GeometryExportContextClass & context)
  208. {
  209. /*
  210. ** Create the dazzle object
  211. */
  212. DazzleSaveClass * dazzle = new DazzleSaveClass( Name,
  213. ContainerName,
  214. Node,
  215. ExportSpace,
  216. context.CurTime,
  217. *context.ProgressMeter);
  218. /*
  219. ** Export it.
  220. */
  221. dazzle->Write_To_File(context.CSave);
  222. delete dazzle;
  223. context.ProgressMeter->Add_Increment();
  224. };
  225. protected:
  226. virtual int Get_Geometry_Type(void) { return DAZZLE; }
  227. };
  228. /**
  229. ** NullGeometryExportTaskClass
  230. ** Export task for INodes which are to generate W3D NULL objects. Note that this
  231. ** does not do anything in the Export_Geometry call, these only create entries in
  232. ** any Hierarhcical model or collection object being exported.
  233. */
  234. class NullGeometryExportTaskClass : public GeometryExportTaskClass
  235. {
  236. public:
  237. NullGeometryExportTaskClass(INode * node,GeometryExportContextClass & context) :
  238. GeometryExportTaskClass(node,context)
  239. {
  240. memset(ContainerName,0,sizeof(ContainerName));
  241. memset(Name,0,sizeof(Name));
  242. strcpy(Name,"NULL");
  243. }
  244. virtual void Export_Geometry(GeometryExportContextClass & context)
  245. {
  246. context.ProgressMeter->Add_Increment();
  247. };
  248. protected:
  249. virtual int Get_Geometry_Type(void) { return NULLOBJ; }
  250. };
  251. /**
  252. ** AggregateGeometryExportTaskClass
  253. ** Export task for INodes which are to generate W3D Aggregates. These are nodes
  254. ** that refer to some external W3D object. This export task doesn't export any
  255. ** geometry (similer to the Null export task) and it clears its container name
  256. ** because the object to be attached is not a sub-object of the model we are
  257. ** currently exporting.
  258. */
  259. class AggregateGeometryExportTaskClass : public GeometryExportTaskClass
  260. {
  261. public:
  262. AggregateGeometryExportTaskClass(INode * node,GeometryExportContextClass & context) :
  263. GeometryExportTaskClass(node,context)
  264. {
  265. memset(ContainerName,0,sizeof(ContainerName));
  266. }
  267. virtual void Export_Geometry(GeometryExportContextClass & context)
  268. {
  269. context.ProgressMeter->Add_Increment();
  270. };
  271. virtual bool Is_Aggregate(void)
  272. {
  273. return true;
  274. }
  275. protected:
  276. virtual int Get_Geometry_Type(void) { return AGGREGATE; }
  277. };
  278. /**
  279. ** ProxyExportTaskClass
  280. ** These are used by the Renegade Level Editor to cause game objects to be
  281. ** instantiated at the specified transform. Like aggregates they have to
  282. ** be handled specially and therefore have the Is_Proxy member function devoted
  283. ** solely to them :-) Hopefully we don't have any more geometry types which
  284. ** have to be handled specially or this is going to get messy again.
  285. */
  286. class ProxyExportTaskClass : public GeometryExportTaskClass
  287. {
  288. public:
  289. ProxyExportTaskClass(INode * node,GeometryExportContextClass & context) :
  290. GeometryExportTaskClass(node,context)
  291. {
  292. /*
  293. ** clear the container name
  294. */
  295. memset(ContainerName,0,sizeof(ContainerName));
  296. /*
  297. ** strip the trailing ~
  298. */
  299. char *tilda = ::strchr(Name, '~');
  300. memset(tilda,0,sizeof(Name) - ((int)tilda - (int)Name));
  301. }
  302. virtual void Export_Geometry(GeometryExportContextClass & context)
  303. {
  304. context.ProgressMeter->Add_Increment();
  305. };
  306. virtual bool Is_Proxy(void) { return true; }
  307. protected:
  308. virtual int Get_Geometry_Type(void) { return PROXY; }
  309. };
  310. /***********************************************************************************************
  311. **
  312. ** Implementations
  313. **
  314. ***********************************************************************************************/
  315. /***********************************************************************************************
  316. * GeometryExportTaskClass::GeometryExportTaskClass -- Constructor *
  317. * *
  318. * INPUT: *
  319. * *
  320. * OUTPUT: *
  321. * *
  322. * WARNINGS: *
  323. * *
  324. * HISTORY: *
  325. * 10/20/2000 gth : Created. *
  326. *=============================================================================================*/
  327. GeometryExportTaskClass::GeometryExportTaskClass(INode * node,GeometryExportContextClass & context) :
  328. BoneIndex(0),
  329. ExportSpace(1),
  330. CurTime(context.CurTime),
  331. Node(node)
  332. {
  333. /*
  334. ** Set up the names
  335. */
  336. Set_W3D_Name(Name,Node->GetName());
  337. Append_Lod_Character(Name,Get_Lod_Level(context.Origin),context.OriginList);
  338. Set_W3D_Name(ContainerName,context.ModelName);
  339. /*
  340. ** Set up the bone index and export coordinate system.
  341. */
  342. if (context.HTree != NULL) {
  343. if (!Is_Skin(node)) {
  344. context.HTree->Get_Export_Coordinate_System(Node,&BoneIndex,NULL,&ExportSpace);
  345. } else {
  346. BoneIndex = 0;
  347. ExportSpace = context.OriginTransform;
  348. }
  349. }
  350. }
  351. /***********************************************************************************************
  352. * GeometryExportTaskClass::GeometryExportTaskClass -- Copy Constructor *
  353. * *
  354. * INPUT: *
  355. * *
  356. * OUTPUT: *
  357. * *
  358. * WARNINGS: *
  359. * *
  360. * HISTORY: *
  361. * 10/20/2000 gth : Created. *
  362. *=============================================================================================*/
  363. GeometryExportTaskClass::GeometryExportTaskClass(const GeometryExportTaskClass & that) :
  364. BoneIndex(that.BoneIndex),
  365. ExportSpace(that.ExportSpace),
  366. CurTime(that.CurTime),
  367. Node(that.Node)
  368. {
  369. Set_W3D_Name(Name,that.Name);
  370. Set_W3D_Name(ContainerName,that.ContainerName);
  371. }
  372. /***********************************************************************************************
  373. * GeometryExportTaskClass::~GeometryExportTaskClass -- Destructor *
  374. * *
  375. * INPUT: *
  376. * *
  377. * OUTPUT: *
  378. * *
  379. * WARNINGS: *
  380. * *
  381. * HISTORY: *
  382. *=============================================================================================*/
  383. GeometryExportTaskClass::~GeometryExportTaskClass(void)
  384. {
  385. }
  386. /***********************************************************************************************
  387. * GeometryExportTaskClass::Get_Full_Name -- Composes the full name of this robj *
  388. * *
  389. * INPUT: *
  390. * *
  391. * OUTPUT: *
  392. * *
  393. * WARNINGS: *
  394. * *
  395. * HISTORY: *
  396. * 10/24/2000 gth : Created. *
  397. *=============================================================================================*/
  398. void GeometryExportTaskClass::Get_Full_Name(char * buffer,int size)
  399. {
  400. char tmp[128];
  401. memset(tmp,0,sizeof(tmp));
  402. if (strlen(ContainerName) > 0) {
  403. strcat(tmp,ContainerName);
  404. strcat(tmp,".");
  405. }
  406. strcat(tmp,Name);
  407. strncpy(buffer,tmp,size);
  408. }
  409. /***********************************************************************************************
  410. * GeometryExportTaskClass::Create_Task -- virtual constructor for export tasks *
  411. * *
  412. * Virtual constructor for geometry export tasks. Will create the proper task *
  413. * type depending on the W3D flag settings. *
  414. * *
  415. * INPUT: *
  416. * *
  417. * OUTPUT: *
  418. * *
  419. * WARNINGS: *
  420. * *
  421. * HISTORY: *
  422. * 10/20/2000 gth : Created. *
  423. *=============================================================================================*/
  424. GeometryExportTaskClass *
  425. GeometryExportTaskClass::Create_Task(INode * node,GeometryExportContextClass & context)
  426. {
  427. if (!::Is_Geometry(node)) {
  428. return NULL;
  429. }
  430. // NOTE: we *have* to check Is_Proxy first because it is tied to a naming convention
  431. // rather than an explicit UI setting like the rest of the types.
  432. if (::Is_Proxy(*node)) {
  433. return new ProxyExportTaskClass(node,context);
  434. }
  435. if (::Is_Normal_Mesh(node) || Is_Camera_Aligned_Mesh(node) || Is_Camera_Oriented_Mesh(node) || Is_Skin(node)) {
  436. return new MeshGeometryExportTaskClass(node,context);
  437. }
  438. if (::Is_Collision_AABox(node) || Is_Collision_OBBox(node)) {
  439. return new CollisionBoxGeometryExportTaskClass(node,context);
  440. }
  441. if (::Is_Null_Object(node)) {
  442. return new NullGeometryExportTaskClass(node,context);
  443. }
  444. if (::Is_Dazzle(node)) {
  445. return new DazzleGeometryExportTaskClass(node,context);
  446. }
  447. if (::Is_Aggregate(node)) {
  448. return new AggregateGeometryExportTaskClass(node,context);
  449. }
  450. return NULL;
  451. }
  452. /***********************************************************************************************
  453. * GeometryExportTaskClass::Optimize_Geometry -- Optimizes the export tasks *
  454. * *
  455. * This function will attempt to split meshes so that they use only a single material and *
  456. * then try to combine small meshes that use the same material. Export tasks may be *
  457. * removed and new ones added. *
  458. * *
  459. * INPUT: *
  460. * tasks - dynamic vector of export task pointers. Some tasks may be deleted, some added *
  461. * *
  462. * OUTPUT: *
  463. * *
  464. * WARNINGS: *
  465. * *
  466. * HISTORY: *
  467. * 10/20/2000 gth : Created. *
  468. *=============================================================================================*/
  469. void GeometryExportTaskClass::Optimize_Geometry
  470. (
  471. DynamicVectorClass<GeometryExportTaskClass *> & tasks,
  472. GeometryExportContextClass & context
  473. )
  474. {
  475. int j=0,i=0;
  476. /*
  477. ** Pass 1: Extract all mesh geometry tasks from the input task array.
  478. ** NOTE: We're not optimizing Skin meshes so we leave them in the task array.
  479. */
  480. DynamicVectorClass<MeshGeometryExportTaskClass *> meshes;
  481. while (i<tasks.Count()) {
  482. if ( (tasks[i]->Get_Geometry_Type() == GeometryExportTaskClass::MESH) &&
  483. (!Is_Skin(tasks[i]->Get_Object_Node())) )
  484. {
  485. /*
  486. ** Add to the mesh array, remove from the tasks array
  487. */
  488. meshes.Add((MeshGeometryExportTaskClass *)(tasks[i]));
  489. tasks.Delete(i);
  490. } else {
  491. /*
  492. ** Leave in the task array and move to the next one.
  493. */
  494. i++;
  495. }
  496. }
  497. /*
  498. ** Pass 2: Split all meshes which use more than one material
  499. */
  500. DynamicVectorClass<MeshGeometryExportTaskClass *> simple_meshes;
  501. while (meshes.Count() > 0) {
  502. int cur_index = meshes.Count() - 1;
  503. MeshGeometryExportTaskClass * cur_mesh = meshes[cur_index];
  504. /*
  505. ** If this mesh already uses only one material, just transfer it to the simple_meshes array.
  506. ** Otherwise, have it split into new tasks, add them to the simple_meshes array, and delete this task.
  507. */
  508. if (cur_mesh->Is_Single_Material()) {
  509. simple_meshes.Add(cur_mesh);
  510. } else {
  511. cur_mesh->Split(simple_meshes);
  512. delete cur_mesh;
  513. }
  514. meshes.Delete(cur_index);
  515. }
  516. /*
  517. ** Pass 3: Combine meshes which satisfy the following
  518. ** - They use the same (single) material
  519. ** - They have fewer than 'x' polygons
  520. ** - They are 'close' to each other
  521. */
  522. i=0;
  523. while (i < simple_meshes.Count()) {
  524. if (simple_meshes[i]->Can_Combine()) {
  525. j=i+1;
  526. while (j < simple_meshes.Count()) {
  527. if (simple_meshes[i]->Can_Combine_With(simple_meshes[j])) {
  528. /*
  529. ** Add mesh 'j' into mesh 'i', delete its task.
  530. */
  531. simple_meshes[i]->Combine_Mesh(simple_meshes[j]);
  532. delete simple_meshes[j];
  533. simple_meshes.Delete(j);
  534. /*
  535. ** If we've just exceeded the max poly count, move to the next mesh
  536. */
  537. if (simple_meshes[i]->Can_Combine() == false) {
  538. j = simple_meshes.Count();
  539. }
  540. } else {
  541. /*
  542. ** Otherwise, move to the next mesh
  543. */
  544. j++;
  545. }
  546. }
  547. }
  548. i++;
  549. }
  550. /*
  551. ** Generate names for each of the meshes that were created
  552. */
  553. for (i=0; i<simple_meshes.Count(); i++) {
  554. // if (simple_meshes[i]->Is_Name_Dirty()) {
  555. simple_meshes[i]->Generate_Name("MESH",i,context);
  556. // }
  557. }
  558. /*
  559. ** Finally, transfer all of the optimized tasks into the big task array
  560. */
  561. for (i=0; i<simple_meshes.Count(); i++) {
  562. tasks.Add(simple_meshes[i]);
  563. }
  564. simple_meshes.Delete_All();
  565. }
  566. /***********************************************************************************************
  567. * GeometryExportTaskClass::Generate_Name -- create a name for this object *
  568. * *
  569. * During optimization, we can generate new meshes which need unique names. *
  570. * *
  571. * INPUT: *
  572. * index - will be used to create the name, don't re-use the same index within an LOD *
  573. * context - geometry export context *
  574. * *
  575. * OUTPUT: *
  576. * *
  577. * WARNINGS: *
  578. * *
  579. * HISTORY: *
  580. * 10/23/2000 gth : Created. *
  581. *=============================================================================================*/
  582. void GeometryExportTaskClass::Generate_Name(char * root,int index,GeometryExportContextClass & context)
  583. {
  584. /*
  585. ** Check if the original had a Renegade "building prefix" in its name. Building meshes
  586. ** are named with a 2-3 letter user specified prefix followed by either '^' or '#'. We
  587. ** have to maintain this prefix on the optimized meshes...
  588. */
  589. char prefix[5];
  590. strncpy(prefix,Name,sizeof(prefix));
  591. prefix[4] = 0;
  592. char * interior_prefix = strchr(prefix,'^');
  593. char * exterior_prefix = strchr(prefix,'#');
  594. memset(Name,0,sizeof(Name));
  595. if (interior_prefix != NULL) {
  596. strncpy(Name,prefix,(int)(interior_prefix - prefix) + 1);
  597. } else if (exterior_prefix != NULL) {
  598. strncpy(Name,prefix,(int)(exterior_prefix - prefix) + 1);
  599. }
  600. /*
  601. ** Append "root"{index} to the name
  602. */
  603. strcat(Name,root);
  604. strcat(Name,"{");
  605. sprintf(Name + strlen(Name),"%04d",index);
  606. strcat(Name,"}");
  607. /*
  608. ** Append the LOD index so the generated name doesn't collide with meshes in the same
  609. ** object but in other LOD's
  610. */
  611. sprintf(Name + strlen(Name),"%c",'a'+Get_Lod_Level(context.Origin));
  612. }
  613. /***********************************************************************************************
  614. * MeshGeometryExportTaskClass::Update_Cached_Data -- updates the cached material pointer, etc *
  615. * *
  616. * This should be called after the meshdata changes *
  617. * *
  618. * INPUT: *
  619. * *
  620. * OUTPUT: *
  621. * *
  622. * WARNINGS: *
  623. * *
  624. * HISTORY: *
  625. * 10/23/2000 gth : Created. *
  626. *=============================================================================================*/
  627. void MeshGeometryExportTaskClass::Update_Cached_Data(void)
  628. {
  629. SingleMtl = NULL;
  630. Mtl * nodemtl = Node->GetMtl();
  631. /*
  632. ** Set the SingleMtl pointer if this mesh uses only one material (again, even if its in a Multi-Sub)
  633. */
  634. if (nodemtl == NULL) {
  635. SingleMtl = NULL;
  636. } else if (nodemtl->NumSubMtls() <= 1) {
  637. SingleMtl = nodemtl;
  638. } else {
  639. int mat_index;
  640. int face_index;
  641. int sub_mtl_count = nodemtl->NumSubMtls();
  642. bool * sub_mtl_flags = new bool[sub_mtl_count];
  643. /*
  644. ** Initialize each sub-material flag to false (indicates that the material is un-used)
  645. */
  646. for (mat_index=0; mat_index<sub_mtl_count; mat_index++) {
  647. sub_mtl_flags[mat_index] = false;
  648. }
  649. /*
  650. ** Set a true for each material actually referenced by the mesh
  651. */
  652. for (face_index=0; face_index<MeshData.getNumFaces(); face_index++) {
  653. int max_mat_index = MeshData.faces[face_index].getMatID();
  654. int mat_index = (max_mat_index % sub_mtl_count);
  655. sub_mtl_flags[mat_index] = true;
  656. }
  657. /*
  658. ** Loop over the used materials counting how many 'true's we see
  659. */
  660. int mat_count = 0;
  661. for (mat_index=0; mat_index<sub_mtl_count; mat_index++) {
  662. if (sub_mtl_flags[mat_index]) {
  663. SingleMtl = nodemtl->GetSubMtl(mat_index);
  664. mat_count++;
  665. }
  666. }
  667. if (mat_count > 1) {
  668. SingleMtl = NULL;
  669. }
  670. }
  671. /*
  672. ** Update the bounding box
  673. */
  674. Point3 boxmin(0,0,0);
  675. Point3 boxmax(0,0,0);
  676. if (MeshData.numVerts > 0) {
  677. boxmin = MeshData.verts[1];
  678. boxmax = MeshData.verts[0];
  679. for (int i=0; i<MeshData.numVerts; i++) {
  680. boxmin.x = MIN(MeshData.verts[i].x,boxmin.x);
  681. boxmin.y = MIN(MeshData.verts[i].y,boxmin.y);
  682. boxmin.z = MIN(MeshData.verts[i].z,boxmin.z);
  683. boxmax.x = MAX(MeshData.verts[i].x,boxmax.x);
  684. boxmax.y = MAX(MeshData.verts[i].y,boxmax.y);
  685. boxmax.z = MAX(MeshData.verts[i].z,boxmax.z);
  686. }
  687. }
  688. boxmax += Point3(0.1f,0.1f,0.1f);
  689. boxmin -= Point3(0.1f,0.1f,0.1f);
  690. BoxCenter = (boxmax + boxmin) * 0.5f;
  691. BoxExtent = (boxmax - boxmin) * 0.5f;
  692. WorldBounds = Box3(boxmin,boxmax) * Node->GetObjectTM(CurTime);
  693. }
  694. /***********************************************************************************************
  695. * MeshGeometryExportTaskClass::Is_Single_Material -- Tests if this mesh uses a single materia *
  696. * *
  697. * INPUT: *
  698. * *
  699. * OUTPUT: *
  700. * *
  701. * WARNINGS: *
  702. * *
  703. * HISTORY: *
  704. * 10/20/2000 gth : Created. *
  705. *=============================================================================================*/
  706. bool MeshGeometryExportTaskClass::Is_Single_Material(void)
  707. {
  708. return ((SingleMtl != NULL) || (Node->GetMtl() == NULL));
  709. }
  710. /***********************************************************************************************
  711. * MeshGeometryExportTaskClass::Get_Single_Material -- returns pointer to the material *
  712. * *
  713. * INPUT: *
  714. * *
  715. * OUTPUT: *
  716. * *
  717. * WARNINGS: *
  718. * *
  719. * HISTORY: *
  720. * 10/23/2000 gth : Created. *
  721. *=============================================================================================*/
  722. Mtl* MeshGeometryExportTaskClass::Get_Single_Material(void)
  723. {
  724. return SingleMtl;
  725. }
  726. /***********************************************************************************************
  727. * MeshGeometryExportTaskClass::Split -- Splits into single material meshes *
  728. * *
  729. * This function will create new export tasks and add them to the supplied array. Each of *
  730. * these will be single-material meshes. *
  731. * *
  732. * INPUT: *
  733. * *
  734. * OUTPUT: *
  735. * *
  736. * WARNINGS: *
  737. * *
  738. * HISTORY: *
  739. * 10/20/2000 gth : Created. *
  740. *=============================================================================================*/
  741. void MeshGeometryExportTaskClass::Split(DynamicVectorClass<MeshGeometryExportTaskClass *> & simple_meshes)
  742. {
  743. assert(!Is_Single_Material());
  744. Mtl * nodemtl = Node->GetMtl();
  745. int mat_index;
  746. int face_index;
  747. int sub_mtl_count = nodemtl->NumSubMtls();
  748. bool * sub_mtl_flags = new bool[sub_mtl_count];
  749. /*
  750. ** Initialize each sub-material flag to false (indicates that the material is un-used)
  751. */
  752. for (mat_index=0; mat_index<sub_mtl_count; mat_index++) {
  753. sub_mtl_flags[mat_index] = false;
  754. }
  755. /*
  756. ** Set a true for each material actually referenced by the mesh
  757. */
  758. for (face_index=0; face_index<MeshData.getNumFaces(); face_index++) {
  759. int max_mat_index = MeshData.faces[face_index].getMatID();
  760. int mat_index = (max_mat_index % sub_mtl_count);
  761. sub_mtl_flags[mat_index] = true;
  762. }
  763. /*
  764. ** Loop over the used materials, creating a new MeshGeometryExportTaskClass for each one
  765. */
  766. for (mat_index=0; mat_index<sub_mtl_count; mat_index++) {
  767. if (sub_mtl_flags[mat_index]) {
  768. MeshGeometryExportTaskClass * new_task = new MeshGeometryExportTaskClass(*this);
  769. new_task->Reduce_To_Single_Material(mat_index);
  770. new_task->Set_Name_Dirty(true);
  771. simple_meshes.Add(new_task);
  772. }
  773. }
  774. }
  775. /***********************************************************************************************
  776. * MeshGeometryExportTaskClass::Reduce_To_Single_Material -- deletes polys *
  777. * *
  778. * This function deletes all polys (and subsequent un-used vertices) except for the ones *
  779. * that use the specified material. *
  780. * *
  781. * INPUT: *
  782. * mat_id - only faces using this material id will remain in the mesh. *
  783. * *
  784. * OUTPUT: *
  785. * *
  786. * WARNINGS: *
  787. * *
  788. * HISTORY: *
  789. * 10/20/2000 gth : Created. *
  790. *=============================================================================================*/
  791. void MeshGeometryExportTaskClass::Reduce_To_Single_Material(int mat_id)
  792. {
  793. int sub_mtl_count = Node->GetMtl()->NumSubMtls();
  794. BitArray faces_to_delete(MeshData.getNumFaces());
  795. BitArray verts_to_delete(MeshData.getNumVerts());
  796. faces_to_delete.ClearAll();
  797. verts_to_delete.ClearAll();
  798. for (int i=0; i<MeshData.getNumFaces(); i++) {
  799. if ((MeshData.faces[i].getMatID() % sub_mtl_count) != mat_id) {
  800. faces_to_delete.Set(i,true);
  801. }
  802. }
  803. MeshData.DeleteFaceSet(faces_to_delete,&verts_to_delete);
  804. MeshData.DeleteVertSet(verts_to_delete);
  805. Update_Cached_Data();
  806. }
  807. /***********************************************************************************************
  808. * MeshGeometryExportTaskClass::Can_Combine -- can this mesh combine with anything *
  809. * *
  810. * This will return false if the mesh has multiple materials, too many polys, etc. *
  811. * *
  812. * INPUT: *
  813. * *
  814. * OUTPUT: *
  815. * *
  816. * WARNINGS: *
  817. * *
  818. * HISTORY: *
  819. * 10/24/2000 gth : Created. *
  820. *=============================================================================================*/
  821. bool MeshGeometryExportTaskClass::Can_Combine(void)
  822. {
  823. /*
  824. ** This mesh can't combine at all if:
  825. ** - it has multiple materials
  826. ** - its polygon count is too high
  827. ** - (Renegade) VIS is enabled (don't want to create really wierd shaped vis-sectors...)
  828. */
  829. if (!Is_Single_Material()) {
  830. return false;
  831. }
  832. if (MeshData.numFaces > OPTIMIZATION_FACECOUNT_LIMIT) {
  833. return false;
  834. }
  835. if (ExportOptions.Is_Vis_Collision_Enabled()) {
  836. return false;
  837. }
  838. return true;
  839. }
  840. /***********************************************************************************************
  841. * MeshGeometryExportTaskClass::Can_Combine_With -- can this mesh be combined with the given m *
  842. * *
  843. * INPUT: *
  844. * *
  845. * OUTPUT: *
  846. * *
  847. * WARNINGS: *
  848. * *
  849. * HISTORY: *
  850. * 10/24/2000 gth : Created. *
  851. *=============================================================================================*/
  852. bool MeshGeometryExportTaskClass::Can_Combine_With(MeshGeometryExportTaskClass * other_mesh)
  853. {
  854. /*
  855. ** Does the mesh attach to the same W3D bone that we do?
  856. */
  857. if (other_mesh->BoneIndex != BoneIndex) {
  858. return false;
  859. }
  860. /*
  861. ** Does the mesh use the same (single) material that we do?
  862. */
  863. Mtl * other_mtl = other_mesh->Get_Single_Material();
  864. if (other_mtl == NULL) {
  865. return false;
  866. }
  867. Mtl * my_mtl = Get_Single_Material();
  868. if (my_mtl != other_mtl) {
  869. return false;
  870. }
  871. /*
  872. ** Are its relevant W3D options the same as ours?
  873. */
  874. if (ExportOptions.Geometry_Options_Match(other_mesh->ExportOptions)) {
  875. return false;
  876. }
  877. /*
  878. ** Would our combined polygon count be reasonable
  879. */
  880. if (MeshData.numFaces + other_mesh->MeshData.numFaces > OPTIMIZATION_FACECOUNT_LIMIT) {
  881. return false;
  882. }
  883. /*
  884. ** Is the other mesh near me?
  885. */
  886. Point3 my_center = Node->GetObjectTM(CurTime) * BoxCenter;
  887. Point3 other_center = other_mesh->Node->GetObjectTM(CurTime) * BoxCenter;
  888. float dist = ::FLength(my_center - other_center);
  889. if (dist > OPTIMIZATION_COMBINING_DISTANCE) {
  890. return false;
  891. }
  892. return true;
  893. }
  894. /***********************************************************************************************
  895. * MeshGeometryExportTaskClass::Combine_Mesh -- Add the given mesh into this mesh *
  896. * *
  897. * INPUT: *
  898. * *
  899. * OUTPUT: *
  900. * *
  901. * WARNINGS: *
  902. * *
  903. * HISTORY: *
  904. * 10/24/2000 gth : Created. *
  905. *=============================================================================================*/
  906. void MeshGeometryExportTaskClass::Combine_Mesh(MeshGeometryExportTaskClass * other_mesh)
  907. {
  908. /*
  909. ** Compute the transform from other_mesh's coordinate system to ours so that
  910. ** its polygons can be combined with ours (by calling CombineMeshes)
  911. */
  912. Matrix3 our_tm = Node->GetObjectTM(CurTime);
  913. Matrix3 his_tm = other_mesh->Node->GetObjectTM(CurTime);
  914. Matrix3 tm = Inverse(our_tm) * his_tm;
  915. /*
  916. ** Store our current material index
  917. */
  918. int matid = MeshData.faces[0].getMatID();
  919. if (Node->GetMtl()->NumSubMtls() > 1) {
  920. matid = matid % Node->GetMtl()->NumSubMtls();
  921. }
  922. /*
  923. ** Combine the meshes
  924. */
  925. Mesh new_mesh;
  926. ::CombineMeshes(new_mesh,MeshData,other_mesh->MeshData,&our_tm,&his_tm,0);
  927. MeshData = new_mesh;
  928. /*
  929. ** Set all material ID's
  930. */
  931. for (int i=0; i<MeshData.numFaces; i++) {
  932. MeshData.faces[i].setMatID(matid);
  933. }
  934. }
  935. Point3 MeshGeometryExportTaskClass::Get_Shared_Vertex_Normal(const Point3 & world_pos,int smgroup)
  936. {
  937. const float EPSILON = 0.001f;
  938. Point3 normal(0,0,0);
  939. /*
  940. ** Does the bounding box for this node even contain the point
  941. ** we are looking for?
  942. */
  943. if (WorldBounds.Contains(world_pos) != 0) {
  944. /*
  945. ** Transform the query point into object space
  946. */
  947. Matrix3 tm = Node->GetObjectTM(CurTime);
  948. Point3 obj_pos = world_pos * Inverse(tm);
  949. /*
  950. ** Loop through all the faces in this mesh and find out which ones
  951. ** share the same smoothing group as the vertex we are looking for.
  952. */
  953. for (int face_index = 0; face_index < MeshData.numFaces; face_index ++) {
  954. Face &maxface = MeshData.faces[face_index];
  955. int face_smgroup = maxface.getSmGroup();
  956. if ((face_smgroup & smgroup) || (face_smgroup == smgroup)) {
  957. /*
  958. ** Find out if any of the verticies of this face share the
  959. ** same space as the vertex we are looking for.
  960. */
  961. bool found = false;
  962. for (int vert_index = 0; (vert_index < 3) && !found; vert_index ++) {
  963. int max_vert_index = maxface.v[vert_index];
  964. Point3 delta = obj_pos - MeshData.verts[max_vert_index];
  965. if ((fabs (delta.x) < EPSILON) &&
  966. (fabs (delta.y) < EPSILON) &&
  967. (fabs (delta.z) < EPSILON))
  968. {
  969. /*
  970. ** Compute the normal for this face
  971. */
  972. Point3 v0 = MeshData.verts[maxface.v[0]];
  973. Point3 v1 = MeshData.verts[maxface.v[1]];
  974. Point3 v2 = MeshData.verts[maxface.v[2]];
  975. Point3 face_normal = (v1-v0)^(v2-v1);
  976. face_normal = ::Normalize(face_normal);
  977. /*
  978. ** Add this face normal to the sum
  979. */
  980. normal.x += face_normal.x;
  981. normal.y += face_normal.y;
  982. normal.z += face_normal.z;
  983. /*
  984. ** Done with this face, look for more
  985. */
  986. found = true;
  987. }
  988. }
  989. }
  990. }
  991. /*
  992. ** Transform the "normal" to world space. Note that this vector isn't
  993. ** normalized because we are basically summing the contributions of each
  994. ** face in each mesh which shares this normal. The final normal
  995. ** will be normalized in the MeshBuilderClass.
  996. */
  997. tm.NoTrans();
  998. normal = tm.PointTransform(normal);
  999. }
  1000. return normal;
  1001. }