meshbuild.cpp 71 KB

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