meshsave.cpp 79 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /* $Header: /Commando/Code/Tools/max2w3d/meshsave.cpp 107 8/21/01 10:28a Greg_h $ */
  19. /***********************************************************************************************
  20. *** Confidential - Westwood Studios ***
  21. ***********************************************************************************************
  22. * *
  23. * Project Name : Commando / G *
  24. * *
  25. * File Name : MESHSAVE.CPP *
  26. * *
  27. * Programmer : Greg Hjelstrom *
  28. * *
  29. * Start Date : 06/10/97 *
  30. * *
  31. * Last Update : 10/20/1999997 [GH] *
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * MeshSaveClass::MeshSaveClass -- constructor, processes a Max mesh *
  36. * MeshSaveClass::~MeshSaveClass -- destructor, frees all allocated memory *
  37. * MeshSaveClass::write_verts -- write the vertex chunk into a wtm file *
  38. * MeshSaveClass::write_header -- write a mesh header chunk into a wtm file *
  39. * MeshSaveClass::Write_To_File -- Append the mesh to an open wtm file *
  40. * MeshSaveClass::write_normals -- writes the vertex normals chunk into a wtm file *
  41. * MeshSaveClass::write_vert_normals -- Writes the surrender normal chunk into a wtm file *
  42. * MeshSaveClass::write_triangles -- Write the triangles chunk into a wtm file. *
  43. * MeshSaveClass::write_sr_triangles -- writes the triangles in surrender friendly format *
  44. * MeshSaveClass::write_triangles -- write the triangles chunk *
  45. * MeshSaveClass::compute_surrender_vertex -- Compute the surrender vertex normals *
  46. * MeshSaveClass::setup_material -- Gets the texture names and base colors for a material *
  47. * MeshSaveClass::compute_bounding_volumes -- computes a bounding box and bounding sphere for*
  48. * MeshSaveClass::set_transform -- set the default transformation matrix for the mesh *
  49. * MeshSaveClass::compute_physical_properties -- computes the volume and moment of inertia *
  50. * MeshSaveClass::prep_mesh -- pre-transform the MAX mesh by a specified matrix *
  51. * MeshSaveClass::write_user_text -- write the user text chunk *
  52. * MeshSaveClass::get_htree_bone_index_for_inode -- searches the htree for the given INode *
  53. * MeshSaveClass::get_skin_modifier_objects -- Searches for the WWSkin modifier for this mes *
  54. * MeshSaveClass::inv_deform_mesh -- preprocess the mesh for skinning *
  55. * MeshSaveClass::create_materials -- create the materials for this mesh *
  56. * MeshSaveClass::write_ps2_shaders -- Write shaders specific to the PS2 in their own chunk. *
  57. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  58. #include "meshsave.h"
  59. #include <Max.h>
  60. #include <stdmat.h>
  61. #include <modstack.h>
  62. #include "gamemtl.h"
  63. #include "errclass.h"
  64. #include "vxl.h"
  65. #include "vxldbg.h"
  66. #include "nodelist.h"
  67. #include "hiersave.h"
  68. #include "util.h"
  69. #include "w3dappdata.h"
  70. #include "skin.h"
  71. #include "skindata.h"
  72. #include "meshbuild.h"
  73. #include "alphamodifier.h"
  74. #include "aabtreebuilder.h"
  75. #include "exportlog.h"
  76. static char _string1[512];
  77. const int VOXEL_RESOLUTION = 64; // resolution to use when computing I, V and CM
  78. #define DEBUG_VOXELS 0
  79. #define MIN_AABTREE_POLYGONS 8
  80. #define DIFFUSE_HOUSECOLOR_TEXTURE_PREFIX 0x4443485A //'ZHCD' prefix put on all code generated textures
  81. #define DIFFUSE_COLOR_TEXTURE_PREFIX 0x44434d5A //'ZMCD' prefix put on all code generated textures
  82. #define DIFFUSE_COLOR_TEXTURE_MASK 0x4443005A
  83. /************************************************************************************
  84. **
  85. ** Compute the determinant of the 3x3 portion of the given matrix
  86. **
  87. ************************************************************************************/
  88. float Compute_3x3_Determinant(const Matrix3 & tm)
  89. {
  90. float det = tm[0][0] * (tm[1][1]*tm[2][2] - tm[1][2]*tm[2][1]);
  91. det -= tm[0][1] * (tm[1][0]*tm[2][2] - tm[1][2]*tm[2][0]);
  92. det += tm[0][2] * (tm[1][0]*tm[2][1] - tm[1][1]*tm[2][0]);
  93. return det;
  94. }
  95. /************************************************************************************
  96. **
  97. ** check if this is a mesh which should use a simple rendering method. I don't
  98. ** compute vertex normals or store u-v's in that case (prevents vertex splitting)
  99. **
  100. ************************************************************************************/
  101. bool use_simple_rendering(int geo_type)
  102. {
  103. geo_type &= W3D_MESH_FLAG_GEOMETRY_TYPE_MASK;
  104. if ( (geo_type == OBSOLETE_W3D_MESH_FLAG_GEOMETRY_TYPE_SHADOW) ||
  105. (geo_type == W3D_MESH_FLAG_GEOMETRY_TYPE_AABOX) ||
  106. (geo_type == W3D_MESH_FLAG_GEOMETRY_TYPE_OBBOX) )
  107. {
  108. return true;
  109. } else {
  110. return false;
  111. }
  112. }
  113. /************************************************************************************
  114. **
  115. ** build the bitfield of W3D mesh attributes for the given node
  116. **
  117. ************************************************************************************/
  118. uint32 setup_mesh_attributes(INode * node)
  119. {
  120. uint32 attributes = W3D_MESH_FLAG_NONE;
  121. /*
  122. ** Mesh will be one of:
  123. ** W3D_MESH_FLAG_NONE,
  124. ** W3D_MESH_FLAG_COLLISION_BOX,
  125. ** W3D_MESH_FLAG_SKIN,
  126. ** W3D_MESH_FLAG_ALIGNED
  127. ** W3D_MESH_FLAG_ORIENTED
  128. */
  129. if (Is_Collision_AABox(node)) {
  130. attributes = W3D_MESH_FLAG_GEOMETRY_TYPE_AABOX;
  131. } else if (Is_Collision_OBBox(node)) {
  132. attributes = W3D_MESH_FLAG_GEOMETRY_TYPE_OBBOX;
  133. } else if (Is_Skin(node)) {
  134. attributes = W3D_MESH_FLAG_GEOMETRY_TYPE_SKIN;
  135. } else if (Is_Camera_Aligned_Mesh(node)) {
  136. attributes = W3D_MESH_FLAG_GEOMETRY_TYPE_CAMERA_ALIGNED;
  137. } else if (Is_Camera_Oriented_Mesh(node)) {
  138. attributes = W3D_MESH_FLAG_GEOMETRY_TYPE_CAMERA_ORIENTED;
  139. }
  140. /*
  141. ** And, a mesh may have one or more types of collision detection enabled.
  142. ** W3D_MESH_FLAG_COLLISION_TYPE_PHYSICAL
  143. ** W3D_MESH_FLAG_COLLISION_TYPE_PROJECTILE
  144. ** However, if the mesh is SKIN, SHADOW, ALIGNED, ORIENTED or NULL, don't let
  145. ** the collision bits get set...
  146. */
  147. if ( attributes != W3D_MESH_FLAG_GEOMETRY_TYPE_SKIN &&
  148. attributes != W3D_MESH_FLAG_GEOMETRY_TYPE_CAMERA_ALIGNED &&
  149. attributes != W3D_MESH_FLAG_GEOMETRY_TYPE_CAMERA_ORIENTED )
  150. {
  151. if (Is_Physical_Collision(node)) {
  152. attributes |= W3D_MESH_FLAG_COLLISION_TYPE_PHYSICAL;
  153. }
  154. if (Is_Projectile_Collision(node)) {
  155. attributes |= W3D_MESH_FLAG_COLLISION_TYPE_PROJECTILE;
  156. }
  157. if (Is_Vis_Collision(node)) {
  158. attributes |= W3D_MESH_FLAG_COLLISION_TYPE_VIS;
  159. }
  160. if (Is_Camera_Collision(node)) {
  161. attributes |= W3D_MESH_FLAG_COLLISION_TYPE_CAMERA;
  162. }
  163. if (Is_Vehicle_Collision(node)) {
  164. attributes |= W3D_MESH_FLAG_COLLISION_TYPE_VEHICLE;
  165. }
  166. }
  167. /*
  168. ** A mesh may have one of the following bits set as well
  169. */
  170. if (Is_Hidden(node)) {
  171. attributes |= W3D_MESH_FLAG_HIDDEN;
  172. }
  173. if (Is_Two_Sided(node)) {
  174. attributes |= W3D_MESH_FLAG_TWO_SIDED;
  175. }
  176. if (Is_Shadow(node)) {
  177. attributes |= W3D_MESH_FLAG_CAST_SHADOW;
  178. }
  179. if (Is_Shatterable(node)) {
  180. attributes |= W3D_MESH_FLAG_SHATTERABLE;
  181. }
  182. if (Is_NPatchable(node)) {
  183. attributes |= W3D_MESH_FLAG_NPATCHABLE;
  184. }
  185. return attributes;
  186. }
  187. /***********************************************************************************************
  188. * MeshSaveClass::MeshSaveClass -- constructor, processes a Max mesh *
  189. * *
  190. * This class takes a MAX mesh and computes the information for a W3D mesh or skin. *
  191. * *
  192. * INPUT: *
  193. * *
  194. * inode - the max INode containing the mesh/skin to export *
  195. * exportspace - matrix defining the desired coordinate system for the mesh *
  196. * htree - hierarchy tree that this mesh is being connected to *
  197. * curtime - current time in Max. *
  198. * meter - progress meter *
  199. * mesh_name - name to use for the mesh *
  200. * container_name - name of the container *
  201. * *
  202. * OUTPUT: *
  203. * *
  204. * WARNINGS: *
  205. * *
  206. * HISTORY: *
  207. * 06/10/1997 GH : Created. *
  208. *=============================================================================================*/
  209. MeshSaveClass::MeshSaveClass
  210. (
  211. char * mesh_name,
  212. char * container_name,
  213. INode * inode,
  214. const Mesh * input_mesh,
  215. Matrix3 & exportspace,
  216. W3DAppData2Struct & exportoptions,
  217. HierarchySaveClass * htree,
  218. TimeValue curtime,
  219. Progress_Meter_Class & meter,
  220. unsigned int * materialColors,
  221. int &numMaterialColors,
  222. int &numHouseColors,
  223. char *materialColorTexture,
  224. WorldInfoClass * world_info
  225. ) :
  226. MaxINode(inode),
  227. ExportOptions(exportoptions),
  228. CurTime(curtime),
  229. ExportSpace(exportspace),
  230. HTree(htree),
  231. UserText(NULL),
  232. VertInfluences(NULL),
  233. MaterialRemapTable(NULL)
  234. {
  235. Mesh mesh = *input_mesh; // copy the mesh so we can modify it
  236. Mtl * nodemtl = inode->GetMtl();
  237. DWORD wirecolor = inode->GetWireColor();
  238. PS2Material = FALSE;
  239. // Check to see if the mesh uses PS2 game materials. If so, set a flag so
  240. // that write_shaders will know to make a PS2 shader chunk.
  241. if (nodemtl) {
  242. if (nodemtl->ClassID() == PS2GameMaterialClassID) {
  243. PS2Material = TRUE;
  244. } else if (nodemtl->IsMultiMtl()) {
  245. for (int i = 0; i < nodemtl->NumSubMtls(); i++) {
  246. Mtl *sub = nodemtl->GetSubMtl(i);
  247. if (sub->ClassID() == PS2GameMaterialClassID) {
  248. PS2Material = TRUE;
  249. }
  250. }
  251. }
  252. }
  253. //////////////////////////////////////////////////////////////////////
  254. // Check if the mesh is being inverted by its transform. If this
  255. // is the case, then we will need to reverse the winding of all
  256. // polygons later.
  257. //////////////////////////////////////////////////////////////////////
  258. Matrix3 objtm = MaxINode->GetObjectTM(curtime);
  259. MeshInverted = (Compute_3x3_Determinant(objtm) < 0.0f);
  260. //////////////////////////////////////////////////////////////////////
  261. // Prep the mesh by transforming it by the delta between exportspace
  262. // and this INodes current space
  263. // (this is the delta between the bone and the mesh if one exists...)
  264. //////////////////////////////////////////////////////////////////////
  265. MeshToExportSpace = objtm * Inverse(ExportSpace);
  266. prep_mesh(mesh,MeshToExportSpace);
  267. //////////////////////////////////////////////////////////////////////
  268. // Prepare the mesh header.
  269. //////////////////////////////////////////////////////////////////////
  270. assert(mesh_name != NULL);
  271. assert(container_name != NULL);
  272. memset(&Header,0,sizeof(Header));
  273. Set_W3D_Name(Header.MeshName,mesh_name);
  274. Set_W3D_Name(Header.ContainerName,container_name);
  275. Header.Version = W3D_CURRENT_MESH_VERSION;
  276. Header.Attributes = setup_mesh_attributes(MaxINode);
  277. meter.Finish_In_Steps(
  278. 3*Header.NumTris + // normals
  279. Header.NumVertices + // surrender normals
  280. 64 // voxelization
  281. );
  282. ExportLog::printf("\nProcessing Mesh: %s\n",Header.MeshName);
  283. //////////////////////////////////////////////////////////////////////
  284. // Enforce that we have enough data to actually make a mesh
  285. //////////////////////////////////////////////////////////////////////
  286. if (mesh.getNumFaces() <= 0) {
  287. throw ErrorClass("No Triangles in Mesh: %s",Header.MeshName);
  288. }
  289. if (mesh.getNumVerts() <= 0) {
  290. throw ErrorClass("No Vertices in Mesh: %s",Header.MeshName);
  291. }
  292. //////////////////////////////////////////////////////////////////////
  293. // process the materials
  294. //////////////////////////////////////////////////////////////////////
  295. DebugPrint("processing materials\n");
  296. scan_used_materials(mesh,nodemtl);
  297. create_materials(nodemtl,wirecolor,materialColorTexture);
  298. //////////////////////////////////////////////////////////////////////
  299. // what face and vertex attributes are we going to export?
  300. //////////////////////////////////////////////////////////////////////
  301. Header.FaceChannels = W3D_FACE_CHANNEL_FACE;
  302. Header.VertexChannels = W3D_VERTEX_CHANNEL_LOCATION;
  303. if (!use_simple_rendering(Header.Attributes)) {
  304. Header.VertexChannels |= W3D_VERTEX_CHANNEL_NORMAL;
  305. }
  306. if (((Header.Attributes & W3D_MESH_FLAG_GEOMETRY_TYPE_MASK) == W3D_MESH_FLAG_GEOMETRY_TYPE_SKIN) && (HTree != NULL)) {
  307. Header.VertexChannels |= W3D_VERTEX_CHANNEL_BONEID;
  308. }
  309. //////////////////////////////////////////////////////////////////////
  310. // Process the mesh
  311. //////////////////////////////////////////////////////////////////////
  312. Builder.Set_World_Info (world_info);
  313. Build_Mesh(mesh, nodemtl, materialColors, numMaterialColors, numHouseColors);
  314. if (materialColorTexture)
  315. { //diffuse color materials are replaced by textures
  316. //set diffuse to 255,255,255 so it has no effect.
  317. fix_diffuse_materials(numHouseColors != 0);
  318. }
  319. //////////////////////////////////////////////////////////////////////
  320. // Create damage (deform) information for the mesh
  321. //////////////////////////////////////////////////////////////////////
  322. Object *ref_obj = MaxINode->GetObjectRef ();
  323. DeformSave.Initialize(Builder, ref_obj, mesh, &MeshToExportSpace);
  324. //////////////////////////////////////////////////////////////////////
  325. // Determine if the deformer should use alpha or v-color info
  326. //////////////////////////////////////////////////////////////////////
  327. if (ExportOptions.Is_Vertex_Alpha_Enabled()) {
  328. unsigned int alpha_passes = 0;
  329. for (int pass=0; pass < MaterialDesc.Pass_Count(); pass++) {
  330. if (MaterialDesc.Pass_Uses_Vertex_Alpha(pass)) {
  331. alpha_passes |= (1 << pass);
  332. }
  333. }
  334. DeformSave.Set_Alpha_Passes(alpha_passes);
  335. }
  336. //////////////////////////////////////////////////////////////////////
  337. // Set the counts in the mesh header
  338. //////////////////////////////////////////////////////////////////////
  339. Header.NumTris = Builder.Get_Face_Count();
  340. Header.NumVertices = Builder.Get_Vertex_Count();
  341. //////////////////////////////////////////////////////////////////////
  342. // Compute the mesh's bounding box and sphere. This must be done
  343. // before we pre-deform the mesh (if its a skin).
  344. //////////////////////////////////////////////////////////////////////
  345. compute_bounding_volumes();
  346. //////////////////////////////////////////////////////////////////////
  347. // Voxelize the mesh and compute the Moment of Inertia and
  348. // Center of Mass. This must come after we compute the bounding
  349. // volumes and before we pre-deform the mesh.
  350. //////////////////////////////////////////////////////////////////////
  351. Progress_Meter_Class voxelmeter(meter, 64.0f * meter.Increment);
  352. compute_physical_constants(MaxINode,voxelmeter,false /*usevoxelizer*/);
  353. //////////////////////////////////////////////////////////////////////
  354. // If this is a skin, pre-deform the mesh.
  355. //////////////////////////////////////////////////////////////////////
  356. if (((Header.Attributes & W3D_MESH_FLAG_GEOMETRY_TYPE_MASK) == W3D_MESH_FLAG_GEOMETRY_TYPE_SKIN) && (HTree != NULL)) {
  357. inv_deform_mesh();
  358. }
  359. //////////////////////////////////////////////////////////////////////
  360. // Get the user text from MAX's properties window.
  361. //////////////////////////////////////////////////////////////////////
  362. TSTR usertext;
  363. MaxINode->GetUserPropBuffer(usertext);
  364. CStr usertext8 = usertext;
  365. if (usertext8.Length() > 0) {
  366. UserText = new char[usertext8.Length() + 1];
  367. memset(UserText,0,usertext8.Length() + 1);
  368. memcpy(UserText,usertext8.data(),usertext8.Length());
  369. }
  370. }
  371. /***********************************************************************************************
  372. * MeshSaveClass::~MeshSaveClass -- destructor, frees all allocated memory *
  373. * *
  374. * INPUT: *
  375. * *
  376. * OUTPUT: *
  377. * *
  378. * WARNINGS: *
  379. * *
  380. * HISTORY: *
  381. * 06/10/1997 GH : Created. *
  382. *=============================================================================================*/
  383. MeshSaveClass::~MeshSaveClass(void)
  384. {
  385. if (UserText) {
  386. delete[] UserText;
  387. UserText = NULL;
  388. }
  389. if (VertInfluences) {
  390. delete[] VertInfluences;
  391. VertInfluences = NULL;
  392. }
  393. if (MaterialRemapTable) {
  394. delete[] MaterialRemapTable;
  395. MaterialRemapTable = NULL;
  396. }
  397. }
  398. //search through previously found material colors and return index. house colors are always placed in top row.
  399. void getMaterialUV(UVVert &tvert,unsigned int diffuse, unsigned int *materialColors, int &numMaterialColors, int &numHouseColors, bool house)
  400. {
  401. int i;
  402. if (house)
  403. { //this material is a house color, place it in first row.
  404. for (i=0; i<16; i++)
  405. {
  406. if (materialColors[i]==diffuse)
  407. {
  408. tvert.x=((double)(i%16)+0.5)/16.0; ///@todo: MW: Remove hard-coded texture size
  409. tvert.y=1.0-((double)(i/16)+0.5)/16.0;
  410. numHouseColors=16;
  411. return;
  412. }
  413. }
  414. ExportLog::printf("\nUndefined House Color %d,%d,%d",(diffuse>>16)&0xff,(diffuse>>8)&0xff,diffuse&0xff);
  415. assert(0); //all house colors must be from a predefined range of reds
  416. }
  417. for (i=16; i<(16+numMaterialColors); i++)
  418. {
  419. if (materialColors[i]==diffuse)
  420. {
  421. tvert.x=((double)(i%16)+0.5)/16.0; ///@todo: MW: Remove hard-coded texture size
  422. tvert.y=1.0-((double)(i/16)+0.5)/16.0;
  423. return;
  424. }
  425. }
  426. //new color found
  427. tvert.x=((double)(i%16)+0.5)/16.0; ///@todo: MW: Remove hard-coded texture size
  428. tvert.y=1.0-((double)(i/16)+0.5)/16.0;
  429. materialColors[i]=diffuse;
  430. numMaterialColors++;
  431. }
  432. void MeshSaveClass::Build_Mesh(Mesh & mesh, Mtl *node_mtl, unsigned int *materialColors, int &numMaterialColors, int &numHouseColors)
  433. {
  434. int vert_counter;
  435. int face_index;
  436. int pass;
  437. int stage;
  438. float *vdata = NULL;
  439. int firstSolidColoredMaterial=-1;
  440. Builder.Reset(true,mesh.getNumFaces(),mesh.getNumFaces()/3);
  441. // Get a pointer to the channel that has alpha values entered by the artist.
  442. // This pointer will be NULL if they didn't use the channel.
  443. vdata = mesh.vertexFloat(ALPHA_VERTEX_CHANNEL);
  444. /*
  445. ** Get the skin info
  446. */
  447. bool is_skin = false;
  448. SkinDataClass * skindata = NULL;
  449. SkinWSMObjectClass * skinobj = NULL;
  450. get_skin_modifier_objects(&skindata,&skinobj);
  451. if ( ((Header.Attributes & W3D_MESH_FLAG_GEOMETRY_TYPE_MASK) == W3D_MESH_FLAG_GEOMETRY_TYPE_SKIN) &&
  452. (HTree != NULL) )
  453. {
  454. is_skin = ((skindata != NULL) && (skinobj != NULL));
  455. }
  456. /*
  457. ** Submit all of the faces
  458. */
  459. MeshBuilderClass::FaceClass face;
  460. for (face_index = 0; face_index < mesh.getNumFaces(); face_index++) {
  461. Face maxface = mesh.faces[face_index];
  462. int mat_index = 0;
  463. if (Header.NumMaterials > 0) {
  464. mat_index = MaterialRemapTable[(maxface.getMatID() % Header.NumMaterials)];
  465. }
  466. assert(mat_index != -1);
  467. for (pass=0; pass<MaterialDesc.Pass_Count(); pass++) {
  468. face.ShaderIndex[pass] = MaterialDesc.Get_Shader_Index(mat_index,pass);
  469. for (stage=0; stage<W3dMaterialClass::MAX_STAGES; stage++) {
  470. face.TextureIndex[pass][stage] = MaterialDesc.Get_Texture_Index(mat_index,pass,stage);
  471. }
  472. }
  473. int geo_type = Header.Attributes & W3D_MESH_FLAG_GEOMETRY_TYPE_MASK;
  474. if ( use_simple_rendering(geo_type) ) {
  475. face.SmGroup = 0xFFFFFFFF;
  476. } else {
  477. face.SmGroup = maxface.getSmGroup();
  478. }
  479. face.Index = face_index;
  480. face.Attributes = 0;
  481. face.SurfaceType = 0;
  482. /*
  483. ** Lookup this face's surface type
  484. */
  485. Mtl *mtl_to_use = node_mtl;
  486. if ((node_mtl != NULL) && (node_mtl->NumSubMtls() > 1)) {
  487. mtl_to_use = node_mtl->GetSubMtl (maxface.getMatID() % node_mtl->NumSubMtls());
  488. }
  489. if ((mtl_to_use != NULL) && ((mtl_to_use->ClassID() == GameMaterialClassID) ||
  490. (mtl_to_use->ClassID() == PS2GameMaterialClassID))) {
  491. face.SurfaceType = ((GameMtl *)mtl_to_use)->Get_Surface_Type ();
  492. }
  493. for (vert_counter = 0; vert_counter < 3; vert_counter++) {
  494. /*
  495. ** if this mesh is being inverted, we need to insert the verts in
  496. ** the opposite order. max_vert_counter will count backwards
  497. ** in this case; causing all vertex data to be entered in the
  498. ** reverse winding.
  499. */
  500. int max_vert_counter;
  501. if (MeshInverted) {
  502. max_vert_counter = 2 - vert_counter;
  503. } else {
  504. max_vert_counter = vert_counter;
  505. }
  506. int max_vert_index = maxface.v[max_vert_counter];
  507. /*
  508. ** Vertex Id, to prevent unwanted welding!
  509. */
  510. face.Verts[vert_counter].Id = max_vert_index;
  511. /*
  512. ** Vertex Position
  513. */
  514. face.Verts[vert_counter].Position.X = mesh.verts[max_vert_index].x;
  515. face.Verts[vert_counter].Position.Y = mesh.verts[max_vert_index].y;
  516. face.Verts[vert_counter].Position.Z = mesh.verts[max_vert_index].z;
  517. if (vdata) {
  518. // If an alpha channel has been created, use its value.
  519. for (int pass=0; pass < MaterialDesc.Pass_Count(); pass++) {
  520. if (MaterialDesc.Pass_Uses_Vertex_Alpha(pass)) {
  521. // Mulitiply by .01 to change from percentage.
  522. face.Verts[vert_counter].Alpha[pass] = vdata[max_vert_index] * .01;
  523. }
  524. }
  525. }
  526. /*
  527. ** Texture coordinate. Apply uv coords if the mesh has them and is not being
  528. ** instructed to ignore them.
  529. ** - check if the mesh needs them (uses_simple_rendering() == false)
  530. ** - for each pass and stage, look up what map channel this face's material is using
  531. ** - ask Max for the tfaces and tverts for that map channel
  532. ** - copy the values into each vertex
  533. */
  534. for (pass=0; pass<MaterialDesc.Pass_Count(); pass++) {
  535. for (stage=0; stage<W3dMaterialClass::MAX_STAGES; stage++) {
  536. /*
  537. ** Start with a 0,0 uv coordinate
  538. */
  539. UVVert tvert(0.0,0.0,0.0);
  540. W3dVertexMaterialStruct *vmat=MaterialDesc.Get_Vertex_Material(mat_index, pass);
  541. //Check if this material needs material color texture substitution.
  542. W3dMapClass *map3d=MaterialDesc.Get_Texture(mat_index,pass,stage);
  543. if (map3d && map3d->Filename && *((unsigned int *)map3d->Filename) == DIFFUSE_COLOR_TEXTURE_PREFIX) //check for prefix
  544. {
  545. double Diffuse = vmat->Diffuse.Get_Color() >> 8; //get material color
  546. //MW: Encode the material color into the u texture coordinate
  547. tvert.x=Diffuse;
  548. tvert.y=Diffuse;
  549. //find out material color location within texture page
  550. if (strnicmp(MaterialDesc.Get_Vertex_Material_Name(mat_index,pass),"HouseColor",10)==0)
  551. getMaterialUV(tvert,vmat->Diffuse.Get_Color() >> 8, materialColors, numMaterialColors, numHouseColors, true);
  552. else
  553. getMaterialUV(tvert,vmat->Diffuse.Get_Color() >> 8, materialColors, numMaterialColors, numHouseColors, false);
  554. //Keep track of first vertex material converted, so we can remap all other non-textured
  555. //materials to use the same material.
  556. if (firstSolidColoredMaterial == -1)
  557. firstSolidColoredMaterial=MaterialDesc.Get_Vertex_Material_Index(mat_index,pass);
  558. }
  559. /*
  560. ** If the mesh needs uv coords and they are present, copy the uv into tvert
  561. */
  562. else
  563. if (!use_simple_rendering(Header.Attributes)) {
  564. int channel = MaterialDesc.Get_Map_Channel(mat_index,pass,stage);
  565. UVVert * uvarray = mesh.mapVerts(channel);
  566. TVFace * tvfacearray = mesh.mapFaces(channel);
  567. ///@todo: MW: Forced ingoring of uv coordinates if no texture! Is this ok?
  568. W3dMapClass *map3d=MaterialDesc.Get_Texture(mat_index,pass,stage);
  569. if (map3d && (uvarray != NULL) && (tvfacearray != NULL)) {
  570. int tvert_index = tvfacearray[face_index].t[max_vert_counter];
  571. tvert = uvarray[tvert_index];
  572. }
  573. }
  574. /*
  575. ** Copy the texture coordinate into the vertex structure
  576. */
  577. face.Verts[vert_counter].TexCoord[pass][stage].X = tvert.x;
  578. face.Verts[vert_counter].TexCoord[pass][stage].Y = tvert.y;
  579. }
  580. }
  581. /*
  582. ** Vertex Color
  583. */
  584. if (mesh.vcFace) {
  585. /*
  586. ** If the mesh is being mirrored, remap the index
  587. */
  588. int max_cvert_index = mesh.vcFace[face_index].t[max_vert_counter];
  589. VertColor vc;
  590. vc = mesh.vertCol[max_cvert_index];
  591. /*
  592. ** If Vertex Alpha is specified, the vertex color is converted
  593. ** to alpha. If the (obsolete) Node-flag for vertex alpha is enabled,
  594. ** the alpha is put into each pass which has alpha enabled. Otherwise,
  595. ** we check the material settings for which passes should get the alpha
  596. ** values. If neither alpha options are enabled, we put the color
  597. ** value into the first pass.
  598. */
  599. if (ExportOptions.Is_Vertex_Alpha_Enabled()) {
  600. float alpha = (vc.x + vc.y + vc.z) / 3.0f;
  601. for (int pass=0; pass < MaterialDesc.Pass_Count(); pass++) {
  602. if (MaterialDesc.Pass_Uses_Vertex_Alpha(pass)) {
  603. face.Verts[vert_counter].Alpha[pass] = alpha;
  604. }
  605. }
  606. } else {
  607. face.Verts[vert_counter].DiffuseColor[0].X = vc.x;
  608. face.Verts[vert_counter].DiffuseColor[0].Y = vc.y;
  609. face.Verts[vert_counter].DiffuseColor[0].Z = vc.z;
  610. }
  611. face.Verts[vert_counter].MaxVertColIndex = max_cvert_index;
  612. } else {
  613. face.Verts[vert_counter].MaxVertColIndex = 0;
  614. }
  615. /*
  616. ** Vertex materials (get's index of sub-material)
  617. */
  618. for (pass = 0; pass<MaterialDesc.Pass_Count(); pass++) {
  619. //if solid-color texture substitution, replace the vertex material with first solid one found.
  620. W3dMapClass *map3d=MaterialDesc.Get_Texture(mat_index,pass,0);
  621. if (map3d && map3d->Filename && *((unsigned int *)map3d->Filename) == DIFFUSE_COLOR_TEXTURE_PREFIX) //check for prefix
  622. face.Verts[vert_counter].VertexMaterialIndex[pass]=firstSolidColoredMaterial;
  623. else
  624. face.Verts[vert_counter].VertexMaterialIndex[pass] = MaterialDesc.Get_Vertex_Material_Index(mat_index,pass);
  625. }
  626. face.Verts[vert_counter].Attribute0 = max_vert_index;
  627. face.Verts[vert_counter].Attribute1 = face_index;
  628. /*
  629. ** Skin attachment
  630. */
  631. face.Verts[vert_counter].BoneIndex = 0;
  632. if (is_skin) {
  633. int skin_bone_index = skindata->VertData[max_vert_index].BoneIdx[0];
  634. // If this is a valid bone, try to find the corresponding bone index in the HTree
  635. if ( (skin_bone_index != -1) &&
  636. (skin_bone_index < skinobj->Num_Bones()) &&
  637. (skinobj->BoneTab[skin_bone_index] != NULL) )
  638. {
  639. face.Verts[vert_counter].BoneIndex = get_htree_bone_index_for_inode(skinobj->BoneTab[skin_bone_index]);
  640. }
  641. }
  642. }
  643. Builder.Add_Face(face);
  644. }
  645. /*
  646. ** Process the mesh
  647. */
  648. Builder.Build_Mesh(true);
  649. const MeshBuilderClass::MeshStatsStruct & stats = Builder.Get_Mesh_Stats();
  650. int vcount = Builder.Get_Vertex_Count();
  651. int pcount = mesh.numFaces;
  652. float vert_poly_ratio = (float)vcount / (float)pcount;
  653. ExportLog::printf(" triangle count: %d\n",pcount);
  654. ExportLog::printf(" final vertex count: %d\n",vcount);
  655. ExportLog::printf(" vertex/triangle ratio: %f\n",vert_poly_ratio);
  656. ExportLog::printf(" strip count: %d\n",stats.StripCount);
  657. ExportLog::printf(" average strip length: %f\n",stats.AvgStripLength);
  658. ExportLog::printf(" longest strip: %d\n",stats.MaxStripLength);
  659. }
  660. /***********************************************************************************************
  661. * MeshSaveClass::get_skin_modifier_objects -- Searches for the WWSkin modifier for this mesh *
  662. * *
  663. * INPUT: *
  664. * *
  665. * OUTPUT: *
  666. * *
  667. * WARNINGS: *
  668. * *
  669. * HISTORY: *
  670. *=============================================================================================*/
  671. void MeshSaveClass::get_skin_modifier_objects(SkinDataClass ** skin_data_ptr,SkinWSMObjectClass ** skin_obj_ptr)
  672. {
  673. *skin_data_ptr = NULL;
  674. *skin_obj_ptr = NULL;
  675. // loop through the references that our node has
  676. for (int i = 0; i < MaxINode->NumRefs(); i++) {
  677. ReferenceTarget *refTarg = MaxINode->GetReference(i);
  678. // if the reference is a WSM Derived Object.
  679. if (refTarg != NULL && refTarg->ClassID() == Class_ID(WSM_DERIVOB_CLASS_ID,0)) {
  680. IDerivedObject * wsm_der_obj = (IDerivedObject *)refTarg;
  681. // loop through the WSM's attached to this WSM Derived object
  682. for (int j = 0; j < wsm_der_obj->NumModifiers(); j++) {
  683. Modifier * mod = wsm_der_obj->GetModifier(j);
  684. if (mod->ClassID() == SKIN_MOD_CLASS_ID) {
  685. // This is our modifier! Get the data from it!
  686. SkinModifierClass * skinmod = (SkinModifierClass *)mod;
  687. ModContext * mc = wsm_der_obj->GetModContext(j);
  688. *skin_data_ptr = (SkinDataClass *)(mc->localData);
  689. *skin_obj_ptr = (SkinWSMObjectClass *)skinmod->GetReference(SkinModifierClass::OBJ_REF);
  690. }
  691. }
  692. }
  693. }
  694. }
  695. /***********************************************************************************************
  696. * MeshSaveClass::get_htree_bone_index_for_inode -- searches the htree for the given INode *
  697. * *
  698. * INPUT: *
  699. * *
  700. * OUTPUT: *
  701. * *
  702. * WARNINGS: *
  703. * *
  704. * HISTORY: *
  705. * 5/1/2000 gth : Created. *
  706. *=============================================================================================*/
  707. int MeshSaveClass::get_htree_bone_index_for_inode(INode * node)
  708. {
  709. // index of this INode in the hierarchy tree (it better be there :-)
  710. char w3dname[W3D_NAME_LEN];
  711. Set_W3D_Name(w3dname,node->GetName());
  712. int bindex = HTree->Find_Named_Node(w3dname);
  713. // If the desired bone isn't being exported, export the point
  714. // relative to the root.
  715. if (bindex == -1) {
  716. bindex = 0;
  717. }
  718. return bindex;
  719. }
  720. /***********************************************************************************************
  721. * MeshSaveClass::inv_deform_mesh -- preprocess the mesh for skinning *
  722. * *
  723. * INPUT: *
  724. * *
  725. * OUTPUT: *
  726. * *
  727. * WARNINGS: *
  728. * *
  729. * HISTORY: *
  730. * 10/26/1997 GH : Created. *
  731. * 5/1/2000 gth : Created. *
  732. *=============================================================================================*/
  733. void MeshSaveClass::inv_deform_mesh()
  734. {
  735. // Got the skinning info, now pre-deform each vertex and
  736. // create an array of vertex influence indexes.
  737. VertInfluences = new W3dVertInfStruct[Builder.Get_Vertex_Count()];
  738. memset(VertInfluences,0,sizeof(W3dVertInfStruct) * Builder.Get_Vertex_Count());
  739. Header.VertexChannels |= W3D_VERTEX_CHANNEL_BONEID;
  740. for (int vert_index = 0; vert_index < Builder.Get_Vertex_Count(); vert_index++) {
  741. MeshBuilderClass::VertClass & vert = Builder.Get_Vertex(vert_index);
  742. if (vert.BoneIndex == 0) {
  743. // set the influence to 0 (root node) and leave the vert and normal unchanged
  744. // (gth) 07/11/2000: Note that in this case, the mesh coordinates have already been
  745. // transformed relative to the user or scene origin and do not need to be further modified.
  746. VertInfluences[vert_index].BoneIdx = 0;
  747. } else {
  748. // here we go! get the matrix from the hierarchy tree and transform
  749. // the point into the bone's coordinate system.
  750. // (gth) 07/11/2000: The origin_offset_tm is no longer needed because
  751. // skin meshes are transformed into the origin coordinate system before
  752. // we get to this code.
  753. Matrix3 bonetm = HTree->Get_Node_Transform(vert.BoneIndex);
  754. Matrix3 invbonetm = Inverse(bonetm);
  755. Point3 pos;
  756. pos.x = vert.Position.X;
  757. pos.y = vert.Position.Y;
  758. pos.z = vert.Position.Z;
  759. pos = pos * invbonetm;
  760. vert.Position.X = pos.x;
  761. vert.Position.Y = pos.y;
  762. vert.Position.Z = pos.z;
  763. // Now, transform the normal into the bone's coordinate system.
  764. invbonetm.NoTrans();
  765. Point3 norm;
  766. norm.x = vert.Normal.X;
  767. norm.y = vert.Normal.Y;
  768. norm.z = vert.Normal.Z;
  769. norm = norm * invbonetm;
  770. vert.Normal.X = norm.x;
  771. vert.Normal.Y = norm.y;
  772. vert.Normal.Z = norm.z;
  773. VertInfluences[vert_index].BoneIdx = vert.BoneIndex;
  774. }
  775. }
  776. }
  777. /***********************************************************************************************
  778. * MeshSaveClass::Write_To_File -- Append the mesh to an open wtm file *
  779. * *
  780. * INPUT: *
  781. * csave - ChunkSaveClass object to handle writing the chunk-based file *
  782. * export_aabtree - should we generate an aabtree for this mesh *
  783. * *
  784. * OUTPUT: *
  785. * 0 if nothing went wrong, Non-Zero otherwise *
  786. * *
  787. * WARNINGS: *
  788. * *
  789. * *
  790. * HISTORY: *
  791. * 06/10/1997 GH : Created. *
  792. *=============================================================================================*/
  793. int MeshSaveClass::Write_To_File(ChunkSaveClass & csave,bool export_aabtree)
  794. {
  795. if (!csave.Begin_Chunk(W3D_CHUNK_MESH)) {
  796. return 1;
  797. }
  798. if (write_header(csave) != 0) {
  799. return 1;
  800. }
  801. if (write_user_text(csave) != 0) {
  802. return 1;
  803. }
  804. if (write_verts(csave) != 0) {
  805. return 1;
  806. }
  807. if (write_vert_normals(csave) != 0) {
  808. return 1;
  809. }
  810. if (write_triangles(csave) != 0) {
  811. return 1;
  812. }
  813. if (write_vert_influences(csave) != 0) {
  814. return 1;
  815. }
  816. if (write_vert_shade_indices(csave) != 0) {
  817. return 1;
  818. }
  819. if (write_material_info(csave) != 0) {
  820. return 1;
  821. }
  822. if (write_vertex_materials(csave) != 0) {
  823. return 1;
  824. }
  825. if (PS2Material == TRUE) {
  826. // The ps2 shaders must be written out first.
  827. if (write_ps2_shaders(csave) != 0) {
  828. return 1;
  829. }
  830. }
  831. if (write_shaders(csave) != 0) {
  832. return 1;
  833. }
  834. if (write_textures(csave) != 0) {
  835. return 1;
  836. }
  837. for (int pass=0; pass<MaterialDesc.Pass_Count(); pass++) {
  838. if (write_pass(csave,pass) != 0) {
  839. return 1;
  840. }
  841. }
  842. if (DeformSave.Export (csave) != true) {
  843. return 1;
  844. }
  845. if (export_aabtree == true) {
  846. if (write_aabtree(csave) != 0) {
  847. return 1;
  848. }
  849. }
  850. if (!csave.End_Chunk()) {
  851. return 1;
  852. }
  853. return 0;
  854. }
  855. /***********************************************************************************************
  856. * MeshSaveClass::write_header -- write a mesh header chunk into a wtm file *
  857. * *
  858. * INPUT: *
  859. * csave - chunk save object *
  860. * *
  861. * OUTPUT: *
  862. * 0 if nothing went wrong, Non-Zero otherwise *
  863. * *
  864. * WARNINGS: *
  865. * *
  866. * HISTORY: *
  867. * 06/10/1997 GH : Created. *
  868. *=============================================================================================*/
  869. int MeshSaveClass::write_header(ChunkSaveClass & csave)
  870. {
  871. if (!csave.Begin_Chunk(W3D_CHUNK_MESH_HEADER3)) {
  872. return 1;
  873. }
  874. if (csave.Write(&Header,sizeof(W3dMeshHeader3Struct)) != sizeof(W3dMeshHeader3Struct)) {
  875. return 1;
  876. }
  877. if (!csave.End_Chunk()) {
  878. return 1;
  879. }
  880. return 0;
  881. }
  882. /***********************************************************************************************
  883. * MeshSaveClass::write_user_text -- write the user text chunk *
  884. * *
  885. * INPUT: *
  886. * *
  887. * OUTPUT: *
  888. * *
  889. * WARNINGS: *
  890. * *
  891. * HISTORY: *
  892. * 08/20/1997 GH : Created. *
  893. *=============================================================================================*/
  894. int MeshSaveClass::write_user_text(ChunkSaveClass & csave)
  895. {
  896. // If there's no user text, just don't write the chunk
  897. if (UserText == NULL) {
  898. return 0;
  899. }
  900. if (!csave.Begin_Chunk(W3D_CHUNK_MESH_USER_TEXT)) {
  901. return 1;
  902. }
  903. // write the user text buffer (writing one extra byte to include the NULL)
  904. if (csave.Write(UserText,strlen(UserText) + 1) != strlen(UserText) + 1) {
  905. return 1;
  906. }
  907. if (!csave.End_Chunk()) {
  908. return 1;
  909. }
  910. return 0;
  911. }
  912. /***********************************************************************************************
  913. * MeshSaveClass::write_verts -- write the vertex chunk into a wtm file *
  914. * *
  915. * INPUT: *
  916. * csave - chunk save object *
  917. * *
  918. * OUTPUT: *
  919. * 0 if nothing went wrong, Non-Zero otherwise *
  920. * *
  921. * WARNINGS: *
  922. * *
  923. * HISTORY: *
  924. * 06/10/1997 GH : Created. *
  925. *=============================================================================================*/
  926. int MeshSaveClass::write_verts(ChunkSaveClass & csave)
  927. {
  928. if (!csave.Begin_Chunk(W3D_CHUNK_VERTICES)) {
  929. return 1;
  930. }
  931. assert(Builder.Get_Vertex_Count() > 0);
  932. assert(Builder.Get_Vertex_Count() == (int)Header.NumVertices);
  933. for (int i=0; i<Builder.Get_Vertex_Count(); i++) {
  934. const MeshBuilderClass::VertClass & vert = Builder.Get_Vertex(i);
  935. if ((Header.Attributes & W3D_MESH_FLAG_GEOMETRY_TYPE_MASK) != W3D_MESH_FLAG_GEOMETRY_TYPE_SKIN) {
  936. assert(vert.Position.X <= Header.Max.X);
  937. assert(vert.Position.Y <= Header.Max.Y);
  938. assert(vert.Position.Z <= Header.Max.Z);
  939. assert(vert.Position.X >= Header.Min.X);
  940. assert(vert.Position.Y >= Header.Min.Y);
  941. assert(vert.Position.Z >= Header.Min.Z);
  942. }
  943. W3dVectorStruct w3dvert;
  944. w3dvert.X = vert.Position.X;
  945. w3dvert.Y = vert.Position.Y;
  946. w3dvert.Z = vert.Position.Z;
  947. if (csave.Write(&(w3dvert),sizeof(W3dVectorStruct)) != sizeof(W3dVectorStruct)) {
  948. return 1;
  949. }
  950. }
  951. if (!csave.End_Chunk()) {
  952. return 1;
  953. }
  954. return 0;
  955. }
  956. /***********************************************************************************************
  957. * MeshSaveClass::write_vert_normals -- Writes the surrender normal chunk into a wtm file *
  958. * *
  959. * INPUT: *
  960. * csave - chunk save object *
  961. * *
  962. * OUTPUT: *
  963. * 0 if nothing went wrong, Non-Zero otherwise *
  964. * *
  965. * WARNINGS: *
  966. * *
  967. * HISTORY: *
  968. * 06/10/1997 GH : Created. *
  969. *=============================================================================================*/
  970. int MeshSaveClass::write_vert_normals(ChunkSaveClass & csave)
  971. {
  972. if (!(Header.VertexChannels & W3D_VERTEX_CHANNEL_NORMAL)) return 0;
  973. if (!csave.Begin_Chunk(W3D_CHUNK_VERTEX_NORMALS)) {
  974. return 1;
  975. }
  976. for (int i=0; i < Builder.Get_Vertex_Count(); i++) {
  977. const MeshBuilderClass::VertClass & vert = Builder.Get_Vertex(i);
  978. W3dVectorStruct norm;
  979. if (ExportOptions.Is_ZNormals_Enabled()) {
  980. norm.X = 0.0f;
  981. norm.Y = 0.0f;
  982. norm.Z = 1.0f;
  983. } else {
  984. norm.X = vert.Normal.X;
  985. norm.Y = vert.Normal.Y;
  986. norm.Z = vert.Normal.Z;
  987. }
  988. if (csave.Write(&(norm),sizeof(W3dVectorStruct)) != sizeof(W3dVectorStruct)) {
  989. return 1;
  990. }
  991. }
  992. if (!csave.End_Chunk()) {
  993. return 1;
  994. }
  995. return 0;
  996. }
  997. /***********************************************************************************************
  998. * MeshSaveClass::write_vert_influences -- skins will have this chunk that binds verts to bones*
  999. * *
  1000. * INPUT: *
  1001. * csave - chunk save object *
  1002. * *
  1003. * OUTPUT: *
  1004. * 0 if nothing went wrong, Non-Zero otherwise *
  1005. * *
  1006. * WARNINGS: *
  1007. * *
  1008. * HISTORY: *
  1009. * 06/10/1997 GH : Created. *
  1010. *=============================================================================================*/
  1011. int MeshSaveClass::write_vert_influences(ChunkSaveClass & csave)
  1012. {
  1013. if (((Header.Attributes & W3D_MESH_FLAG_GEOMETRY_TYPE_MASK) != W3D_MESH_FLAG_GEOMETRY_TYPE_SKIN) ||
  1014. !(Header.VertexChannels & W3D_VERTEX_CHANNEL_BONEID) ||
  1015. (VertInfluences == NULL)) {
  1016. return 0;
  1017. }
  1018. if (!csave.Begin_Chunk(W3D_CHUNK_VERTEX_INFLUENCES)) {
  1019. return 1;
  1020. }
  1021. int count = Builder.Get_Vertex_Count();
  1022. if (csave.Write(VertInfluences,count * sizeof(W3dVertInfStruct)) != count * sizeof(W3dVertInfStruct)) {
  1023. return 1;
  1024. }
  1025. if (!csave.End_Chunk()) {
  1026. return 1;
  1027. }
  1028. return 0;
  1029. }
  1030. /***********************************************************************************************
  1031. * MeshSaveClass::write_triangles -- write the triangles chunk *
  1032. * *
  1033. * INPUT: *
  1034. * *
  1035. * OUTPUT: *
  1036. * *
  1037. * WARNINGS: *
  1038. * *
  1039. * HISTORY: *
  1040. * 4/7/98 GTH : Created. *
  1041. *=============================================================================================*/
  1042. int MeshSaveClass::write_triangles(ChunkSaveClass & csave)
  1043. {
  1044. if (!csave.Begin_Chunk(W3D_CHUNK_TRIANGLES)) {
  1045. return 1;
  1046. }
  1047. assert(Builder.Get_Face_Count() == (int)Header.NumTris);
  1048. for (int i=0; i<Builder.Get_Face_Count(); i++) {
  1049. const MeshBuilderClass::FaceClass & face = Builder.Get_Face(i);
  1050. W3dTriStruct tri;
  1051. memset(&tri,0,sizeof(W3dTriStruct));
  1052. // convert each triangle into surrender format
  1053. tri.Vindex[0] = face.VertIdx[0];
  1054. tri.Vindex[1] = face.VertIdx[1];
  1055. tri.Vindex[2] = face.VertIdx[2];
  1056. tri.Attributes = face.SurfaceType;
  1057. tri.Normal.X = face.Normal.X;
  1058. tri.Normal.Y = face.Normal.Y;
  1059. tri.Normal.Z = face.Normal.Z;
  1060. tri.Dist = face.Dist;
  1061. // write each triangle
  1062. if (csave.Write(&tri,sizeof(W3dTriStruct)) != sizeof(W3dTriStruct)) {
  1063. return 1;
  1064. }
  1065. }
  1066. if (!csave.End_Chunk()) {
  1067. return 1;
  1068. }
  1069. return 0;
  1070. }
  1071. int MeshSaveClass::write_vert_shade_indices(ChunkSaveClass & csave)
  1072. {
  1073. if (!(Header.VertexChannels & W3D_VERTEX_CHANNEL_NORMAL)) return 0;
  1074. if (!csave.Begin_Chunk(W3D_CHUNK_VERTEX_SHADE_INDICES)) {
  1075. return 1;
  1076. }
  1077. for (int i=0; i < Builder.Get_Vertex_Count(); i++) {
  1078. const MeshBuilderClass::VertClass & vert = Builder.Get_Vertex(i);
  1079. uint32 shade_index = vert.ShadeIndex;
  1080. if (csave.Write(&(shade_index),sizeof(shade_index)) != sizeof(shade_index)) {
  1081. return 1;
  1082. }
  1083. }
  1084. if (!csave.End_Chunk()) {
  1085. return 1;
  1086. }
  1087. return 0;
  1088. }
  1089. int MeshSaveClass::write_material_info(ChunkSaveClass & csave)
  1090. {
  1091. if (!csave.Begin_Chunk(W3D_CHUNK_MATERIAL_INFO)) {
  1092. return 1;
  1093. }
  1094. W3dMaterialInfoStruct matinfo;
  1095. matinfo.PassCount = MaterialDesc.Pass_Count();
  1096. matinfo.VertexMaterialCount = MaterialDesc.Vertex_Material_Count();
  1097. matinfo.ShaderCount = MaterialDesc.Shader_Count();
  1098. matinfo.TextureCount = MaterialDesc.Texture_Count();
  1099. if (csave.Write(&matinfo,sizeof(matinfo)) != sizeof(matinfo)) {
  1100. return 1;
  1101. }
  1102. if (!csave.End_Chunk()) {
  1103. return 1;
  1104. }
  1105. return 0;
  1106. }
  1107. int MeshSaveClass::write_vertex_materials(ChunkSaveClass & csave)
  1108. {
  1109. if (MaterialDesc.Vertex_Material_Count() <= 0) return 0;
  1110. // open a chunk which wraps all of the vertex materials
  1111. if (!csave.Begin_Chunk(W3D_CHUNK_VERTEX_MATERIALS)) {
  1112. return 1;
  1113. }
  1114. for (int i=0; i<MaterialDesc.Vertex_Material_Count(); i++) {
  1115. csave.Begin_Chunk(W3D_CHUNK_VERTEX_MATERIAL);
  1116. // write the filename
  1117. const char * name = MaterialDesc.Get_Vertex_Material_Name(i);
  1118. if (name != NULL) {
  1119. csave.Begin_Chunk(W3D_CHUNK_VERTEX_MATERIAL_NAME);
  1120. if (csave.Write(name,strlen(name) + 1) != strlen(name) + 1) {
  1121. return 1;
  1122. }
  1123. csave.End_Chunk();
  1124. }
  1125. // write the struct
  1126. W3dVertexMaterialStruct * vmat = MaterialDesc.Get_Vertex_Material(i);
  1127. csave.Begin_Chunk(W3D_CHUNK_VERTEX_MATERIAL_INFO);
  1128. if (csave.Write(vmat,sizeof(W3dVertexMaterialStruct)) != sizeof(W3dVertexMaterialStruct)) {
  1129. return 1;
  1130. }
  1131. csave.End_Chunk();
  1132. // write the mapper args
  1133. const char * args = MaterialDesc.Get_Mapper_Args(i, 0);
  1134. if (args != NULL) {
  1135. csave.Begin_Chunk(W3D_CHUNK_VERTEX_MAPPER_ARGS0);
  1136. if (csave.Write(args,strlen(args) + 1) != strlen(args) + 1) {
  1137. return 1;
  1138. }
  1139. csave.End_Chunk();
  1140. }
  1141. args = MaterialDesc.Get_Mapper_Args(i, 1);
  1142. if (args != NULL) {
  1143. csave.Begin_Chunk(W3D_CHUNK_VERTEX_MAPPER_ARGS1);
  1144. if (csave.Write(args,strlen(args) + 1) != strlen(args) + 1) {
  1145. return 1;
  1146. }
  1147. csave.End_Chunk();
  1148. }
  1149. csave.End_Chunk();
  1150. }
  1151. if (!csave.End_Chunk()) {
  1152. return 1;
  1153. }
  1154. return 0;
  1155. }
  1156. int MeshSaveClass::write_shaders(ChunkSaveClass & csave)
  1157. {
  1158. assert(MaterialDesc.Shader_Count() > 0);
  1159. if (PS2Material == TRUE) {
  1160. // Make the PC shader as close to the PS2 shader as possible.
  1161. // This will allow for viewing in W3D View.
  1162. setup_PC_shaders_from_PS2_shaders();
  1163. }
  1164. if (!csave.Begin_Chunk(W3D_CHUNK_SHADERS)) {
  1165. return 1;
  1166. }
  1167. W3dShaderStruct * shader;
  1168. for (int i=0; i<MaterialDesc.Shader_Count(); i++) {
  1169. shader = MaterialDesc.Get_Shader(i);
  1170. if (csave.Write(shader,sizeof(W3dShaderStruct)) != sizeof(W3dShaderStruct)) {
  1171. return 1;
  1172. }
  1173. }
  1174. if (!csave.End_Chunk()) {
  1175. return 1;
  1176. }
  1177. return 0;
  1178. }
  1179. /***********************************************************************************************
  1180. * MeshSaveClass::write_ps2_shaders -- Write shaders specific to the PS2 in their own chunk. *
  1181. * *
  1182. * *
  1183. * *
  1184. * *
  1185. * HISTORY: *
  1186. * 10/20/1999MLL: Created. *
  1187. *=============================================================================================*/
  1188. int MeshSaveClass::write_ps2_shaders(ChunkSaveClass & csave)
  1189. {
  1190. if (!csave.Begin_Chunk(W3D_CHUNK_PS2_SHADERS)) {
  1191. return 1;
  1192. }
  1193. W3dShaderStruct *shader;
  1194. W3dPS2ShaderStruct ps2shader;
  1195. for (int i=0; i<MaterialDesc.Shader_Count(); i++) {
  1196. shader = MaterialDesc.Get_Shader(i);
  1197. ps2shader.AlphaTest = W3d_Shader_Get_Alpha_Test(shader);
  1198. ps2shader.AParam = W3d_Shader_Get_PS2_Param_A(shader);
  1199. ps2shader.BParam = W3d_Shader_Get_PS2_Param_B(shader);
  1200. ps2shader.CParam = W3d_Shader_Get_PS2_Param_C(shader);
  1201. ps2shader.DParam = W3d_Shader_Get_PS2_Param_D(shader);
  1202. // Write out the PS2 native form.
  1203. switch (W3d_Shader_Get_Depth_Compare(shader))
  1204. {
  1205. case PSS_DEPTHCOMPARE_PASS_NEVER:
  1206. ps2shader.DepthCompare = 0;
  1207. break;
  1208. case PSS_DEPTHCOMPARE_PASS_LESS:
  1209. ps2shader.DepthCompare = 3;
  1210. break;
  1211. case PSS_DEPTHCOMPARE_PASS_ALWAYS:
  1212. ps2shader.DepthCompare = 1;
  1213. break;
  1214. case PSS_DEPTHCOMPARE_PASS_LEQUAL:
  1215. ps2shader.DepthCompare = 2;
  1216. break;
  1217. }
  1218. ps2shader.DepthMask = !W3d_Shader_Get_Depth_Mask(shader);
  1219. switch (W3d_Shader_Get_Pri_Gradient(shader))
  1220. {
  1221. case PSS_PRIGRADIENT_MODULATE:
  1222. ps2shader.PriGradient = PSS_PS2_PRIGRADIENT_MODULATE;
  1223. break;
  1224. case PSS_PRIGRADIENT_HIGHLIGHT:
  1225. ps2shader.PriGradient = PSS_PS2_PRIGRADIENT_HIGHLIGHT;
  1226. break;
  1227. case PSS_PRIGRADIENT_HIGHLIGHT2:
  1228. ps2shader.PriGradient = PSS_PS2_PRIGRADIENT_HIGHLIGHT2;
  1229. break;
  1230. case PSS_PRIGRADIENT_DECAL:
  1231. ps2shader.PriGradient = PSS_PS2_PRIGRADIENT_DECAL;
  1232. break;
  1233. }
  1234. ps2shader.Texturing = W3d_Shader_Get_Texturing(shader);
  1235. if (csave.Write(&ps2shader,sizeof(W3dPS2ShaderStruct)) != sizeof(W3dPS2ShaderStruct)) {
  1236. return 1;
  1237. }
  1238. }
  1239. if (!csave.End_Chunk()) {
  1240. return 1;
  1241. }
  1242. return (0);
  1243. }
  1244. void MeshSaveClass::setup_PC_shaders_from_PS2_shaders()
  1245. {
  1246. W3dShaderStruct *shader;
  1247. for (int i=0; i<MaterialDesc.Shader_Count(); i++) {
  1248. shader = MaterialDesc.Get_Shader(i);
  1249. // DepthCompare on the PS2 doesn't have as many options as on the PC.
  1250. switch (W3d_Shader_Get_Depth_Compare(shader))
  1251. {
  1252. case PSS_DEPTHCOMPARE_PASS_NEVER:
  1253. W3d_Shader_Set_Depth_Compare(shader, W3DSHADER_DEPTHCOMPARE_PASS_NEVER);
  1254. break;
  1255. case PSS_DEPTHCOMPARE_PASS_LESS:
  1256. W3d_Shader_Set_Depth_Compare(shader, W3DSHADER_DEPTHCOMPARE_PASS_LESS);
  1257. break;
  1258. case PSS_DEPTHCOMPARE_PASS_LEQUAL:
  1259. W3d_Shader_Set_Depth_Compare(shader, W3DSHADER_DEPTHCOMPARE_PASS_LEQUAL);
  1260. break;
  1261. case PSS_DEPTHCOMPARE_PASS_ALWAYS:
  1262. W3d_Shader_Set_Depth_Compare(shader, W3DSHADER_DEPTHCOMPARE_PASS_ALWAYS);
  1263. break;
  1264. }
  1265. // The PS2 has more gradient functions than the PC.
  1266. switch (W3d_Shader_Get_Pri_Gradient(shader))
  1267. {
  1268. case PSS_PRIGRADIENT_MODULATE:
  1269. W3d_Shader_Set_Pri_Gradient(shader, W3DSHADER_PRIGRADIENT_MODULATE);
  1270. break;
  1271. // No PC equivalent. Set to default: modulate.
  1272. case PSS_PRIGRADIENT_HIGHLIGHT:
  1273. case PSS_PRIGRADIENT_HIGHLIGHT2:
  1274. case PSS_PRIGRADIENT_DECAL:
  1275. W3d_Shader_Set_Pri_Gradient(shader, W3DSHADER_PRIGRADIENT_MODULATE);
  1276. break;
  1277. }
  1278. }
  1279. }
  1280. int MeshSaveClass::write_textures(ChunkSaveClass & csave)
  1281. {
  1282. if (MaterialDesc.Texture_Count() <= 0) return 0;
  1283. // open a chunk which wraps all textures
  1284. if (!csave.Begin_Chunk(W3D_CHUNK_TEXTURES)) {
  1285. return 1;
  1286. }
  1287. for (int i=0; i<MaterialDesc.Texture_Count(); i++) {
  1288. W3dMapClass * map = MaterialDesc.Get_Texture(i);
  1289. csave.Begin_Chunk(W3D_CHUNK_TEXTURE);
  1290. // write the filename
  1291. csave.Begin_Chunk(W3D_CHUNK_TEXTURE_NAME);
  1292. if (csave.Write(map->Filename,strlen(map->Filename) + 1) != strlen(map->Filename) + 1) return 1;
  1293. csave.End_Chunk();
  1294. // optionally write an animation info chunk
  1295. if (map->AnimInfo != NULL) {
  1296. csave.Begin_Chunk(W3D_CHUNK_TEXTURE_INFO);
  1297. if (csave.Write(map->AnimInfo,sizeof(W3dTextureInfoStruct)) != sizeof(W3dTextureInfoStruct)) return 1;
  1298. csave.End_Chunk();
  1299. }
  1300. csave.End_Chunk();
  1301. }
  1302. if (!csave.End_Chunk()) {
  1303. return 1;
  1304. }
  1305. return 0;
  1306. }
  1307. int MeshSaveClass::write_pass(ChunkSaveClass & csave,int pass)
  1308. {
  1309. assert(pass >= 0);
  1310. assert(pass < MaterialDesc.Pass_Count());
  1311. if (!csave.Begin_Chunk(W3D_CHUNK_MATERIAL_PASS)) {
  1312. return 1;
  1313. }
  1314. const MeshBuilderClass::MeshStatsStruct & stats = Builder.Get_Mesh_Stats();
  1315. if (stats.HasVertexMaterial[pass]) {
  1316. write_vertex_material_ids(csave,pass);
  1317. }
  1318. if (stats.HasShader[pass]) {
  1319. write_shader_ids(csave,pass);
  1320. }
  1321. if (stats.HasDiffuseColor[pass] || DeformSave.Does_Deformer_Modify_DCG ()) {
  1322. write_dcg(csave,pass);
  1323. }
  1324. for (int stage = 0; stage < MeshBuilderClass::MAX_STAGES; stage++) {
  1325. write_texture_stage(csave,pass,stage);
  1326. }
  1327. if (!csave.End_Chunk()) {
  1328. return 1;
  1329. }
  1330. return 0;
  1331. }
  1332. int MeshSaveClass::write_vertex_material_ids(ChunkSaveClass & csave,int pass)
  1333. {
  1334. const MeshBuilderClass::MeshStatsStruct & stats = Builder.Get_Mesh_Stats();
  1335. if (!csave.Begin_Chunk(W3D_CHUNK_VERTEX_MATERIAL_IDS)) {
  1336. return 1;
  1337. }
  1338. uint32 matid;
  1339. if (stats.HasPerVertexMaterial[pass]) {
  1340. for (int i=0; i<Builder.Get_Vertex_Count(); i++) {
  1341. matid = Builder.Get_Vertex(i).VertexMaterialIndex[pass];
  1342. if (csave.Write(&matid,sizeof(uint32)) != sizeof(uint32)) return 1;
  1343. }
  1344. } else {
  1345. matid = Builder.Get_Vertex(0).VertexMaterialIndex[pass];
  1346. if (csave.Write(&matid,sizeof(uint32)) != sizeof(uint32)) return 1;
  1347. }
  1348. if (!csave.End_Chunk()) {
  1349. return 1;
  1350. }
  1351. return 0;
  1352. }
  1353. int MeshSaveClass::write_shader_ids(ChunkSaveClass & csave,int pass)
  1354. {
  1355. const MeshBuilderClass::MeshStatsStruct & stats = Builder.Get_Mesh_Stats();
  1356. if (!csave.Begin_Chunk(W3D_CHUNK_SHADER_IDS)) {
  1357. return 1;
  1358. }
  1359. uint32 shaderid;
  1360. if (stats.HasPerPolyShader[pass]) {
  1361. for (int i=0; i<Builder.Get_Face_Count(); i++) {
  1362. shaderid = Builder.Get_Face(i).ShaderIndex[pass];
  1363. if (csave.Write(&shaderid,sizeof(uint32)) != sizeof(uint32)) return 1;
  1364. }
  1365. } else {
  1366. shaderid = Builder.Get_Face(0).ShaderIndex[pass];
  1367. if (csave.Write(&shaderid,sizeof(uint32)) != sizeof(uint32)) return 1;
  1368. }
  1369. if (!csave.End_Chunk()) {
  1370. return 1;
  1371. }
  1372. return 0;
  1373. }
  1374. int MeshSaveClass::write_dcg(ChunkSaveClass & csave,int pass)
  1375. {
  1376. const MeshBuilderClass::MeshStatsStruct & stats = Builder.Get_Mesh_Stats();
  1377. if (!csave.Begin_Chunk(W3D_CHUNK_DCG)) {
  1378. return 1;
  1379. }
  1380. for (int i=0; i<Builder.Get_Vertex_Count(); i++) {
  1381. Vector3 vcolor = Builder.Get_Vertex(i).DiffuseColor[pass];
  1382. W3dRGBAStruct color;
  1383. color.R = (uint8)(255.0f * vcolor.X);
  1384. color.G = (uint8)(255.0f * vcolor.Y);
  1385. color.B = (uint8)(255.0f * vcolor.Z);
  1386. float a = Builder.Get_Vertex(i).Alpha[pass];
  1387. color.A = (uint8)(255.0f * a);
  1388. if (csave.Write(&(color),sizeof(W3dRGBAStruct)) != sizeof(W3dRGBAStruct)) {
  1389. return 1;
  1390. }
  1391. }
  1392. if (!csave.End_Chunk()) {
  1393. return 1;
  1394. }
  1395. return 0;
  1396. }
  1397. int MeshSaveClass::write_texture_stage(ChunkSaveClass & csave,int pass,int stage)
  1398. {
  1399. const MeshBuilderClass::MeshStatsStruct & stats = Builder.Get_Mesh_Stats();
  1400. if (!stats.HasTexture[pass][stage]) return 0;
  1401. if (!csave.Begin_Chunk(W3D_CHUNK_TEXTURE_STAGE)) {
  1402. return 1;
  1403. }
  1404. write_texture_ids(csave,pass,stage);
  1405. write_texture_coords(csave,pass,stage);
  1406. if (!csave.End_Chunk()) {
  1407. return 1;
  1408. }
  1409. return 0;
  1410. }
  1411. int MeshSaveClass::write_texture_ids(ChunkSaveClass & csave,int pass,int stage)
  1412. {
  1413. const MeshBuilderClass::MeshStatsStruct & stats = Builder.Get_Mesh_Stats();
  1414. if (!csave.Begin_Chunk(W3D_CHUNK_TEXTURE_IDS)) {
  1415. return 1;
  1416. }
  1417. uint32 texid;
  1418. if (stats.HasPerPolyTexture[pass][stage]) {
  1419. for (int i=0; i<Builder.Get_Face_Count(); i++) {
  1420. texid = Builder.Get_Face(i).TextureIndex[pass][stage];
  1421. if (csave.Write(&texid,sizeof(uint32)) != sizeof(uint32)) return 1;
  1422. }
  1423. } else {
  1424. texid = Builder.Get_Face(0).TextureIndex[pass][stage];
  1425. if (csave.Write(&texid,sizeof(uint32)) != sizeof(uint32)) return 1;
  1426. }
  1427. if (!csave.End_Chunk()) {
  1428. return 1;
  1429. }
  1430. return 0;
  1431. }
  1432. int MeshSaveClass::write_texture_coords(ChunkSaveClass & csave,int pass,int stage)
  1433. {
  1434. const MeshBuilderClass::MeshStatsStruct & stats = Builder.Get_Mesh_Stats();
  1435. if (stats.HasTexCoords[pass][stage] == false) return 0;
  1436. if (!csave.Begin_Chunk(W3D_CHUNK_STAGE_TEXCOORDS)) {
  1437. return 1;
  1438. }
  1439. for (int i=0; i<Builder.Get_Vertex_Count(); i++) {
  1440. W3dTexCoordStruct tex;
  1441. tex.U = Builder.Get_Vertex(i).TexCoord[pass][stage].X;
  1442. tex.V = Builder.Get_Vertex(i).TexCoord[pass][stage].Y;
  1443. if (csave.Write(&(tex),sizeof(W3dTexCoordStruct)) != sizeof(W3dTexCoordStruct)) {
  1444. return 1;
  1445. }
  1446. }
  1447. if (!csave.End_Chunk()) {
  1448. return 1;
  1449. }
  1450. return 0;
  1451. }
  1452. int MeshSaveClass::write_aabtree(ChunkSaveClass & csave)
  1453. {
  1454. /*
  1455. ** Don't bother with an AABTree if this not a normal mesh or if it has very few polygons
  1456. */
  1457. if ( (Builder.Get_Face_Count() >= MIN_AABTREE_POLYGONS) &&
  1458. ((Header.Attributes & W3D_MESH_FLAG_GEOMETRY_TYPE_MASK) == W3D_MESH_FLAG_GEOMETRY_TYPE_NORMAL)
  1459. )
  1460. {
  1461. /*
  1462. ** Build temporary array representations of the mesh
  1463. */
  1464. int vertcount = Builder.Get_Vertex_Count();
  1465. int polycount = Builder.Get_Face_Count();
  1466. Vector3 * verts = new Vector3[vertcount];
  1467. Vector3i * polys = new Vector3i[polycount];
  1468. for (int vi=0; vi<vertcount; vi++) {
  1469. verts[vi] = Builder.Get_Vertex(vi).Position;
  1470. }
  1471. for (int pi=0; pi<polycount; pi++) {
  1472. polys[pi].I = Builder.Get_Face(pi).VertIdx[0];
  1473. polys[pi].J = Builder.Get_Face(pi).VertIdx[1];
  1474. polys[pi].K = Builder.Get_Face(pi).VertIdx[2];
  1475. }
  1476. /*
  1477. ** Build the AABTree and save it
  1478. */
  1479. AABTreeBuilderClass aabtree_builder;
  1480. aabtree_builder.Build_AABTree(polycount,polys,vertcount,verts);
  1481. aabtree_builder.Export(csave);
  1482. /*
  1483. ** Release the allocated resources
  1484. */
  1485. delete[] verts;
  1486. delete[] polys;
  1487. }
  1488. return 0;
  1489. }
  1490. int MeshSaveClass::scan_used_materials(Mesh & mesh,Mtl * nodemtl)
  1491. {
  1492. int face_index;
  1493. int mat_index;
  1494. if ((nodemtl == NULL) || (nodemtl->NumSubMtls() <= 1)) {
  1495. MaterialRemapTable = new int[1];
  1496. MaterialRemapTable[0] = 0;
  1497. return 1;
  1498. } else {
  1499. int sub_mtl_count = nodemtl->NumSubMtls();
  1500. MaterialRemapTable = new int[sub_mtl_count];
  1501. // Initialize each remap to -1 (indicates that the material is un-used)
  1502. for (mat_index=0; mat_index<sub_mtl_count; mat_index++) {
  1503. MaterialRemapTable[mat_index] = -1;
  1504. }
  1505. // Set a 1 for each material actually referenced by the mesh
  1506. for (face_index=0; face_index<mesh.getNumFaces(); face_index++) {
  1507. int max_mat_index = mesh.faces[face_index].getMatID();
  1508. int mat_index = (max_mat_index % sub_mtl_count);
  1509. MaterialRemapTable[mat_index] = 1;
  1510. }
  1511. // Replace each 1 entry with an incrementing material index
  1512. int matcount = 0;
  1513. for (mat_index=0; mat_index<sub_mtl_count; mat_index++) {
  1514. if (MaterialRemapTable[mat_index] == 1) {
  1515. MaterialRemapTable[mat_index] = matcount;
  1516. matcount++;
  1517. }
  1518. }
  1519. assert(matcount > 0);
  1520. return matcount;
  1521. }
  1522. }
  1523. //Check if material is solid, non-textured.
  1524. int isTexturedMaterial(Mtl * mtl)
  1525. {
  1526. Texmap * tmap;
  1527. tmap = mtl->GetSubTexmap(ID_DI);
  1528. if (mtl->ClassID() == GameMaterialClassID)
  1529. {
  1530. GameMtl * gamemtl=(GameMtl *)mtl;
  1531. for (int pass=0;pass<gamemtl->Get_Pass_Count(); pass++)
  1532. {
  1533. for (int stage=0; stage < W3dMaterialClass::MAX_STAGES; stage++)
  1534. { if (gamemtl->Get_Texture_Enable(pass,stage) && gamemtl->Get_Texture(pass,stage))
  1535. return 1; //using a texture
  1536. }
  1537. }
  1538. return 0;
  1539. }
  1540. else
  1541. {
  1542. return (tmap && tmap->ClassID() == Class_ID(BMTEX_CLASS_ID,0));
  1543. }
  1544. }
  1545. //count number of used solid materials (no texture)
  1546. int MeshSaveClass::getNumSolidMaterials(Mtl * nodemtl)
  1547. {
  1548. int mat_index;
  1549. int numSolid=0;
  1550. if ((nodemtl == NULL) || (nodemtl->NumSubMtls() <= 1))
  1551. { //Check if diffuse texture present
  1552. if (isTexturedMaterial(nodemtl))
  1553. return 00;
  1554. return 1;
  1555. }
  1556. else
  1557. {
  1558. int sub_mtl_count = nodemtl->NumSubMtls();
  1559. for (mat_index=0; mat_index<sub_mtl_count; mat_index++)
  1560. {
  1561. if (MaterialRemapTable[mat_index] != -1)
  1562. { //material is used on some faces
  1563. if (isTexturedMaterial(nodemtl->GetSubMtl(mat_index)))
  1564. continue;
  1565. numSolid++;
  1566. }
  1567. }
  1568. return numSolid;
  1569. }
  1570. }
  1571. //Cancels out the material colors by setting them to white.
  1572. //Also changes prefix on texture names to use DIFFUSE_HOUSECOLOR_TEXTURE_PREFIX
  1573. //if house color is used.
  1574. void MeshSaveClass::fix_diffuse_materials(bool isHouseColor)
  1575. {
  1576. uint8 color=255;
  1577. char materialColorFilename[_MAX_FNAME + 1];
  1578. for (int mat_index=0; mat_index<MaterialDesc.Material_Count(); mat_index++)
  1579. {
  1580. for (int pass=0; pass<MaterialDesc.Pass_Count(); pass++)
  1581. {
  1582. W3dVertexMaterialStruct *vmat=MaterialDesc.Get_Vertex_Material(mat_index,pass);
  1583. for (int stage=0; stage<W3dMaterialClass::MAX_STAGES; stage++)
  1584. {
  1585. W3dMapClass *map3d=MaterialDesc.Get_Texture(mat_index,pass,stage);
  1586. if (map3d && map3d->Filename && (*((unsigned int *)map3d->Filename) & 0xffff00ff) == DIFFUSE_COLOR_TEXTURE_MASK) //check for 'TXC^' prefix
  1587. { //found a material which had its material color replaced by a texture
  1588. //set the material color to white so it's not being used.
  1589. vmat->Diffuse.Set(color,color,color);
  1590. vmat->Ambient.Set(color,color,color);
  1591. if (isHouseColor && *(map3d->Filename+1) != 'H')
  1592. { //our material texture contains house colors, adjust the name
  1593. //by adding H prefix.
  1594. sprintf(materialColorFilename,"%s%s","ZHCD",map3d->Filename+4);
  1595. map3d->Set_Filename(materialColorFilename);
  1596. }
  1597. }
  1598. }
  1599. }
  1600. }
  1601. }
  1602. /***********************************************************************************************
  1603. * MeshSaveClass::create_materials -- create the materials for this mesh *
  1604. * *
  1605. * INPUT: *
  1606. * *
  1607. * OUTPUT: *
  1608. * *
  1609. * WARNINGS: *
  1610. * *
  1611. * HISTORY: *
  1612. * 10/26/1997 GH : Created. *
  1613. * 2/8/99 GTH : modified to use the MaterialRemapTable *
  1614. *=============================================================================================*/
  1615. void MeshSaveClass::create_materials(Mtl * nodemtl,DWORD wirecolor, char *materialColorTexture)
  1616. {
  1617. bool domaps = !use_simple_rendering(Header.Attributes);
  1618. //////////////////////////////////////////////////////////////////////
  1619. // Create materials
  1620. // Four cases:
  1621. // - Creating a collision object: use a hard-coded material
  1622. // - The material is null: use wire color
  1623. // - The material is a simple material: create one material
  1624. // - The material is a multi-material: create n materials
  1625. //////////////////////////////////////////////////////////////////////
  1626. int geo_type = Header.Attributes & W3D_MESH_FLAG_GEOMETRY_TYPE_MASK;
  1627. if ((geo_type == W3D_MESH_FLAG_GEOMETRY_TYPE_AABOX) ||
  1628. (geo_type == W3D_MESH_FLAG_GEOMETRY_TYPE_OBBOX))
  1629. {
  1630. Header.NumMaterials = 1;
  1631. W3dVertexMaterialStruct vmat;
  1632. W3dShaderStruct shader;
  1633. W3dMaterialClass material;
  1634. Color diffuse;
  1635. if (Header.Attributes & W3D_MESH_FLAG_COLLISION_TYPE_PHYSICAL) {
  1636. diffuse.r = 0.0f;
  1637. diffuse.g = 0.0f;
  1638. diffuse.b = 1.0f;
  1639. } else {
  1640. diffuse.r = 0.4f;
  1641. diffuse.g = 1.0f;
  1642. diffuse.b = 0.4f;
  1643. }
  1644. W3d_Shader_Reset(&shader);
  1645. W3d_Shader_Set_Dest_Blend_Func(&shader,W3DSHADER_DESTBLENDFUNC_ONE_MINUS_SRC_ALPHA);
  1646. W3d_Shader_Set_Src_Blend_Func(&shader,W3DSHADER_SRCBLENDFUNC_SRC_ALPHA);
  1647. W3d_Vertex_Material_Reset(&vmat);
  1648. vmat.Opacity = 0.5f;
  1649. // add material 0
  1650. vmat.Diffuse.R = (uint8)(diffuse.r * 255.0f);
  1651. vmat.Diffuse.G = (uint8)(diffuse.g * 255.0f);
  1652. vmat.Diffuse.B = (uint8)(diffuse.b * 255.0f);
  1653. material.Set_Pass_Count(1);
  1654. material.Set_Vertex_Material(vmat,0);
  1655. material.Set_Shader(shader,0);
  1656. MaterialDesc.Add_Material(material);
  1657. assert(MaterialDesc.Material_Count() == 1);
  1658. } else if (!nodemtl) {
  1659. // Create a single material using the wire color
  1660. Header.NumMaterials = 1;
  1661. W3dVertexMaterialStruct vmat;
  1662. W3dShaderStruct shader;
  1663. W3dMaterialClass material;
  1664. W3d_Vertex_Material_Reset(&vmat);
  1665. W3d_Shader_Reset(&shader);
  1666. vmat.Diffuse.R = GetRValue(wirecolor);
  1667. vmat.Diffuse.G = GetGValue(wirecolor);
  1668. vmat.Diffuse.B = GetBValue(wirecolor);
  1669. material.Set_Pass_Count(1);
  1670. material.Set_Vertex_Material(vmat,0);
  1671. material.Set_Shader(shader,0);
  1672. MaterialDesc.Add_Material(material,"WireColor");
  1673. assert(MaterialDesc.Material_Count() == 1);
  1674. } else if (!nodemtl->IsMultiMtl()) {
  1675. Header.NumMaterials = 1;
  1676. W3dMaterialClass mat;
  1677. if (isTexturedMaterial(nodemtl) == 0)
  1678. mat.Init(nodemtl,materialColorTexture);
  1679. else
  1680. mat.Init(nodemtl,NULL);
  1681. W3dMaterialDescClass::ErrorType err;
  1682. err = MaterialDesc.Add_Material(mat,nodemtl->GetName());
  1683. if (err == W3dMaterialDescClass::MULTIPASS_TRANSPARENT) {
  1684. sprintf(_string1,"Exporting Materials for Mesh: %s\nMaterial %s is multi-pass and transparent\nMulti-pass transparent materials are not allowed.\n",
  1685. Header.MeshName,
  1686. nodemtl->GetName());
  1687. ExportLog::printf(_string1);
  1688. throw ErrorClass(_string1);
  1689. }
  1690. assert(MaterialDesc.Material_Count() == 1);
  1691. } else {
  1692. Header.NumMaterials = nodemtl->NumSubMtls();
  1693. W3dMaterialClass mat;
  1694. for (unsigned mi = 0; mi < Header.NumMaterials; mi++) {
  1695. // only process materials that were found to be used in the scan_used_materials call
  1696. if (MaterialRemapTable[mi] != -1) {
  1697. if (isTexturedMaterial(nodemtl->GetSubMtl(mi)) == 0)
  1698. mat.Init(nodemtl->GetSubMtl(mi),materialColorTexture);
  1699. else
  1700. mat.Init(nodemtl->GetSubMtl(mi),NULL);
  1701. char * name;
  1702. W3dMaterialDescClass::ErrorType err;
  1703. name = nodemtl->GetSubMtl(mi)->GetName();
  1704. err = MaterialDesc.Add_Material(mat,name);
  1705. if (err == W3dMaterialDescClass::INCONSISTENT_PASSES) {
  1706. sprintf(_string1,"Exporting Materials for Mesh: %s\nMaterial %s has %d passes.\nThe other materials have %d passes.\nAll Materials must have the same number of passes.\n",
  1707. Header.MeshName,
  1708. nodemtl->GetSubMtl(mi)->GetName(),
  1709. mat.Get_Pass_Count(),
  1710. MaterialDesc.Pass_Count());
  1711. ExportLog::printf(_string1);
  1712. throw ErrorClass(_string1);
  1713. }
  1714. if (err == W3dMaterialDescClass::MULTIPASS_TRANSPARENT) {
  1715. sprintf(_string1,"Exporting Materials for Mesh: %s\nMaterial %s is multi-pass and transparent\nMulti-pass transparent materials are not allowed.\n",
  1716. Header.MeshName,
  1717. nodemtl->GetSubMtl(mi)->GetName());
  1718. throw ErrorClass(_string1);
  1719. }
  1720. if (err == W3dMaterialDescClass::INCONSISTENT_SORT_LEVEL) {
  1721. sprintf(_string1,"Exporting Materials for Mesh: %s\nMaterial %s does not have the same Static Sort Level as other materials used on the mesh.\nAll materials for a mesh must use the same Static Sort Level value.\n",
  1722. Header.MeshName,
  1723. nodemtl->GetSubMtl(mi)->GetName());
  1724. ExportLog::printf(_string1);
  1725. throw ErrorClass(_string1);
  1726. }
  1727. }
  1728. }
  1729. }
  1730. // Store the material's sort level in the mesh header.
  1731. Header.SortLevel = MaterialDesc.Get_Sort_Level();
  1732. }
  1733. /***********************************************************************************************
  1734. * MeshSaveClass::compute_bounding_volumes -- computes a bounding box and bounding sphere for *
  1735. * *
  1736. * INPUT: *
  1737. * *
  1738. * OUTPUT: *
  1739. * *
  1740. * WARNINGS: *
  1741. * *
  1742. * HISTORY: *
  1743. * 08/01/1997 GH : Created. *
  1744. *=============================================================================================*/
  1745. void MeshSaveClass::compute_bounding_volumes(void)
  1746. {
  1747. Vector3 min,max,center;
  1748. float radius;
  1749. Builder.Compute_Bounding_Box(&min,&max);
  1750. Builder.Compute_Bounding_Sphere(&center,&radius);
  1751. Header.SphCenter.X = center.X;
  1752. Header.SphCenter.Y = center.Y;
  1753. Header.SphCenter.Z = center.Z;
  1754. Header.SphRadius = radius;
  1755. Header.Min.X = min.X;
  1756. Header.Min.Y = min.Y;
  1757. Header.Min.Z = min.Z;
  1758. Header.Max.X = max.X;
  1759. Header.Max.Y = max.Y;
  1760. Header.Max.Z = max.Z;
  1761. }
  1762. /***********************************************************************************************
  1763. * MeshSaveClass::compute_physical_properties -- computes the volume and moment of inertia *
  1764. * *
  1765. * INPUT: *
  1766. * *
  1767. * OUTPUT: *
  1768. * *
  1769. * WARNINGS: *
  1770. * *
  1771. * HISTORY: *
  1772. * 08/01/1997 GH : Created. *
  1773. *=============================================================================================*/
  1774. void MeshSaveClass::compute_physical_constants
  1775. (
  1776. INode * inode,
  1777. Progress_Meter_Class & meter,
  1778. bool voxelize
  1779. )
  1780. {
  1781. #if 0 // IF we need this again, move the data to a physics chunk (header doesn't have it anymore)
  1782. if (voxelize) {
  1783. // Create an INodeList object for this mesh
  1784. AnyINodeFilter nodefilt;
  1785. INodeListClass meshlist(CurTime,&nodefilt);
  1786. meshlist.Insert(inode);
  1787. // Create a Voxel object for this mesh
  1788. VoxelClass * voxel = new VoxelClass
  1789. (
  1790. meshlist,
  1791. VOXEL_RESOLUTION,
  1792. ExportSpace,
  1793. CurTime,
  1794. meter
  1795. );
  1796. #if DEBUG_VOXELS
  1797. VoxelDebugWindowClass dbgwin(voxel);
  1798. dbgwin.Display_Window();
  1799. #endif
  1800. double vol[1];
  1801. double cm[3];
  1802. double inertia[9];
  1803. voxel->Compute_Physical_Properties(vol,cm,inertia);
  1804. Header.Volume = (float)vol[0];
  1805. Header.MassCenter.X = (float)cm[0];
  1806. Header.MassCenter.Y = (float)cm[1];
  1807. Header.MassCenter.Z = (float)cm[2];
  1808. Header.Inertia[0] = (float)inertia[0];
  1809. Header.Inertia[1] = (float)inertia[1];
  1810. Header.Inertia[2] = (float)inertia[2];
  1811. Header.Inertia[3] = (float)inertia[3];
  1812. Header.Inertia[4] = (float)inertia[4];
  1813. Header.Inertia[5] = (float)inertia[5];
  1814. Header.Inertia[6] = (float)inertia[6];
  1815. Header.Inertia[7] = (float)inertia[7];
  1816. Header.Inertia[8] = (float)inertia[8];
  1817. } else {
  1818. // Set mass center to the center of the bounding box
  1819. Header.MassCenter.X = (Header.Max.X + Header.Min.X) / 2.0f;
  1820. Header.MassCenter.Y = (Header.Max.Y + Header.Min.Y) / 2.0f;
  1821. Header.MassCenter.Z = (Header.Max.Z + Header.Min.Z) / 2.0f;
  1822. // Set inertia tensor to inertia tensor of the bounding box
  1823. // (gth) !!!! DO THIS !!!!
  1824. Header.Inertia[0] = 1.0f;
  1825. Header.Inertia[1] = 0.0f;
  1826. Header.Inertia[2] = 0.0f;
  1827. Header.Inertia[3] = 0.0f;
  1828. Header.Inertia[4] = 1.0f;
  1829. Header.Inertia[5] = 0.0f;
  1830. Header.Inertia[6] = 0.0f;
  1831. Header.Inertia[7] = 0.0f;
  1832. Header.Inertia[8] = 1.0f;
  1833. Header.Volume = (Header.Max.X - Header.Min.X) * (Header.Max.Y - Header.Min.Y) * (Header.Max.Z - Header.Min.Z);
  1834. }
  1835. #endif
  1836. }
  1837. /***********************************************************************************************
  1838. * MeshSaveClass::prep_mesh -- pre-transform the MAX mesh by a specified matrix *
  1839. * *
  1840. * INPUT: *
  1841. * *
  1842. * OUTPUT: *
  1843. * *
  1844. * WARNINGS: *
  1845. * *
  1846. * HISTORY: *
  1847. * 08/01/1997 GH : Created. *
  1848. *=============================================================================================*/
  1849. void MeshSaveClass::prep_mesh(Mesh & mesh,Matrix3 & objoff)
  1850. {
  1851. int vert_index;
  1852. // Transform the mesh's vertices so that they are relative to the coordinate
  1853. // system that we want to use with the mesh
  1854. for (vert_index = 0; vert_index < mesh.getNumVerts (); vert_index++) {
  1855. mesh.verts[vert_index] = mesh.verts[vert_index] * objoff;
  1856. }
  1857. // Re-Build the normals.
  1858. mesh.buildNormals();
  1859. }