meshbuild.cpp 71 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837
  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 : WW3D *
  23. * *
  24. * $Archive:: /Commando/Code/ww3d2/meshbuild.cpp $*
  25. * *
  26. * Author:: Greg_h *
  27. * *
  28. * $Modtime:: 1/08/01 10:04a $*
  29. * *
  30. * $Revision:: 1 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * MeshBuilderClass::VertClass::Reset -- reset this vertex *
  35. * MeshBuilderClass::FaceClass::Reset -- rest this face *
  36. * MeshBuilderClass::FaceClass::Compute_Plane -- compute the plane for this face *
  37. * MeshBuilderClass::FaceClass::Is_Degenerate -- check if a face is degenerate *
  38. * MeshBuilderClass::MeshStatsStruct::Reset -- reset the stats to all false *
  39. * MeshBuilderClass::MeshBuilderClass -- constructor *
  40. * MeshBuilderClass::~MeshBuilderClass -- destructor *
  41. * MeshBuilderClass::Free -- release all memory in use *
  42. * MeshBuilderClass::Reset -- Get the builder ready to process a mesh *
  43. * MeshBuilderClass::Add_Face -- Add a face to the mesh *
  44. * MeshBuilderClass::Build_Mesh -- process the mesh *
  45. * MeshBuilderClass::Compute_Face_Normals -- computes all of the face normals from the index *
  46. * MeshBuilderClass::Verify_Face_Normals -- checks if any faces have flipped *
  47. * MeshBuilderClass::Compute_Vertex_Normals -- Computes the vertex normals for the mesh *
  48. * MeshBuilderClass::Remove_Degenerate_Faces -- discard invalid or duplicated faces *
  49. * MeshBuilderClass::Compute_Mesh_Stats -- compute some stats about the mesh *
  50. * MeshBuilderClass::Compute_Bounding_Box -- computes an axis-aligned bounding box for the m *
  51. * MeshBuilderClass::Compute_Bounding_Sphere -- computes a bounding sphere for the mesh *
  52. * MeshBuilderClass::Optimize_Mesh -- "optimize" the mesh *
  53. * MeshBuilderClass::Strip_Optimize_Mesh -- optimize the mesh for triangle strips *
  54. * MeshBuilderClass::Grow_Face_Array -- increases the size of the face array *
  55. * MeshBuilderClass::Sort_Vertices -- Sorts vertices according to the given key array *
  56. * MeshBuilderClass::Sort_Vertices_By_Bone_Index -- sorts verts by bone index *
  57. * MeshBuilderClass::Sort_Vertices_By_Vertex_Material -- sorts verts by vertex mtl in pass0 *
  58. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  59. #include "meshbuild.h"
  60. #include "uarray.h"
  61. #include <stdlib.h>
  62. #include <string.h>
  63. #include <assert.h>
  64. const float EPSILON = 0.0001f;
  65. /*
  66. ** qsort compare functions
  67. */
  68. static int face_material_compare(const void *elem1, const void *elem2);
  69. static int pass0_stage0_compare(const void *elem1, const void *elem2);
  70. static int pass0_stage1_compare(const void *elem1, const void *elem2);
  71. static int pass1_stage0_compare(const void *elem1, const void *elem2);
  72. static int pass1_stage1_compare(const void *elem1, const void *elem2);
  73. static int pass2_stage0_compare(const void *elem1, const void *elem2);
  74. static int pass2_stage1_compare(const void *elem1, const void *elem2);
  75. static int pass3_stage0_compare(const void *elem1, const void *elem2);
  76. static int pass3_stage1_compare(const void *elem1, const void *elem2);
  77. static int vertex_compare(const void *elem1, const void *elem2);
  78. typedef int (*COMPARE_FUNC_TYPE)(const void * elem1,const void * elem2);
  79. COMPARE_FUNC_TYPE Texture_Compare_Funcs[MeshBuilderClass::MAX_PASSES][MeshBuilderClass::MAX_STAGES] =
  80. {
  81. { pass0_stage0_compare, pass0_stage1_compare },
  82. { pass1_stage0_compare, pass1_stage1_compare },
  83. { pass2_stage0_compare, pass2_stage1_compare },
  84. { pass3_stage0_compare, pass3_stage1_compare },
  85. };
  86. /************************************************************************************
  87. **
  88. ** FaceHasherClass, support class for the unique faces hash table. The unique
  89. ** faces table is going to detect exactly duplicated faces and discard them. It
  90. ** appears that the artists occasionally accidentally duplicate a face which causes
  91. ** problems in the stripping algorithm...
  92. **
  93. ************************************************************************************/
  94. class FaceHasherClass : public HashCalculatorClass<MeshBuilderClass::FaceClass>
  95. {
  96. public:
  97. virtual bool Items_Match(const MeshBuilderClass::FaceClass & a, const MeshBuilderClass::FaceClass & b)
  98. {
  99. // Note: if we want this to detect duplicates that are "rotated", must change
  100. // both this function and the Compute_Hash function...
  101. return
  102. (
  103. (a.VertIdx[0] == b.VertIdx[0]) &&
  104. (a.VertIdx[1] == b.VertIdx[1]) &&
  105. (a.VertIdx[2] == b.VertIdx[2])
  106. );
  107. }
  108. virtual void Compute_Hash(const MeshBuilderClass::FaceClass & item)
  109. {
  110. HashVal = (int)(item.VertIdx[0]*12345.6f + item.VertIdx[1]*1714.38484f + item.VertIdx[2]*27561.3f)&1023;
  111. }
  112. virtual int Num_Hash_Bits(void)
  113. {
  114. return 10;
  115. }
  116. virtual int Num_Hash_Values(void)
  117. {
  118. return 1;
  119. }
  120. virtual int Get_Hash_Value(int /*index*/)
  121. {
  122. return HashVal;
  123. }
  124. private:
  125. int HashVal;
  126. };
  127. /************************************************************************************
  128. **
  129. ** VertexArray, build an array of unique vertices. Can't use the UniqueArray
  130. ** template due to the special considerations needed to properly handle smoothing
  131. ** groups... (DAMN!!! this was the reason I *WROTE* UniqueArray, oh well :-)
  132. **
  133. ************************************************************************************/
  134. class VertexArrayClass
  135. {
  136. public:
  137. enum
  138. {
  139. HASH_TABLE_SIZE = 4096,
  140. };
  141. VertexArrayClass(int maxsize,int match_normals = 0)
  142. {
  143. Verts = NULL;
  144. assert(maxsize > 0);
  145. Verts = W3DNEWARRAY MeshBuilderClass::VertClass[maxsize];
  146. assert(Verts);
  147. VertCount = 0;
  148. UVSplits = 0;
  149. HashTable = W3DNEWARRAY MeshBuilderClass::VertClass * [HASH_TABLE_SIZE];
  150. memset(HashTable,0,sizeof(MeshBuilderClass::VertClass *) * HASH_TABLE_SIZE);
  151. MatchNormals = match_normals;
  152. // initialize the center and extent to do nothing to the points input
  153. Center.Set(0.0f,0.0f,0.0f);
  154. Extent.Set(1.0f,1.0f,1.0f);
  155. }
  156. ~VertexArrayClass(void)
  157. {
  158. delete[] Verts;
  159. delete[] HashTable;
  160. }
  161. void VertexArrayClass::Set_Bounds(const Vector3 & minv,const Vector3 & maxv)
  162. {
  163. Extent = (maxv - minv) / 2.0f;
  164. Center = (maxv + minv) / 2.0f;
  165. }
  166. int Submit_Vertex(const MeshBuilderClass::VertClass & vert)
  167. {
  168. // 2D floating point hashing...
  169. unsigned int lasthash = 0xFFFFFFFF;
  170. unsigned int hash;
  171. unsigned int shadeindex = 0xFFFFFFFF;
  172. // transform the position of the point into the range
  173. // -1 < p < 1 as defined by the center and extent.
  174. // aja/ehc 19991005 - Handle the case where an extent is zero.
  175. Vector3 position = (vert.Position - Center);
  176. double xstart;
  177. if(fabs(Extent.X) > EPSILON)
  178. xstart = (vert.Position.X - Center.X) / Extent.X;
  179. else
  180. xstart = Center.X;
  181. double ystart;
  182. if(fabs(Extent.Y) > EPSILON)
  183. ystart = (vert.Position.Y - Center.Y) / Extent.Y;
  184. else
  185. ystart = Center.Y;
  186. double x,y;
  187. for (x = xstart - EPSILON; x <= xstart + EPSILON + 0.0000001; x+= EPSILON) {
  188. for (y = ystart - EPSILON; y <= ystart + EPSILON + 0.000001; y+= EPSILON) {
  189. hash = compute_hash((float)x,(float)y);
  190. if (hash != lasthash) {
  191. MeshBuilderClass::VertClass * test_vert = HashTable[hash];
  192. while (test_vert) {
  193. if (Verts_Shading_Match(vert,*test_vert)) {
  194. if (shadeindex == 0xFFFFFFFF) {
  195. shadeindex = test_vert->UniqueIndex;
  196. // mask the "master" smoothed vertex's smoothing group with our
  197. // face's smoothing group since we are going to be smoothed with it.
  198. Verts[shadeindex].SharedSmGroup &= vert.SmGroup;
  199. }
  200. }
  201. if (Verts_Match(vert,*test_vert)) {
  202. return test_vert->UniqueIndex;
  203. }
  204. test_vert = test_vert->NextHash;
  205. }
  206. }
  207. lasthash = hash;
  208. }
  209. }
  210. // Not found, add to the end of the array
  211. int newindex = VertCount;
  212. VertCount++;
  213. Verts[newindex] = vert;
  214. Verts[newindex].UniqueIndex = newindex;
  215. if (shadeindex == 0xFFFFFFFF) {
  216. Verts[newindex].ShadeIndex = newindex;
  217. // This is a new vertex,so store the face's smoothing group into SharedSmGroup
  218. Verts[newindex].SharedSmGroup = Verts[newindex].SmGroup;
  219. } else {
  220. Verts[newindex].ShadeIndex = shadeindex;
  221. }
  222. // This is a new vertex, store the face's smoothing group with it
  223. //Verts[newindex].SmoothingGroup = face_smooth_group;
  224. // And add to the hash table
  225. x = (vert.Position.X - Center.X) / Extent.X;
  226. y = (vert.Position.Y - Center.Y) / Extent.Y;
  227. hash = compute_hash((float)x,(float)y);
  228. Verts[newindex].NextHash = HashTable[hash];
  229. HashTable[hash] = &Verts[newindex];
  230. return newindex;
  231. }
  232. int Verts_Match(const MeshBuilderClass::VertClass & v0,const MeshBuilderClass::VertClass & v1)
  233. {
  234. // if user is specifying unique id's, they must match:
  235. if (v0.Id != v1.Id) return 0;
  236. // position must match:
  237. float dp = (v0.Position - v1.Position).Length();
  238. if (dp > EPSILON) return 0;
  239. // normal or smoothing group must match:
  240. if (MatchNormals == 0) {
  241. int smgroup_match = ((v0.SmGroup & v1.SmGroup) || (v0.SmGroup == v1.SmGroup));
  242. if (!smgroup_match) return 0;
  243. } else {
  244. float dn = (v0.Normal - v1.Normal).Length();
  245. if (dn > EPSILON) return 0;
  246. }
  247. // colors, and material id's must match for all passes
  248. for (int pass=0; pass < MeshBuilderClass::MAX_PASSES; pass++) {
  249. if (v0.DiffuseColor[pass] != v1.DiffuseColor[pass]) return 0;
  250. if (v0.SpecularColor[pass] != v1.SpecularColor[pass]) return 0;
  251. if (v0.DiffuseIllumination[pass] != v1.DiffuseIllumination[pass]) return 0;
  252. if (v0.Alpha[pass] != v1.Alpha[pass]) return 0;
  253. if (v0.VertexMaterialIndex[pass] != v1.VertexMaterialIndex[pass]) return 0;
  254. }
  255. // texcoords must match for all passes and stages
  256. // Note: I'm checking them separately and last so that I can keep track
  257. // of how many splits are caused solely by u-v discontinuities...
  258. for (pass=0; pass<MeshBuilderClass::MAX_PASSES; pass++) {
  259. for (int stage=0; stage < MeshBuilderClass::MAX_STAGES; stage++) {
  260. if (v0.TexCoord[pass][stage] != v1.TexCoord[pass][stage]) {
  261. UVSplits++;
  262. return 0;
  263. }
  264. }
  265. }
  266. return 1;
  267. }
  268. int Verts_Shading_Match(const MeshBuilderClass::VertClass & v0,const MeshBuilderClass::VertClass & v1)
  269. {
  270. float dv = (v0.Position - v1.Position).Length();
  271. int smgroup_match = ((v0.SmGroup & v1.SmGroup) || (v0.SmGroup == v1.SmGroup));
  272. if ( (dv < EPSILON) &&
  273. (smgroup_match) &&
  274. (v0.Id == v1.Id))
  275. {
  276. return 1;
  277. }
  278. return 0;
  279. }
  280. void Propogate_Shared_Smooth_Groups(void)
  281. {
  282. for (int i=0; i<VertCount; i++) {
  283. if (Verts[i].ShadeIndex != i) {
  284. Verts[i].SharedSmGroup = Verts[Verts[i].ShadeIndex].SharedSmGroup;
  285. }
  286. }
  287. }
  288. const MeshBuilderClass::VertClass & operator [] (int i) { return Verts[i]; }
  289. int VertCount;
  290. int UVSplits;
  291. MeshBuilderClass::VertClass * Verts;
  292. private:
  293. MeshBuilderClass::VertClass * * HashTable;
  294. int MatchNormals;
  295. Vector3 Center;
  296. Vector3 Extent;
  297. unsigned int compute_hash(float x,float y)
  298. {
  299. // 12 bit hash, 6 bits for x and 6 for y, points near each other
  300. // need to generate the same hash value...
  301. int ix = ((int)(x) & 0x0000003F);
  302. int iy = ((int)(y) & 0x0000003F);
  303. return (iy<<6) | ix;
  304. }
  305. };
  306. /***********************************************************************************************
  307. * MeshBuilderClass::VertClass::Reset -- reset this vertex *
  308. * *
  309. * INPUT: *
  310. * *
  311. * OUTPUT: *
  312. * *
  313. * WARNINGS: *
  314. * *
  315. * HISTORY: *
  316. * 10/19/98 GTH : Created. *
  317. *=============================================================================================*/
  318. void MeshBuilderClass::VertClass::Reset(void)
  319. {
  320. Position.Set(0,0,0);
  321. Normal.Set(0,0,0);
  322. SmGroup = 0;
  323. Id = 0;
  324. MaxVertColIndex = 0;
  325. for (int pass = 0; pass < MeshBuilderClass::MAX_PASSES; pass++) {
  326. DiffuseColor[pass].Set(1,1,1);
  327. SpecularColor[pass].Set(1,1,1);
  328. DiffuseIllumination[pass].Set(0,0,0);
  329. Alpha[pass] = 1.0f;
  330. VertexMaterialIndex[pass] = -1;
  331. for (int stage=0; stage < MeshBuilderClass::MAX_STAGES; stage++) {
  332. TexCoord[pass][stage].Set(0,0);
  333. }
  334. }
  335. BoneIndex = 0;
  336. Attribute0 = 0;
  337. Attribute1 = 0;
  338. UniqueIndex = 0;
  339. ShadeIndex = 0;
  340. NextHash = NULL;
  341. }
  342. /***********************************************************************************************
  343. * MeshBuilderClass::FaceClass::Reset -- rest this face *
  344. * *
  345. * INPUT: *
  346. * *
  347. * OUTPUT: *
  348. * *
  349. * WARNINGS: *
  350. * *
  351. * HISTORY: *
  352. * 10/19/98 GTH : Created. *
  353. *=============================================================================================*/
  354. void MeshBuilderClass::FaceClass::Reset(void)
  355. {
  356. for (int i=0; i<3; i++) {
  357. Verts[i].Reset();
  358. VertIdx[i] = 0;
  359. }
  360. SmGroup = 0;
  361. Index = 0;
  362. Attributes = 0;
  363. SurfaceType = 0;
  364. for (int pass=0; pass<MeshBuilderClass::MAX_PASSES; pass++) {
  365. for (int stage=0; stage < MeshBuilderClass::MAX_STAGES; stage++) {
  366. TextureIndex[pass][stage] = -1;
  367. }
  368. ShaderIndex[pass] = -1;
  369. }
  370. AddIndex = 0;
  371. Normal.Set(0,0,0);
  372. Dist = 0.0f;
  373. }
  374. /***********************************************************************************************
  375. * MeshBuilderClass::FaceClass::Compute_Plane -- compute the plane for this face *
  376. * *
  377. * INPUT: *
  378. * *
  379. * OUTPUT: *
  380. * *
  381. * WARNINGS: *
  382. * *
  383. * HISTORY: *
  384. * 5/15/98 GTH : Created. *
  385. *=============================================================================================*/
  386. void MeshBuilderClass::FaceClass::Compute_Plane(void)
  387. {
  388. #ifdef ALLOW_TEMPORARIES
  389. Normal = Vector3::Cross_Product((Verts[1].Position - Verts[0].Position),(Verts[2].Position - Verts[0].Position));
  390. Normal.Normalize();
  391. #else
  392. Vector3::Normalized_Cross_Product((Verts[1].Position - Verts[0].Position),(Verts[2].Position - Verts[0].Position),&Normal);
  393. #endif
  394. Dist = Vector3::Dot_Product(Normal,Verts[0].Position);
  395. }
  396. /***********************************************************************************************
  397. * MeshBuilderClass::FaceClass::Is_Degenerate -- check if a face is degenerate *
  398. * *
  399. * INPUT: *
  400. * *
  401. * OUTPUT: *
  402. * *
  403. * WARNINGS: *
  404. * assumes we have already built the unique vertex table and installed their indices *
  405. * *
  406. * HISTORY: *
  407. * 7/7/98 GTH : Created. *
  408. *=============================================================================================*/
  409. bool MeshBuilderClass::FaceClass::Is_Degenerate(void)
  410. {
  411. for (int v0 = 0; v0 < 3; v0++) {
  412. for (int v1 = v0+1; v1 < 3; v1++) {
  413. if (VertIdx[v0] == VertIdx[v1]) {
  414. return true;
  415. }
  416. if ( (Verts[v0].Position.X == Verts[v1].Position.X) &&
  417. (Verts[v0].Position.Y == Verts[v1].Position.Y) &&
  418. (Verts[v0].Position.Z == Verts[v1].Position.Z) )
  419. {
  420. return true;
  421. }
  422. }
  423. }
  424. return false;
  425. }
  426. /***********************************************************************************************
  427. * MeshBuilderClass::MeshStatsStruct::Reset -- reset the stats to all false *
  428. * *
  429. * INPUT: *
  430. * *
  431. * OUTPUT: *
  432. * *
  433. * WARNINGS: *
  434. * *
  435. * HISTORY: *
  436. * 10/20/98 GTH : Created. *
  437. *=============================================================================================*/
  438. void MeshBuilderClass::MeshStatsStruct::Reset(void)
  439. {
  440. for (int pass = 0; pass < MeshBuilderClass::MAX_PASSES; pass++) {
  441. HasPerPolyShader[pass] = false;
  442. HasPerVertexMaterial[pass] = false;
  443. HasDiffuseColor[pass] = false;
  444. HasSpecularColor[pass] = false;
  445. HasDiffuseIllumination[pass] = false;
  446. HasVertexMaterial[pass] = false;
  447. HasShader[pass] = false;
  448. for (int stage = 0; stage < MeshBuilderClass::MAX_STAGES; stage++) {
  449. HasPerPolyTexture[pass][stage] = false;
  450. HasTexCoords[pass][stage] = false;
  451. HasTexture[pass][stage] = false;
  452. }
  453. }
  454. UVSplitCount = 0;
  455. StripCount = 0;
  456. MaxStripLength = 0;
  457. AvgStripLength = 0.0f;
  458. }
  459. /***********************************************************************************************
  460. * MeshBuilderClass::MeshBuilderClass -- constructor *
  461. * *
  462. * INPUT: *
  463. * *
  464. * OUTPUT: *
  465. * *
  466. * WARNINGS: *
  467. * *
  468. * HISTORY: *
  469. *=============================================================================================*/
  470. MeshBuilderClass::MeshBuilderClass(int pass_count,int face_count_guess,int face_count_growth_rate) :
  471. State(STATE_ACCEPTING_INPUT),
  472. PassCount(pass_count),
  473. FaceCount(0),
  474. Faces(NULL),
  475. InputVertCount(0),
  476. VertCount(0),
  477. Verts(NULL),
  478. CurFace(0),
  479. AllocFaceCount(0),
  480. AllocFaceGrowth(0),
  481. PolyOrderPass(0),
  482. PolyOrderStage(0),
  483. WorldInfo (NULL)
  484. {
  485. Reset(pass_count,face_count_guess,face_count_growth_rate);
  486. }
  487. /***********************************************************************************************
  488. * MeshBuilderClass::~MeshBuilderClass -- destructor *
  489. * *
  490. * INPUT: *
  491. * *
  492. * OUTPUT: *
  493. * *
  494. * WARNINGS: *
  495. * *
  496. * HISTORY: *
  497. * 5/15/98 GTH : Created. *
  498. *=============================================================================================*/
  499. MeshBuilderClass::~MeshBuilderClass(void)
  500. {
  501. Free();
  502. Set_World_Info(NULL);
  503. }
  504. /***********************************************************************************************
  505. * MeshBuilderClass::Free -- release all memory in use *
  506. * *
  507. * INPUT: *
  508. * *
  509. * OUTPUT: *
  510. * *
  511. * WARNINGS: *
  512. * *
  513. * HISTORY: *
  514. * 5/15/98 GTH : Created. *
  515. *=============================================================================================*/
  516. void MeshBuilderClass::Free(void)
  517. {
  518. if (Faces != NULL) {
  519. delete[] Faces;
  520. Faces = NULL;
  521. }
  522. if (Verts != NULL) {
  523. delete Verts;
  524. Verts = NULL;
  525. }
  526. FaceCount = 0;
  527. VertCount = 0;
  528. AllocFaceCount = 0;
  529. AllocFaceGrowth = 0;
  530. }
  531. /***********************************************************************************************
  532. * MeshBuilderClass::Reset -- Get the builder ready to process a mesh *
  533. * *
  534. * INPUT: *
  535. * *
  536. * OUTPUT: *
  537. * *
  538. * WARNINGS: *
  539. * *
  540. * HISTORY: *
  541. * 5/15/98 GTH : Created. *
  542. *=============================================================================================*/
  543. void MeshBuilderClass::Reset(int passcount,int face_count_guess,int face_count_growth_rate)
  544. {
  545. Free();
  546. State = STATE_ACCEPTING_INPUT;
  547. PassCount = passcount;
  548. AllocFaceCount = face_count_guess;
  549. AllocFaceGrowth = face_count_growth_rate;
  550. Faces = W3DNEWARRAY FaceClass[AllocFaceCount];
  551. CurFace = 0;
  552. Stats.Reset();
  553. }
  554. /***********************************************************************************************
  555. * MeshBuilderClass::Add_Face -- Add a face to the mesh *
  556. * *
  557. * INPUT: *
  558. * *
  559. * OUTPUT: *
  560. * *
  561. * WARNINGS: *
  562. * *
  563. * HISTORY: *
  564. * 5/15/98 GTH : Created. *
  565. *=============================================================================================*/
  566. int MeshBuilderClass::Add_Face(const FaceClass & face)
  567. {
  568. assert(State == STATE_ACCEPTING_INPUT);
  569. assert(CurFace <= AllocFaceCount);
  570. if (CurFace == AllocFaceCount) {
  571. Grow_Face_Array();
  572. }
  573. // copy the face
  574. Faces[CurFace] = face;
  575. // calculate the normal
  576. Faces[CurFace].Compute_Plane();
  577. // set the "add index"
  578. Faces[CurFace].AddIndex = CurFace;
  579. // copy the smoothing group into each vert
  580. for (int i=0; i<3; i++) {
  581. Faces[CurFace].Verts[i].SmGroup = Faces[CurFace].SmGroup;
  582. }
  583. // increment the face index
  584. CurFace++;
  585. return CurFace-1;
  586. }
  587. /***********************************************************************************************
  588. * MeshBuilderClass::Build_Mesh -- process the mesh *
  589. * *
  590. * This function finds all sharable vertices, calculates vertex normals, sorts the *
  591. * faces by material, sorts the faces into stripping order... *
  592. * *
  593. * INPUT: *
  594. * *
  595. * OUTPUT: *
  596. * *
  597. * WARNINGS: *
  598. * *
  599. * HISTORY: *
  600. * 5/15/98 GTH : Created. *
  601. *=============================================================================================*/
  602. void MeshBuilderClass::Build_Mesh(bool compute_normals)
  603. {
  604. /*
  605. ** Check the state, set state to "mesh processed"
  606. */
  607. assert(State == STATE_ACCEPTING_INPUT);
  608. State = STATE_MESH_PROCESSED;
  609. /*
  610. ** Seal off the face count
  611. */
  612. FaceCount = CurFace;
  613. /*
  614. ** Optimize the mesh and calculate vertex normals
  615. */
  616. Optimize_Mesh(compute_normals);
  617. }
  618. /***********************************************************************************************
  619. * MeshBuilderClass::Compute_Face_Normals -- computes all of the face normals from the indexed *
  620. * *
  621. * INPUT: *
  622. * *
  623. * OUTPUT: *
  624. * *
  625. * WARNINGS: *
  626. * *
  627. * HISTORY: *
  628. * 7/10/98 GTH : Created. *
  629. * 10/19/98 GTH : Modified to use the FaceClass::Compute_Plane function *
  630. *=============================================================================================*/
  631. void MeshBuilderClass::Compute_Face_Normals(void)
  632. {
  633. for (int faceidx = 0; faceidx < FaceCount; faceidx++) {
  634. Faces[faceidx].Compute_Plane();
  635. }
  636. }
  637. /***********************************************************************************************
  638. * MeshBuilderClass::Verify_Face_Normals -- checks if any faces have flipped *
  639. * *
  640. * INPUT: *
  641. * *
  642. * OUTPUT: *
  643. * *
  644. * WARNINGS: *
  645. * *
  646. * HISTORY: *
  647. * 7/10/98 GTH : Created. *
  648. *=============================================================================================*/
  649. bool MeshBuilderClass::Verify_Face_Normals(void)
  650. {
  651. int faceidx;
  652. bool ok = true;
  653. /*
  654. ** Check each face to see if any have flipped normals
  655. */
  656. for (faceidx = 0; faceidx < FaceCount; faceidx++) {
  657. VertClass & v0 = Verts[Faces[faceidx].VertIdx[0]];
  658. VertClass & v1 = Verts[Faces[faceidx].VertIdx[1]];
  659. VertClass & v2 = Verts[Faces[faceidx].VertIdx[2]];
  660. #ifdef ALLOW_TEMPORARIES
  661. Vector3 normal = Vector3::Cross_Product((v1.Position - v0.Position),(v2.Position - v0.Position));
  662. normal.Normalize();
  663. #else
  664. Vector3 normal;
  665. Vector3::Normalized_Cross_Product((v1.Position - v0.Position),(v2.Position - v0.Position),&normal);
  666. #endif
  667. float dn = (Faces[faceidx].Normal - normal).Length();
  668. if (dn > 0.0000001f) {
  669. ok = false;
  670. }
  671. }
  672. return ok;
  673. }
  674. /***********************************************************************************************
  675. * MeshBuilderClass::Compute_Vertex_Normals -- Computes the vertex normals for the mesh *
  676. * *
  677. * INPUT: *
  678. * *
  679. * OUTPUT: *
  680. * *
  681. * WARNINGS: *
  682. * This function should only be called after the mesh has been "optimized". The algorithm *
  683. * relies on non-smooth vertices having been split... *
  684. * *
  685. * HISTORY: *
  686. * 5/15/98 GTH : Created. *
  687. *=============================================================================================*/
  688. void MeshBuilderClass::Compute_Vertex_Normals(void)
  689. {
  690. int vertidx;
  691. int faceidx;
  692. int facevertidx;
  693. /*
  694. ** First, zero all vertex normals
  695. */
  696. for (vertidx = 0; vertidx < VertCount; vertidx++) {
  697. Verts[vertidx].Normal.Set(0,0,0);
  698. }
  699. /*
  700. ** Now, go through all of the faces, accumulating the face normals into the
  701. ** "first" vertices containing the appropriate vertex normal for each vertex.
  702. */
  703. for (faceidx = 0; faceidx < FaceCount; faceidx++) {
  704. for (facevertidx = 0; facevertidx < 3; facevertidx++) {
  705. int vertindex = Faces[faceidx].VertIdx[facevertidx];
  706. int shadeindex = Verts[vertindex].ShadeIndex;
  707. Verts[shadeindex].Normal += Faces[faceidx].Normal;
  708. }
  709. }
  710. /*
  711. ** Smooth this mesh with neighboring meshes!
  712. */
  713. if (WorldInfo != NULL && WorldInfo->Are_Meshes_Smoothed ()) {
  714. for (vertidx = 0; vertidx < VertCount; vertidx++) {
  715. if (Verts[vertidx].ShadeIndex == vertidx) {
  716. Verts[vertidx].Normal += WorldInfo->Get_Shared_Vertex_Normal(Verts[vertidx].Position, Verts[vertidx].SharedSmGroup);
  717. }
  718. }
  719. }
  720. /*
  721. ** Propogate the accumulated normals to all of the other verts which share them
  722. */
  723. for (vertidx = 0; vertidx < VertCount; vertidx++) {
  724. int shadeindex = Verts[vertidx].ShadeIndex;
  725. Verts[vertidx].Normal = Verts[shadeindex].Normal;
  726. Verts[vertidx].Normal.Normalize();
  727. }
  728. }
  729. /***********************************************************************************************
  730. * MeshBuilderClass::Remove_Degenerate_Faces -- discard invalid or duplicated faces *
  731. * *
  732. * INPUT: *
  733. * *
  734. * OUTPUT: *
  735. * *
  736. * WARNINGS: *
  737. * *
  738. * HISTORY: *
  739. * 7/10/98 GTH : Created. *
  740. *=============================================================================================*/
  741. void MeshBuilderClass::Remove_Degenerate_Faces(void)
  742. {
  743. int faceidx;
  744. FaceHasherClass facehasher;
  745. UniqueArrayClass<MeshBuilderClass::FaceClass> uniquefaces(FaceCount,FaceCount/4,&facehasher);
  746. for (faceidx = 0; faceidx < FaceCount; faceidx++) {
  747. if (!Faces[faceidx].Is_Degenerate()) {
  748. uniquefaces.Add(Faces[faceidx]);
  749. }
  750. }
  751. FaceCount = uniquefaces.Count();
  752. AllocFaceCount = uniquefaces.Count();
  753. CurFace = FaceCount;
  754. delete[] Faces;
  755. Faces = W3DNEWARRAY FaceClass[AllocFaceCount];
  756. for (faceidx = 0; faceidx < FaceCount; faceidx++) {
  757. Faces[faceidx] = uniquefaces.Get(faceidx);
  758. }
  759. }
  760. /***********************************************************************************************
  761. * MeshBuilderClass::Compute_Mesh_Stats -- compute some stats about the mesh *
  762. * *
  763. * INPUT: *
  764. * *
  765. * OUTPUT: *
  766. * *
  767. * WARNINGS: *
  768. * *
  769. * HISTORY: *
  770. * 10/19/98 GTH : Created. *
  771. *=============================================================================================*/
  772. void MeshBuilderClass::Compute_Mesh_Stats(void)
  773. {
  774. int pass;
  775. int stage;
  776. int face_index;
  777. int vert_index;
  778. int tex_index[MAX_PASSES][MAX_STAGES];
  779. int shader_index[MAX_PASSES];
  780. int vmat_index[MAX_PASSES];
  781. Stats.Reset();
  782. for (pass = 0; pass<MAX_PASSES; pass++) {
  783. for (stage = 0; stage < MAX_STAGES; stage++) {
  784. tex_index[pass][stage] = Faces[0].TextureIndex[pass][stage];
  785. }
  786. shader_index[pass] = Faces[0].ShaderIndex[pass];
  787. vmat_index[pass] = Verts[0].VertexMaterialIndex[pass];
  788. }
  789. for (pass = 0; pass<MAX_PASSES; pass++) {
  790. for (stage = 0; stage<MAX_STAGES; stage++) {
  791. for (face_index=0; face_index < FaceCount; face_index++) {
  792. if (Faces[face_index].TextureIndex[pass][stage] != tex_index[pass][stage]) {
  793. Stats.HasPerPolyTexture[pass][stage] = true;
  794. break;
  795. }
  796. }
  797. for (vert_index=0; vert_index < VertCount; vert_index++) {
  798. if (Verts[vert_index].TexCoord[pass][stage] != Vector2(0,0)) {
  799. Stats.HasTexCoords[pass][stage] = true;
  800. break;
  801. }
  802. }
  803. }
  804. for (face_index=0; face_index < FaceCount; face_index++) {
  805. if (Faces[face_index].ShaderIndex[pass] != shader_index[pass]) {
  806. Stats.HasPerPolyShader[pass] = true;
  807. break;
  808. }
  809. }
  810. for (vert_index=0; vert_index < VertCount; vert_index++) {
  811. if (Verts[vert_index].VertexMaterialIndex[pass] != vmat_index[pass]) {
  812. Stats.HasPerVertexMaterial[pass] = true;
  813. break;
  814. }
  815. }
  816. for (vert_index=0; vert_index < VertCount; vert_index++) {
  817. if ( (Verts[vert_index].DiffuseColor[pass] != Vector3(1,1,1)) ||
  818. (Verts[vert_index].Alpha[pass] != 1.0f) )
  819. {
  820. Stats.HasDiffuseColor[pass] = true;
  821. break;
  822. }
  823. }
  824. for (vert_index=0; vert_index < VertCount; vert_index++) {
  825. if (Verts[vert_index].SpecularColor[pass] != Vector3(1,1,1)) {
  826. Stats.HasSpecularColor[pass] = true;
  827. break;
  828. }
  829. }
  830. for (vert_index=0; vert_index < VertCount; vert_index++) {
  831. if (Verts[vert_index].DiffuseIllumination[pass] != Vector3(0,0,0)) {
  832. Stats.HasDiffuseIllumination[pass] = true;
  833. break;
  834. }
  835. }
  836. for (stage = 0; stage<MAX_STAGES; stage++) {
  837. for (face_index=0; face_index < FaceCount; face_index++) {
  838. if (Faces[face_index].TextureIndex[pass][stage] != -1) {
  839. Stats.HasTexture[pass][stage] = true;
  840. break;
  841. }
  842. }
  843. }
  844. for (face_index=0; face_index < FaceCount; face_index++) {
  845. if (Faces[face_index].ShaderIndex[pass] != -1) {
  846. Stats.HasShader[pass] = true;
  847. break;
  848. }
  849. }
  850. for (vert_index=0; vert_index < VertCount; vert_index++) {
  851. if (Verts[vert_index].VertexMaterialIndex[pass] != -1) {
  852. Stats.HasVertexMaterial[pass] = true;
  853. break;
  854. }
  855. }
  856. }
  857. }
  858. /***********************************************************************************************
  859. * MeshBuilderClass::Compute_Bounding_Box -- computes an axis-aligned bounding box for the mes *
  860. * *
  861. * INPUT: *
  862. * *
  863. * OUTPUT: *
  864. * *
  865. * WARNINGS: *
  866. * *
  867. * HISTORY: *
  868. * 10/29/98 GTH : Created. *
  869. *=============================================================================================*/
  870. void MeshBuilderClass::Compute_Bounding_Box(Vector3 * set_min,Vector3 * set_max)
  871. {
  872. int i;
  873. assert(set_min != NULL);
  874. assert(set_max != NULL);
  875. // Bounding Box
  876. // straightforward, axis-aligned bounding box.
  877. set_min->X = Verts[0].Position.X;
  878. set_min->Y = Verts[0].Position.Y;
  879. set_min->Z = Verts[0].Position.Z;
  880. set_max->X = Verts[0].Position.X;
  881. set_max->Y = Verts[0].Position.Y;
  882. set_max->Z = Verts[0].Position.Z;
  883. for (i=0; i<VertCount; i++) {
  884. if (Verts[i].Position.X < set_min->X) set_min->X = Verts[i].Position.X;
  885. if (Verts[i].Position.Y < set_min->Y) set_min->Y = Verts[i].Position.Y;
  886. if (Verts[i].Position.Z < set_min->Z) set_min->Z = Verts[i].Position.Z;
  887. if (Verts[i].Position.X > set_max->X) set_max->X = Verts[i].Position.X;
  888. if (Verts[i].Position.Y > set_max->Y) set_max->Y = Verts[i].Position.Y;
  889. if (Verts[i].Position.Z > set_max->Z) set_max->Z = Verts[i].Position.Z;
  890. }
  891. }
  892. /***********************************************************************************************
  893. * MeshBuilderClass::Compute_Bounding_Sphere -- computes a bounding sphere for the mesh *
  894. * *
  895. * INPUT: *
  896. * *
  897. * OUTPUT: *
  898. * *
  899. * WARNINGS: *
  900. * *
  901. * HISTORY: *
  902. * 10/29/98 GTH : Created. *
  903. *=============================================================================================*/
  904. void MeshBuilderClass::Compute_Bounding_Sphere(Vector3 * set_center,float * set_radius)
  905. {
  906. int i;
  907. double dx,dy,dz;
  908. // bounding sphere
  909. // Using the algorithm described in Graphics Gems I page 301.
  910. // This algorithm supposedly generates a bounding sphere within
  911. // 5% of the optimal one but is much faster and simpler to implement.
  912. Vector3 xmin(Verts[0].Position.X,Verts[0].Position.Y,Verts[0].Position.Z);
  913. Vector3 xmax(Verts[0].Position.X,Verts[0].Position.Y,Verts[0].Position.Z);
  914. Vector3 ymin(Verts[0].Position.X,Verts[0].Position.Y,Verts[0].Position.Z);
  915. Vector3 ymax(Verts[0].Position.X,Verts[0].Position.Y,Verts[0].Position.Z);
  916. Vector3 zmin(Verts[0].Position.X,Verts[0].Position.Y,Verts[0].Position.Z);
  917. Vector3 zmax(Verts[0].Position.X,Verts[0].Position.Y,Verts[0].Position.Z);
  918. // FIRST PASS:
  919. // finding the 6 minima and maxima points
  920. for (i=1; i<VertCount; i++) {
  921. if (Verts[i].Position.X < xmin.X) {
  922. xmin.X = Verts[i].Position.X; xmin.Y = Verts[i].Position.Y; xmin.Z = Verts[i].Position.Z;
  923. }
  924. if (Verts[i].Position.X > xmax.X) {
  925. xmax.X = Verts[i].Position.X; xmax.Y = Verts[i].Position.Y; xmax.Z = Verts[i].Position.Z;
  926. }
  927. if (Verts[i].Position.Y < ymin.Y) {
  928. ymin.X = Verts[i].Position.X; ymin.Y = Verts[i].Position.Y; ymin.Z = Verts[i].Position.Z;
  929. }
  930. if (Verts[i].Position.Y > ymax.Y) {
  931. ymax.X = Verts[i].Position.X; ymax.Y = Verts[i].Position.Y; ymax.Z = Verts[i].Position.Z;
  932. }
  933. if (Verts[i].Position.Z < zmin.Z) {
  934. zmin.X = Verts[i].Position.X; zmin.Y = Verts[i].Position.Y; zmin.Z = Verts[i].Position.Z;
  935. }
  936. if (Verts[i].Position.Z > zmax.Z) {
  937. zmax.X = Verts[i].Position.X; zmax.Y = Verts[i].Position.Y; zmax.Z = Verts[i].Position.Z;
  938. }
  939. }
  940. // xspan = distance between the 2 points xmin and xmax squared.
  941. // same goes for yspan and zspan.
  942. dx = xmax.X - xmin.X;
  943. dy = xmax.Y - xmin.Y;
  944. dz = xmax.Z - xmin.Z;
  945. double xspan = dx*dx + dy*dy + dz*dz;
  946. dx = ymax.X - ymin.X;
  947. dy = ymax.Y - ymin.Y;
  948. dz = ymax.Z - ymin.Z;
  949. double yspan = dx*dx + dy*dy + dz*dz;
  950. dx = zmax.X - zmin.X;
  951. dy = zmax.Y - zmin.Y;
  952. dz = zmax.Z - zmin.Z;
  953. double zspan = dx*dx + dy*dy + dz*dz;
  954. // Set points dia1 and dia2 to the maximally separated pair
  955. // This will be the diameter of the initial sphere
  956. Vector3 dia1 = xmin;
  957. Vector3 dia2 = xmax;
  958. double maxspan = xspan;
  959. if (yspan > maxspan) {
  960. maxspan = yspan;
  961. dia1 = ymin;
  962. dia2 = ymax;
  963. }
  964. if (zspan > maxspan) {
  965. maxspan = zspan;
  966. dia1 = zmin;
  967. dia2 = zmax;
  968. }
  969. // Compute initial center and radius and radius squared
  970. Vector3 center;
  971. center.X = (dia1.X + dia2.X) / 2.0f;
  972. center.Y = (dia1.Y + dia2.Y) / 2.0f;
  973. center.Z = (dia1.Z + dia2.Z) / 2.0f;
  974. dx = dia2.X - center.X;
  975. dy = dia2.Y - center.Y;
  976. dz = dia2.Z - center.Z;
  977. double radsqr = dx*dx + dy*dy + dz*dz;
  978. double radius = sqrt(radsqr);
  979. // SECOND PASS:
  980. // Increment current sphere if any points fall outside of it.
  981. for (i=0; i<VertCount; i++) {
  982. dx = Verts[i].Position.X - center.X;
  983. dy = Verts[i].Position.Y - center.Y;
  984. dz = Verts[i].Position.Z - center.Z;
  985. double testrad2 = dx*dx + dy*dy + dz*dz;
  986. if (testrad2 > radsqr) {
  987. // this point was outside the old sphere, compute a new
  988. // center point and radius which contains this point
  989. double testrad = sqrt(testrad2);
  990. // adjust center and radius
  991. radius = (radius + testrad) / 2.0;
  992. radsqr = radius * radius;
  993. double oldtonew = testrad - radius;
  994. center.X = (radius * center.X + oldtonew * Verts[i].Position.X) / testrad;
  995. center.Y = (radius * center.Y + oldtonew * Verts[i].Position.Y) / testrad;
  996. center.Z = (radius * center.Z + oldtonew * Verts[i].Position.Z) / testrad;
  997. }
  998. }
  999. *set_center = center;
  1000. *set_radius = radius;
  1001. }
  1002. /***********************************************************************************************
  1003. * MeshBuilderClass::Optimize_Mesh -- "optimize" the mesh *
  1004. * *
  1005. * Generates the array of unique vertices and sets the vertex indices in each face. Then *
  1006. * all of the faces are sorted by material and then into stripping order. *
  1007. * *
  1008. * INPUT: *
  1009. * *
  1010. * OUTPUT: *
  1011. * *
  1012. * WARNINGS: *
  1013. * *
  1014. * HISTORY: *
  1015. * 5/15/98 GTH : Created. *
  1016. *=============================================================================================*/
  1017. void MeshBuilderClass::Optimize_Mesh(bool compute_normals)
  1018. {
  1019. int faceidx;
  1020. int vertidx;
  1021. int facevertidx;
  1022. int match_normals = 0;
  1023. if (!compute_normals) {
  1024. match_normals = 1;
  1025. }
  1026. VertexArrayClass unique_verts(FaceCount * 3,match_normals);
  1027. /*
  1028. ** Find the min and max of the array of vertices
  1029. */
  1030. Vector3 minv = Faces[0].Verts[0].Position;
  1031. Vector3 maxv = Faces[0].Verts[0].Position;
  1032. for (faceidx = 0; faceidx < FaceCount; faceidx++) {
  1033. for (facevertidx = 0; facevertidx < 3; facevertidx++) {
  1034. minv.Update_Min(Faces[faceidx].Verts[facevertidx].Position);
  1035. maxv.Update_Max(Faces[faceidx].Verts[facevertidx].Position);
  1036. }
  1037. }
  1038. /*
  1039. ** Tell the vertex array the bounds so that it can do better hashing.
  1040. */
  1041. unique_verts.Set_Bounds(minv,maxv);
  1042. /*
  1043. ** Build the array of unique vertices
  1044. */
  1045. for (faceidx = 0; faceidx < FaceCount; faceidx++) {
  1046. for (facevertidx = 0; facevertidx < 3; facevertidx++) {
  1047. Faces[faceidx].VertIdx[facevertidx] =
  1048. unique_verts.Submit_Vertex(Faces[faceidx].Verts[facevertidx]);
  1049. }
  1050. }
  1051. /*
  1052. ** Assign the shared smoothing groups from each 'master' vertex
  1053. ** to each referring vertex.
  1054. */
  1055. unique_verts.Propogate_Shared_Smooth_Groups();
  1056. /*
  1057. ** Replace the vertex array with the new unique vertex array
  1058. */
  1059. VertCount = unique_verts.VertCount;
  1060. Verts = W3DNEWARRAY VertClass[VertCount];
  1061. for (vertidx=0; vertidx<VertCount;vertidx++) {
  1062. Verts[vertidx] = unique_verts[vertidx];
  1063. }
  1064. /*
  1065. ** Build a new array of faces, removing degenerate faces
  1066. */
  1067. Remove_Degenerate_Faces();
  1068. /*
  1069. ** Calculate the vertex normals if user didn't supply them.
  1070. ** Recompute the face normals just in case they changed a little
  1071. ** due to vertex welding.
  1072. */
  1073. Compute_Face_Normals();
  1074. if (compute_normals) {
  1075. Compute_Vertex_Normals();
  1076. }
  1077. /*
  1078. ** An "Optimal" mesh will follow the following rules:
  1079. **
  1080. ** Triangle Ordering:
  1081. ** - Triangles are in strip order
  1082. ** - Triangles are sorted by textures and detail textures in all passes (!?)
  1083. ** - Triangles are sorted by shader in all passes(!)
  1084. **
  1085. ** Vertex Ordering:
  1086. ** - Skin matrix order if the mesh is being "skinned"
  1087. ** - Few vertex material switches
  1088. ** - Strip ordering
  1089. **
  1090. ** All verts should use the same data fields in each pass (alpha, dcg, ect)
  1091. ** All triangles must use same number of passes.
  1092. **
  1093. ** Here is how I will attempt to get as "close as possible" to this
  1094. ** - find the pass and stage which has the "worst-case" number of textures
  1095. ** and sort all polys with respect to the textures in that pass and stage
  1096. ** - see which pass has the most vertex materials (if any have more than one)
  1097. ** and sort the vertices with respect to the mats in that pass
  1098. ** - put the triangles into strip order based on the pass and stage with the
  1099. ** most texture switches.
  1100. **
  1101. ** NOTE: Most meshes should be able to use *one* vertex material and *one* shader
  1102. ** We should throw up a warning flag in the asset manager whenever we encounter
  1103. ** a mesh which uses more than one of these per pass...
  1104. */
  1105. Compute_Mesh_Stats();
  1106. Stats.UVSplitCount = unique_verts.UVSplits;
  1107. /*
  1108. ** Sort the polys by texture
  1109. */
  1110. qsort(Faces,FaceCount,sizeof(FaceClass),Texture_Compare_Funcs[PolyOrderPass][PolyOrderStage]);
  1111. /*
  1112. ** Sort the vertices by bone index and vertex material
  1113. */
  1114. Sort_Vertices();
  1115. /*
  1116. ** Strip optimize the mesh
  1117. */
  1118. Strip_Optimize_Mesh();
  1119. Verify_Face_Normals();
  1120. }
  1121. /***********************************************************************************************
  1122. * MeshSaveClass::Strip_Optimize_Mesh -- optimize the mesh for triangle strips *
  1123. * *
  1124. * This algorithm was copied/modified from the surrender srModeller code so that we don't have *
  1125. * to pass our models through the modeller at run-time. *
  1126. * *
  1127. * INPUT: *
  1128. * *
  1129. * OUTPUT: *
  1130. * *
  1131. * WARNINGS: *
  1132. * *
  1133. * HISTORY: *
  1134. * 4/6/98 GTH : Created. *
  1135. * 10/20/98 GTH : Modified to strip based on the desired texture channel *
  1136. * 2/11/99 GTH : Added strip stats tracking *
  1137. *=============================================================================================*/
  1138. void MeshBuilderClass::Strip_Optimize_Mesh(void)
  1139. {
  1140. WingedEdgeStruct * edgehash[512];
  1141. WingedEdgeStruct * edgetab = W3DNEWARRAY WingedEdgeStruct[FaceCount*3];
  1142. WingedEdgePolyStruct * pedges = W3DNEWARRAY WingedEdgePolyStruct[FaceCount];
  1143. FaceClass * newpolys = W3DNEWARRAY FaceClass[FaceCount];
  1144. int * newmat = W3DNEWARRAY int[FaceCount];
  1145. int * premap = W3DNEWARRAY int[FaceCount];
  1146. int * vtimestamp = W3DNEWARRAY int[VertCount];
  1147. int vcount = 0;
  1148. int polysinserted = 0;
  1149. int lastmat = 0;
  1150. int edgecount = 0;
  1151. int i,j;
  1152. // init the tables
  1153. memset(edgehash,0,sizeof(WingedEdgeStruct *) * 512);
  1154. memset(edgetab,0,sizeof(WingedEdgeStruct) * FaceCount * 3);
  1155. for (i=0; i<FaceCount; i++) {
  1156. premap[i] = -1;
  1157. }
  1158. for (i=0; i<VertCount; i++) {
  1159. vtimestamp[i] = -1;
  1160. }
  1161. // Build the edge table for the mesh
  1162. // Oct 20,1998: modifying so that every place we were looking at the
  1163. // material index, we now use TextureIndex[PolyOrderPass][PolyOrderStage]
  1164. for (i=0; i<FaceCount; i++) {
  1165. int mat = Faces[i].TextureIndex[PolyOrderPass][PolyOrderStage];
  1166. for (j=0; j<3; j++) {
  1167. // build the WingedEdgePolyStruct for this face. basically, for each edge,
  1168. // see if we already have the edge and if so, link to it. otherwise
  1169. // add the edge to the edge table
  1170. int hash;
  1171. int v0,v1;
  1172. WingedEdgeStruct * edge;
  1173. v0 = j;
  1174. if (v0 < 2) v1 = v0+1; else v1 = 0;
  1175. v0 = Faces[i].VertIdx[v0];
  1176. v1 = Faces[i].VertIdx[v1];
  1177. // order the vertices before hashing (hash depends on vert order)
  1178. if (v0 > v1) { int tmp = v0; v0 = v1; v1 = tmp; }
  1179. // hash value for the edge
  1180. hash = (v0 + v1*119)&511;
  1181. // seek edge from hash table
  1182. for (edge = edgehash[hash]; edge; edge = edge->Next) {
  1183. if (edge->Vertex[0] == v0 && edge->Vertex[1] == v1 && edge->MaterialIdx == mat)
  1184. break;
  1185. }
  1186. if (edge) {
  1187. // found the edge
  1188. edge->Poly[1] = i;
  1189. } else {
  1190. // create new edge and link it to hash table
  1191. edge = edgetab + edgecount++;
  1192. edge->Next = edgehash[hash];
  1193. edgehash[hash] = edge;
  1194. edge->Vertex[0] = v0;
  1195. edge->Vertex[1] = v1;
  1196. edge->Poly[0] = i;
  1197. edge->Poly[1] = -1;
  1198. edge->MaterialIdx = mat;
  1199. }
  1200. pedges[i].Edge[j] = edge;
  1201. }
  1202. }
  1203. // the following loop inserts polygons into a new polygon list until
  1204. // all polygons have been inserted. Internally it attempts to create
  1205. // as long strips as possible while minimizing material switches
  1206. // and maintaining vertex reusage coherency to optimize geometry
  1207. // engine performance.
  1208. while (polysinserted < FaceCount) {
  1209. int startpoly = -1; // best polygon found so far
  1210. int bestc = (1<<29); // best polygon weight value found so far
  1211. int startpass = 0; // should we start from pass 0 or 1?
  1212. int findpass; // 0 = same material only, 1 = any polygon
  1213. int c; // c = number of shared edges
  1214. // first attempt to minimize material switches by choosing a polygon with same material
  1215. // as the starting poly. Basically what we want is the poly with same material with
  1216. // least shared edges and most recent vertices (as we'd like to start the strip from
  1217. // a 'corner polygon'). This pass is done only if mesh has multiple materials.
  1218. // The second pass scans through all polygons.
  1219. // this loop is O(N*N) -> might turn a bit nasty on larger meshes. Try
  1220. // to figure out a way to solve the problem...
  1221. for (findpass = startpass; findpass < 2; findpass++)
  1222. {
  1223. // loop through all polygons
  1224. for (i = 0; i < FaceCount; i++) {
  1225. // if polygon not picked yet
  1226. if (premap[i]==-1) {
  1227. // if material mismatch, cannot choose poly (except in pass 1)
  1228. if (findpass == 0 && Faces[i].TextureIndex[PolyOrderPass][PolyOrderStage] != lastmat)
  1229. continue;
  1230. // calculate number of shared edges
  1231. for (c = 0, j = 0; j < 3; j++) {
  1232. // if edge j is shared by two tris,
  1233. // use a weight factor of vCount for each edge
  1234. if (pedges[i].Edge[j]->Poly[1] >= 0) {
  1235. c += (vcount+1);
  1236. }
  1237. }
  1238. // calculate delta vertex timestamp
  1239. for (j = 0; j < 3; j++) {
  1240. c += (vcount-vtimestamp[Faces[i].VertIdx[j]]);
  1241. }
  1242. // if better than current best pick
  1243. if (c < bestc) {
  1244. bestc = c;
  1245. startpoly = i;
  1246. }
  1247. }
  1248. }
  1249. // if we managed to find a suitable starting poly
  1250. if (startpoly != -1)
  1251. break;
  1252. }
  1253. // track the fact that we created a new strip:
  1254. Stats.StripCount++;
  1255. // update the material index
  1256. lastmat = Faces[startpoly].TextureIndex[PolyOrderPass][PolyOrderStage];
  1257. newmat[polysinserted] = Faces[startpoly].TextureIndex[PolyOrderPass][PolyOrderStage];
  1258. // Add the selected polygon to the new polygon list.
  1259. // for each edge of start poly, see if the "other" polygon using that edge
  1260. // is untaken, if so, store the new polygon such that that edge index is last
  1261. bool found_shared_edge = false;
  1262. newpolys[polysinserted] = Faces[startpoly];
  1263. FaceClass * newpoly = &(newpolys[polysinserted]);
  1264. for (int edge_index = 0; (edge_index < 3) && !found_shared_edge; edge_index++) {
  1265. for (int side_index = 0; (side_index < 2) && !found_shared_edge; side_index++) {
  1266. // if this polygon is not the startpoly and it is not "taken", then this edge is ok!
  1267. int poly = pedges[startpoly].Edge[edge_index]->Poly[side_index];
  1268. if ((poly != -1) && (poly != startpoly) && (premap[poly] == -1)) {
  1269. // find vert which is not on the final edge
  1270. int first_vert = -1;
  1271. for (int vidx=0; vidx<3; vidx++) {
  1272. if ( (newpoly->VertIdx[vidx] != pedges[startpoly].Edge[edge_index]->Vertex[0]) &&
  1273. (newpoly->VertIdx[vidx] != pedges[startpoly].Edge[edge_index]->Vertex[1])) {
  1274. first_vert = newpoly->VertIdx[vidx];
  1275. break;
  1276. }
  1277. }
  1278. assert(first_vert != -1);
  1279. // rotate the vertex indices until first_vert is the index of VertIdx[0]
  1280. while (newpoly->VertIdx[0] != first_vert) {
  1281. int tmp = newpoly->VertIdx[0];
  1282. newpoly->VertIdx[0] = newpoly->VertIdx[1];
  1283. newpoly->VertIdx[1] = newpoly->VertIdx[2];
  1284. newpoly->VertIdx[2] = tmp;
  1285. }
  1286. // ok, we found a shareable edge and adjusted our poly so the strip
  1287. // will start with it. Now break out of this loop...
  1288. found_shared_edge = true;
  1289. break;
  1290. }
  1291. }
  1292. }
  1293. // if a shared edge wasn't found, just copy the poly
  1294. if (!found_shared_edge) {
  1295. newpolys[polysinserted] = Faces[startpoly];
  1296. }
  1297. // Increment the count. Mark the vertices as used (i.e. update their timestamps)
  1298. premap[startpoly] = polysinserted;
  1299. polysinserted++;
  1300. for (i = 0; i < 3; i++) {
  1301. if (vtimestamp[Faces[startpoly].VertIdx[i]]==-1) {
  1302. vtimestamp[Faces[startpoly].VertIdx[i]] = vcount++;
  1303. }
  1304. }
  1305. // If we have no shared edges, this is a lone poly, start another strip
  1306. if (pedges[startpoly].Edge[0]->Poly[1] == -1 &&
  1307. pedges[startpoly].Edge[1]->Poly[1] == -1 &&
  1308. pedges[startpoly].Edge[2]->Poly[1] == -1)
  1309. continue;
  1310. // Build the strip starting from the polygon chosen in the previous loop
  1311. int vFIFO[2]; // vertex fifo
  1312. int scnt = 0; // strip index count (for poly order flipping)
  1313. int nextpoly = startpoly;
  1314. vFIFO[0] = newpoly->VertIdx[1]; // setup the vFIFO
  1315. vFIFO[1] = newpoly->VertIdx[2];
  1316. while (nextpoly != -1)
  1317. {
  1318. startpoly = nextpoly;
  1319. nextpoly = -1;
  1320. for (i = 0; i < 3; i++) {
  1321. // if edge 'i' of startpoly matches the vertices in the fifo
  1322. if ((pedges[startpoly].Edge[i]->Vertex[0] == vFIFO[0] && pedges[startpoly].Edge[i]->Vertex[1] == vFIFO[1]) ||
  1323. (pedges[startpoly].Edge[i]->Vertex[1] == vFIFO[0] && pedges[startpoly].Edge[i]->Vertex[0] == vFIFO[1]))
  1324. {
  1325. for (j = 0; j < 2; j++) {
  1326. // if poly 'j' attached to this edge has not been used already, use it!
  1327. if (pedges[startpoly].Edge[i]->Poly[j] > -1 &&
  1328. premap[pedges[startpoly].Edge[i]->Poly[j]] == -1)
  1329. {
  1330. nextpoly = pedges[startpoly].Edge[i]->Poly[j];
  1331. goto found;
  1332. }
  1333. }
  1334. }
  1335. }
  1336. // couldn't find another poly, break from loop
  1337. break;
  1338. found:;
  1339. // now, find the new vertex (two verts are on the edge, find the third)
  1340. int nw = -1;
  1341. for (i = 0; i < 3; i++) {
  1342. if (Faces[nextpoly].VertIdx[i] != vFIFO[0] && Faces[nextpoly].VertIdx[i] != vFIFO[1]) {
  1343. nw = i;
  1344. break;
  1345. }
  1346. }
  1347. assert(nw != -1);
  1348. int new_vindex = Faces[nextpoly].VertIdx[nw];
  1349. newmat[polysinserted] = Faces[nextpoly].TextureIndex[PolyOrderPass][PolyOrderStage];
  1350. // add the poly to the newpolys array.
  1351. newpolys[polysinserted].VertIdx[0] = vFIFO[0];
  1352. newpolys[polysinserted].VertIdx[1] = vFIFO[1];
  1353. newpolys[polysinserted].VertIdx[2] = new_vindex;
  1354. // if we are on an even triangle, swap the vertex ordering
  1355. if (!(scnt&1)) {
  1356. int tmp = newpolys[polysinserted].VertIdx[0];
  1357. newpolys[polysinserted].VertIdx[0] = newpolys[polysinserted].VertIdx[1];
  1358. newpolys[polysinserted].VertIdx[1] = tmp;
  1359. }
  1360. // push the new vertex into the fifo
  1361. vFIFO[0] = vFIFO[1];
  1362. vFIFO[1] = new_vindex;
  1363. if (vtimestamp[new_vindex]==-1) {
  1364. vtimestamp[new_vindex] = vcount++;
  1365. }
  1366. premap[nextpoly] = polysinserted++;
  1367. scnt++;
  1368. }
  1369. // scnt+1 is the number of polys that were added to the strip
  1370. Stats.AvgStripLength += scnt+1;
  1371. if (scnt+1 > Stats.MaxStripLength) {
  1372. Stats.MaxStripLength = scnt+1;
  1373. }
  1374. }
  1375. // Use the premap array to get the rest of the info into the new face table,
  1376. for (i=0; i<FaceCount; i++) {
  1377. int old_idx = i;
  1378. int new_idx = premap[i];
  1379. for (int pass=0; pass < MAX_PASSES; pass++) {
  1380. for (int stage=0; stage < MAX_STAGES; stage++) {
  1381. newpolys[new_idx].TextureIndex[pass][stage] = Faces[old_idx].TextureIndex[pass][stage];
  1382. }
  1383. newpolys[new_idx].ShaderIndex[pass] = Faces[old_idx].ShaderIndex[pass];
  1384. }
  1385. newpolys[new_idx].SmGroup = Faces[old_idx].SmGroup;
  1386. newpolys[new_idx].Index = Faces[old_idx].Index;
  1387. newpolys[new_idx].Attributes = Faces[old_idx].Attributes;
  1388. newpolys[new_idx].AddIndex = Faces[old_idx].AddIndex;
  1389. newpolys[new_idx].Normal = Faces[old_idx].Normal;
  1390. newpolys[new_idx].Dist = Faces[old_idx].Dist;
  1391. newpolys[new_idx].SurfaceType = Faces[old_idx].SurfaceType;
  1392. // just checking
  1393. assert(newmat[new_idx] == Faces[old_idx].TextureIndex[PolyOrderPass][PolyOrderStage]);
  1394. }
  1395. // then install the pointer to the new face table.
  1396. FaceClass * oldfaces = Faces;
  1397. Faces = newpolys;
  1398. delete[] oldfaces;
  1399. AllocFaceCount = FaceCount;
  1400. // release allocated memory
  1401. delete[] edgetab;
  1402. delete[] pedges;
  1403. delete[] premap;
  1404. delete[] newmat;
  1405. delete[] vtimestamp;
  1406. // finish the computation of the average strip length
  1407. Stats.AvgStripLength /= Stats.StripCount;
  1408. }
  1409. /***********************************************************************************************
  1410. * MeshBuilderClass::Grow_Face_Array -- increases the size of the face array *
  1411. * *
  1412. * INPUT: *
  1413. * *
  1414. * OUTPUT: *
  1415. * *
  1416. * WARNINGS: *
  1417. * *
  1418. * HISTORY: *
  1419. * 5/15/98 GTH : Created. *
  1420. *=============================================================================================*/
  1421. void MeshBuilderClass::Grow_Face_Array(void)
  1422. {
  1423. int oldcount = AllocFaceCount;
  1424. FaceClass * oldfaces = Faces;
  1425. AllocFaceCount += AllocFaceGrowth;
  1426. Faces = W3DNEWARRAY FaceClass[AllocFaceCount];
  1427. for (int i=0; i<oldcount; i++) {
  1428. Faces[i] = oldfaces[i];
  1429. }
  1430. delete[] oldfaces;
  1431. }
  1432. /***********************************************************************************************
  1433. * MeshBuilderClass::Sort_Vertices -- sorts vertices *
  1434. * *
  1435. * the vertices are sorted first by bone index, second by vertex material. *
  1436. * *
  1437. * INPUT: *
  1438. * *
  1439. * OUTPUT: *
  1440. * *
  1441. * WARNINGS: *
  1442. * *
  1443. * HISTORY: *
  1444. * 5/1/2000 gth : Created. *
  1445. *=============================================================================================*/
  1446. void MeshBuilderClass::Sort_Vertices(void)
  1447. {
  1448. /*
  1449. ** Sort the vertices
  1450. */
  1451. qsort(Verts,VertCount,sizeof(VertClass),vertex_compare);
  1452. /*
  1453. ** Build a vertex remap table (table[old_index] = new_index) and
  1454. ** reset each vertex's UniqueIndex.
  1455. */
  1456. int * vertex_remap_table = W3DNEWARRAY int[VertCount];
  1457. for (int vi=0; vi<VertCount; vi++) {
  1458. vertex_remap_table[Verts[vi].UniqueIndex] = vi;
  1459. }
  1460. /*
  1461. ** Remap the faces' vertex indices
  1462. */
  1463. for (int fi=0; fi<FaceCount; fi++) {
  1464. for (int vi=0; vi<3; vi++) {
  1465. int old_index = Faces[fi].VertIdx[vi];
  1466. Faces[fi].VertIdx[vi] = vertex_remap_table[old_index];
  1467. }
  1468. }
  1469. /*
  1470. ** Deallocate the temporary remap table
  1471. */
  1472. delete[] vertex_remap_table;
  1473. }
  1474. /*
  1475. ** Compare functions for qsorting the polygons by texture.
  1476. */
  1477. int face_material_compare(const void *elem1, const void *elem2 )
  1478. {
  1479. MeshBuilderClass::FaceClass * f0 = (MeshBuilderClass::FaceClass *)elem1;
  1480. MeshBuilderClass::FaceClass * f1 = (MeshBuilderClass::FaceClass *)elem2;
  1481. if (f0->TextureIndex[0][0] < f1->TextureIndex[0][0]) return -1;
  1482. if (f0->TextureIndex[0][0] > f1->TextureIndex[0][0]) return 1;
  1483. return 0;
  1484. }
  1485. inline int tex_compare(const void * elem1,const void * elem2,int pass,int stage)
  1486. {
  1487. MeshBuilderClass::FaceClass * f0 = (MeshBuilderClass::FaceClass *)elem1;
  1488. MeshBuilderClass::FaceClass * f1 = (MeshBuilderClass::FaceClass *)elem2;
  1489. /*
  1490. ** Primarily, sort by texture
  1491. */
  1492. if (f0->TextureIndex[pass][stage] < f1->TextureIndex[pass][stage]) return -1;
  1493. if (f0->TextureIndex[pass][stage] > f1->TextureIndex[pass][stage]) return 1;
  1494. /*
  1495. ** Secondarily, sort by vertex material index
  1496. */
  1497. if (f0->Verts[0].VertexMaterialIndex[pass] < f1->Verts[0].VertexMaterialIndex[pass]) return -1;
  1498. if (f0->Verts[0].VertexMaterialIndex[pass] > f1->Verts[0].VertexMaterialIndex[pass]) return 1;
  1499. return 0;
  1500. }
  1501. int pass0_stage0_compare(const void *elem1, const void *elem2)
  1502. {
  1503. return tex_compare(elem1,elem2,0,0);
  1504. }
  1505. int pass0_stage1_compare(const void *elem1, const void *elem2)
  1506. {
  1507. return tex_compare(elem1,elem2,0,1);
  1508. }
  1509. int pass1_stage0_compare(const void *elem1, const void *elem2)
  1510. {
  1511. return tex_compare(elem1,elem2,1,0);
  1512. }
  1513. int pass1_stage1_compare(const void *elem1, const void *elem2)
  1514. {
  1515. return tex_compare(elem1,elem2,1,1);
  1516. }
  1517. int pass2_stage0_compare(const void *elem1, const void *elem2)
  1518. {
  1519. return tex_compare(elem1,elem2,2,0);
  1520. }
  1521. int pass2_stage1_compare(const void *elem1, const void *elem2)
  1522. {
  1523. return tex_compare(elem1,elem2,2,1);
  1524. }
  1525. int pass3_stage0_compare(const void *elem1, const void *elem2)
  1526. {
  1527. return tex_compare(elem1,elem2,3,0);
  1528. }
  1529. int pass3_stage1_compare(const void *elem1, const void *elem2)
  1530. {
  1531. return tex_compare(elem1,elem2,3,1);
  1532. }
  1533. int vertex_compare(const void *elem1, const void *elem2)
  1534. {
  1535. MeshBuilderClass::VertClass * v0 = (MeshBuilderClass::VertClass *)elem1;
  1536. MeshBuilderClass::VertClass * v1 = (MeshBuilderClass::VertClass *)elem2;
  1537. /*
  1538. ** Sort first by bone index, then by vertex material in pass0
  1539. */
  1540. if (v0->BoneIndex < v1->BoneIndex) return -1;
  1541. if (v0->BoneIndex > v1->BoneIndex) return 1;
  1542. if (v0->VertexMaterialIndex[0] < v1->VertexMaterialIndex[0]) return -1;
  1543. if (v0->VertexMaterialIndex[0] > v1->VertexMaterialIndex[0]) return 1;
  1544. return 0;
  1545. }