meshbuild.cpp 71 KB

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