renegadeterrainpatch.cpp 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210
  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 : wwphys *
  23. * *
  24. * $Archive:: /Commando/Code/wwphys/renegadeterrainpatch.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 3/12/02 2:33p $*
  29. * *
  30. * $Revision:: 5 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "renegadeterrainpatch.h"
  36. #include "dx8vertexbuffer.h"
  37. #include "dx8indexbuffer.h"
  38. #include "dx8wrapper.h"
  39. #include "sortingrenderer.h"
  40. #include "rinfo.h"
  41. #include "camera.h"
  42. #include "dx8fvf.h"
  43. #include "vector2i.h"
  44. #include "terrainmaterial.h"
  45. #include "renegadeterrainmaterialpass.h"
  46. #include "persistfactory.h"
  47. #include "ww3dids.h"
  48. #include "wwhack.h"
  49. #include "inttest.h"
  50. #include "matpass.h"
  51. ////////////////////////////////////////////////////////////////
  52. // WWHacks
  53. ////////////////////////////////////////////////////////////////
  54. DECLARE_FORCE_LINK(RenegadeTerrainPatch);
  55. ////////////////////////////////////////////////////////////////
  56. // Local constants
  57. ////////////////////////////////////////////////////////////////
  58. enum
  59. {
  60. CHUNKID_VARIABLES = 0x02261040,
  61. CHUNKID_HEIGHTS,
  62. CHUNKID_NORMALS,
  63. CHUNKID_MATERIAL_LAYERS,
  64. CHUNKID_MATERIAL_LAYER,
  65. CHUNKID_QUAD_FLAGS,
  66. CHUNKID_VERTEX_COLORS,
  67. };
  68. enum
  69. {
  70. VARID_GRID_PTS_X = 0x01,
  71. VARID_GRID_PTS_Y,
  72. VARID_GRID_PT_COUNT,
  73. VARID_GRID_BBOX_MIN,
  74. VARID_GRID_BBOX_MAX,
  75. VARID_GRID_HEIGHT,
  76. VARID_GRID_DENSITY,
  77. VARID_MAX_TEXTURE_PASSES,
  78. VARID_IS_PRELIT,
  79. };
  80. //////////////////////////////////////////////////////////////////////
  81. // PersistFactory for RenegadeTerrainPatchClass
  82. //////////////////////////////////////////////////////////////////////
  83. SimplePersistFactoryClass<RenegadeTerrainPatchClass, WW3D_PERSIST_CHUNKID_RENEGADE_TERRAIN> _TerrainPatchFactory;
  84. //////////////////////////////////////////////////////////////////////
  85. //
  86. // RenegadeTerrainPatchClass
  87. //
  88. //////////////////////////////////////////////////////////////////////
  89. RenegadeTerrainPatchClass::RenegadeTerrainPatchClass (void) :
  90. Density (1.0F),
  91. GridPointsX (0),
  92. GridPointsY (0),
  93. GridPointCount (0),
  94. Grid (NULL),
  95. GridNormals (NULL),
  96. VertexColors (NULL),
  97. QuadFlags (NULL),
  98. BaseMaterial (NULL),
  99. LayerMaterial (NULL),
  100. BaseShader (0),
  101. LayerShader (0),
  102. BoundingBoxMin (0, 0, 0),
  103. BoundingBoxMax (0, 0, 0),
  104. AreBuffersDirty (true),
  105. IsPreLit (false)
  106. {
  107. Initialize ();
  108. return ;
  109. }
  110. //////////////////////////////////////////////////////////////////////
  111. //
  112. // RenegadeTerrainPatchClass
  113. //
  114. //////////////////////////////////////////////////////////////////////
  115. RenegadeTerrainPatchClass::RenegadeTerrainPatchClass (const RenegadeTerrainPatchClass &src) :
  116. Density (1.0F),
  117. GridPointsX (0),
  118. GridPointsY (0),
  119. GridPointCount (0),
  120. Grid (NULL),
  121. GridNormals (NULL),
  122. VertexColors (NULL),
  123. QuadFlags (NULL),
  124. BaseMaterial (NULL),
  125. LayerMaterial (NULL),
  126. BaseShader (0),
  127. LayerShader (0),
  128. BoundingBoxMin (0, 0, 0),
  129. BoundingBoxMax (0, 0, 0),
  130. AreBuffersDirty (true),
  131. IsPreLit (false),
  132. RenderObjClass (src)
  133. {
  134. Initialize ();
  135. *this = src;
  136. return ;
  137. }
  138. //////////////////////////////////////////////////////////////////////
  139. //
  140. // ~RenegadeTerrainPatchClass
  141. //
  142. //////////////////////////////////////////////////////////////////////
  143. RenegadeTerrainPatchClass::~RenegadeTerrainPatchClass (void)
  144. {
  145. REF_PTR_RELEASE (BaseMaterial);
  146. REF_PTR_RELEASE (LayerMaterial);
  147. Free_Rendering_Buffers ();
  148. Free_Grid ();
  149. Free_Materials ();
  150. return ;
  151. }
  152. //////////////////////////////////////////////////////////////////////
  153. //
  154. // operator=
  155. //
  156. //////////////////////////////////////////////////////////////////////
  157. const RenegadeTerrainPatchClass &
  158. RenegadeTerrainPatchClass::operator= (const RenegadeTerrainPatchClass &src)
  159. {
  160. return *this;
  161. }
  162. //////////////////////////////////////////////////////////////////////
  163. //
  164. // Initialize
  165. //
  166. //////////////////////////////////////////////////////////////////////
  167. void
  168. RenegadeTerrainPatchClass::Initialize (void)
  169. {
  170. Set_Collision_Type (COLLISION_TYPE_PHYSICAL | COLLISION_TYPE_PROJECTILE | COLLISION_TYPE_CAMERA);
  171. //
  172. // Initialize the material settings
  173. //
  174. Initialize_Material ();
  175. return ;
  176. }
  177. //////////////////////////////////////////////////////////////////////
  178. //
  179. // Allocate
  180. //
  181. //////////////////////////////////////////////////////////////////////
  182. void
  183. RenegadeTerrainPatchClass::Allocate (int points_x, int points_y, float meters_per_point)
  184. {
  185. Free_Grid ();
  186. //
  187. // Calculate how many grid points we will have in the x and y directions
  188. //
  189. Density = meters_per_point;
  190. GridPointsX = points_x;
  191. GridPointsY = points_y;
  192. GridPointsX = max (1, GridPointsX);
  193. GridPointsY = max (1, GridPointsY);
  194. GridPointCount = (GridPointsX * GridPointsY);
  195. BoundingBoxMin.Z = 0.0F;
  196. BoundingBoxMax.Z = 0.0F;
  197. //
  198. // Allocate and initialize the grid and supporting data structures
  199. //
  200. Allocate_Grid ();
  201. Invalidate_Cached_Bounding_Volumes();
  202. return ;
  203. }
  204. //////////////////////////////////////////////////////////////////////
  205. //
  206. // Allocate_Grid
  207. //
  208. //////////////////////////////////////////////////////////////////////
  209. void
  210. RenegadeTerrainPatchClass::Allocate_Grid (void)
  211. {
  212. //
  213. // Allocate the per-vertex arrays
  214. //
  215. GridNormals = new Vector3[GridPointCount];
  216. Grid = new Vector3[GridPointCount];
  217. VertexColors = new Vector3[GridPointCount];
  218. //
  219. // Initialize the vertex color array to white
  220. //
  221. for (int index = 0; index < GridPointCount; index ++) {
  222. VertexColors[index].Set (1.0F, 1.0F, 1.0F);
  223. }
  224. //
  225. // Allocate and initiailze the array of quad flags
  226. //
  227. int quad_count = (GridPointsX - 1) * (GridPointsY - 1);
  228. QuadFlags = new uint8[quad_count];
  229. ::memset (QuadFlags, 0, sizeof (uint8) * quad_count);
  230. AreBuffersDirty = true;
  231. return ;
  232. }
  233. //////////////////////////////////////////////////////////////////////
  234. //
  235. // Free_Grid
  236. //
  237. //////////////////////////////////////////////////////////////////////
  238. void
  239. RenegadeTerrainPatchClass::Free_Grid (void)
  240. {
  241. if (Grid != NULL) {
  242. delete [] Grid;
  243. Grid = NULL;
  244. }
  245. if (GridNormals != NULL) {
  246. delete [] GridNormals;
  247. GridNormals = NULL;
  248. }
  249. if (VertexColors != NULL) {
  250. delete [] VertexColors;
  251. VertexColors = NULL;
  252. }
  253. if (QuadFlags != NULL) {
  254. delete [] QuadFlags;
  255. QuadFlags = NULL;
  256. }
  257. Free_Materials ();
  258. BoundingBoxMin.Z = 0;
  259. BoundingBoxMax.Z = 0;
  260. Density = 1.0F;
  261. GridPointsX = 0;
  262. GridPointsY = 0;
  263. GridPointCount = 0;
  264. return ;
  265. }
  266. //////////////////////////////////////////////////////////////////////
  267. //
  268. // Render
  269. //
  270. //////////////////////////////////////////////////////////////////////
  271. void
  272. RenegadeTerrainPatchClass::Render (RenderInfoClass &rinfo)
  273. {
  274. //
  275. // Make sure our vertex and index buffers are up to date.
  276. //
  277. if (AreBuffersDirty) {
  278. Update_Rendering_Buffers ();
  279. AreBuffersDirty = false;
  280. }
  281. if (IsPreLit) {
  282. BaseMaterial->Set_Ambient_Color_Source (VertexMaterialClass::COLOR1);
  283. BaseMaterial->Set_Diffuse_Color_Source (VertexMaterialClass::COLOR1);
  284. }
  285. //
  286. // Install the mesh'es transform. NOTE, this could go wrong if someone changes the
  287. // transform between the time that the mesh is rendered and the time that the decal
  288. // mesh is rendered... It shouldn't happen though.
  289. //
  290. DX8Wrapper::Set_Transform (D3DTS_WORLD, Get_Transform ());
  291. if (rinfo.light_environment != NULL) {
  292. DX8Wrapper::Set_Light_Environment (rinfo.light_environment);
  293. }
  294. //
  295. // If the object's inherent materials are not disabled, render the terrain
  296. //
  297. if ((rinfo.Current_Override_Flags() & RenderInfoClass::RINFO_OVERRIDE_ADDITIONAL_PASSES_ONLY) == 0)
  298. {
  299. //
  300. // Render the base passes first
  301. //
  302. for (int index = 0; index < MaterialPassList.Count (); index ++) {
  303. Render_By_Texture (index, RenegadeTerrainMaterialPassClass::PASS_BASE);
  304. }
  305. //
  306. // Do a "z-bias" to offset the alpha polys by just a teeny bit. This
  307. // avoids any z-fighting issues with the different passes.
  308. //
  309. //DX8Wrapper::Set_Pseudo_ZBias (1);
  310. //
  311. // Next render the alpha passes
  312. //
  313. for (index = 0; index < MaterialPassList.Count (); index ++) {
  314. Render_By_Texture (index, RenegadeTerrainMaterialPassClass::PASS_ALPHA);
  315. }
  316. }
  317. //
  318. // Render the procedural material passes
  319. //
  320. for (int i=0; i<rinfo.Additional_Pass_Count(); i++) {
  321. MaterialPassClass * matpass = rinfo.Peek_Additional_Pass(i);
  322. Render_Procedural_Material_Pass(matpass);
  323. }
  324. //
  325. // Reset the z-bias
  326. //
  327. //DX8Wrapper::Set_Pseudo_ZBias (0);
  328. //
  329. // Reset the z-bias
  330. //
  331. //DX8Wrapper::Set_DX8_ZBias (0);
  332. return ;
  333. }
  334. //////////////////////////////////////////////////////////////////////
  335. //
  336. // Render_Procedural_Material_Pass
  337. //
  338. //////////////////////////////////////////////////////////////////////
  339. void
  340. RenegadeTerrainPatchClass::Render_Procedural_Material_Pass(MaterialPassClass * matpass)
  341. {
  342. #if 0
  343. if ((pass->Get_Cull_Volume() != NULL) && (MaterialPassClass::Is_Per_Polygon_Culling_Enabled())) {
  344. /*
  345. ** Generate the APT
  346. */
  347. temp_apt.Delete_All(false);
  348. Matrix3D modeltminv;
  349. Get_Transform().Get_Orthogonal_Inverse(modeltminv);
  350. OBBoxClass localbox;
  351. OBBoxClass::Transform(modeltminv,*(pass->Get_Cull_Volume()),&localbox);
  352. Vector3 view_dir;
  353. localbox.Basis.Get_Z_Vector(&view_dir);
  354. view_dir = -view_dir;
  355. if (Model->Has_Cull_Tree()) {
  356. Model->Generate_Rigid_APT(localbox,view_dir,temp_apt);
  357. } else {
  358. Model->Generate_Rigid_APT(view_dir,temp_apt);
  359. }
  360. if (temp_apt.Count() > 0) {
  361. int buftype = BUFFER_TYPE_DYNAMIC_DX8;
  362. if (Model->Get_Flag(MeshGeometryClass::SORT) && WW3D::Is_Sorting_Enabled()) {
  363. buftype = BUFFER_TYPE_DYNAMIC_SORTING;
  364. }
  365. /*
  366. ** Spew triangles in the APT into the dynamic index buffer
  367. */
  368. int min_v = Model->Get_Vertex_Count();
  369. int max_v = 0;
  370. DynamicIBAccessClass dynamic_ib(buftype,temp_apt.Count() * 3);
  371. {
  372. DynamicIBAccessClass::WriteLockClass lock(&dynamic_ib);
  373. unsigned short * indices = lock.Get_Index_Array();
  374. const TriIndex * polys = Model->Get_Polygon_Array();
  375. for (int i=0; i < temp_apt.Count(); i++)
  376. {
  377. unsigned v0 = polys[temp_apt[i]].I;
  378. unsigned v1 = polys[temp_apt[i]].J;
  379. unsigned v2 = polys[temp_apt[i]].K;
  380. indices[i*3 + 0] = (unsigned short)v0;
  381. indices[i*3 + 1] = (unsigned short)v1;
  382. indices[i*3 + 2] = (unsigned short)v2;
  383. min_v = WWMath::Min(v0,min_v);
  384. min_v = WWMath::Min(v1,min_v);
  385. min_v = WWMath::Min(v2,min_v);
  386. max_v = WWMath::Max(v0,max_v);
  387. max_v = WWMath::Max(v1,max_v);
  388. max_v = WWMath::Max(v2,max_v);
  389. }
  390. }
  391. /*
  392. ** Render
  393. */
  394. int vertex_offset = PolygonRendererList.Peek_Head()->Get_Vertex_Offset();
  395. pass->Install_Materials();
  396. DX8Wrapper::Set_Transform(D3DTS_WORLD,Get_Transform());
  397. DX8Wrapper::Set_Index_Buffer(dynamic_ib,vertex_offset);
  398. DX8Wrapper::Draw_Triangles(
  399. 0,
  400. temp_apt.Count(),
  401. min_v,
  402. max_v-min_v+1);
  403. }
  404. } else {
  405. #endif
  406. /*
  407. ** Normal mesh case, render polys with this mesh's transform
  408. */
  409. matpass->Install_Materials();
  410. //
  411. // Render the base passes first
  412. //
  413. for (int index = 0; index < MaterialPassList.Count (); index ++) {
  414. Submit_Rendering_Buffers (index, RenegadeTerrainMaterialPassClass::PASS_BASE);
  415. //
  416. // Alias some data
  417. //
  418. DynamicVectorClass<int> &vert_list = MaterialPassList[index]->VertexRenderList[RenegadeTerrainMaterialPassClass::PASS_BASE];
  419. DynamicVectorClass<int> &quad_list = MaterialPassList[index]->QuadList[RenegadeTerrainMaterialPassClass::PASS_BASE];
  420. //
  421. // Determine how many polygons and verts to render
  422. //
  423. int quad_count = quad_list.Count ();
  424. int vert_count = vert_list.Count ();
  425. int poly_count = quad_count * 2;
  426. //
  427. // Draw the mesh!
  428. //
  429. DX8Wrapper::Draw_Triangles (BUFFER_TYPE_DYNAMIC_DX8, 0, poly_count, 0, vert_count);
  430. }
  431. // }
  432. }
  433. //////////////////////////////////////////////////////////////////////
  434. //
  435. // Submit_Rendering_Buffers
  436. //
  437. //////////////////////////////////////////////////////////////////////
  438. void
  439. RenegadeTerrainPatchClass::Submit_Rendering_Buffers (int texture_index, int pass_type)
  440. {
  441. //
  442. // Don't render this layer if there isn't anything to render!
  443. //
  444. if ( MaterialPassList[texture_index]->VertexBuffers[pass_type] == NULL ||
  445. MaterialPassList[texture_index]->IndexBuffers[pass_type] == NULL)
  446. {
  447. return ;
  448. }
  449. //
  450. // Set vertex and index buffers
  451. //
  452. DX8Wrapper::Set_Vertex_Buffer (MaterialPassList[texture_index]->VertexBuffers[pass_type]);
  453. DX8Wrapper::Set_Index_Buffer (MaterialPassList[texture_index]->IndexBuffers[pass_type], 0);
  454. return ;
  455. }
  456. //////////////////////////////////////////////////////////////////////
  457. //
  458. // Render_By_Texture
  459. //
  460. //////////////////////////////////////////////////////////////////////
  461. void
  462. RenegadeTerrainPatchClass::Render_By_Texture (int texture_index, int pass_type)
  463. {
  464. //
  465. // Don't render this layer if there isn't anything to render!
  466. //
  467. if ( MaterialPassList[texture_index]->VertexBuffers[pass_type] == NULL ||
  468. MaterialPassList[texture_index]->IndexBuffers[pass_type] == NULL)
  469. {
  470. return ;
  471. }
  472. //
  473. // Pass the vertex and index buffers onto DX8
  474. //
  475. Submit_Rendering_Buffers (texture_index, pass_type);
  476. //
  477. // Configure the texture
  478. //
  479. DX8Wrapper::Set_Texture (0, MaterialPassList[texture_index]->Material->Peek_Texture ());
  480. DX8Wrapper::Set_Texture (1, NULL);
  481. //
  482. // Configure the material and shader
  483. //
  484. if (pass_type == RenegadeTerrainMaterialPassClass::PASS_BASE) {
  485. DX8Wrapper::Set_Material (BaseMaterial);
  486. DX8Wrapper::Set_Shader (BaseShader);
  487. } else {
  488. DX8Wrapper::Set_Material (LayerMaterial);
  489. DX8Wrapper::Set_Shader (LayerShader);
  490. }
  491. //
  492. // Alias some data
  493. //
  494. DynamicVectorClass<int> &vert_list = MaterialPassList[texture_index]->VertexRenderList[pass_type];
  495. DynamicVectorClass<int> &quad_list = MaterialPassList[texture_index]->QuadList[pass_type];
  496. //
  497. // Determine how many polygons and verts to render
  498. //
  499. int quad_count = quad_list.Count ();
  500. int vert_count = vert_list.Count ();
  501. int poly_count = quad_count * 2;
  502. //
  503. // Draw the mesh!
  504. //
  505. DX8Wrapper::Draw_Triangles (BUFFER_TYPE_DYNAMIC_DX8, 0, poly_count, 0, vert_count);
  506. return ;
  507. }
  508. //////////////////////////////////////////////////////////////////////
  509. //
  510. // Free_Rendering_Buffers
  511. //
  512. //////////////////////////////////////////////////////////////////////
  513. void
  514. RenegadeTerrainPatchClass::Free_Rendering_Buffers (void)
  515. {
  516. //
  517. // Free each rendering layer
  518. //
  519. for (int index = 0; index < MaterialPassList.Count (); index ++) {
  520. for (int pass = 0; pass < RenegadeTerrainMaterialPassClass::PASS_COUNT; pass ++) {
  521. REF_PTR_RELEASE (MaterialPassList[index]->IndexBuffers[pass]);
  522. REF_PTR_RELEASE (MaterialPassList[index]->VertexBuffers[pass]);
  523. }
  524. }
  525. return ;
  526. }
  527. //////////////////////////////////////////////////////////////////////
  528. //
  529. // Update_Rendering_Buffers
  530. //
  531. //////////////////////////////////////////////////////////////////////
  532. void
  533. RenegadeTerrainPatchClass::Update_Rendering_Buffers (void)
  534. {
  535. Free_Rendering_Buffers ();
  536. //
  537. // Build the rendering buffers for each layer
  538. //
  539. for (int index = 0; index < MaterialPassList.Count (); index ++) {
  540. Build_Rendering_Buffers (index, RenegadeTerrainMaterialPassClass::PASS_BASE);
  541. Build_Rendering_Buffers (index, RenegadeTerrainMaterialPassClass::PASS_ALPHA);
  542. }
  543. return ;
  544. }
  545. //////////////////////////////////////////////////////////////////////
  546. //
  547. // Build_Rendering_Buffers
  548. //
  549. //////////////////////////////////////////////////////////////////////
  550. void
  551. RenegadeTerrainPatchClass::Build_Rendering_Buffers (int texture_index, int pass_type)
  552. {
  553. //
  554. // Alias some data
  555. //
  556. RenegadeTerrainMaterialPassClass *material_pass = MaterialPassList[texture_index];
  557. DynamicVectorClass<int> &vert_list = material_pass->VertexRenderList[pass_type];
  558. DynamicVectorClass<int> &quad_list = material_pass->QuadList[pass_type];
  559. int *vertex_index_map = material_pass->VertexIndexMap[pass_type];
  560. //
  561. // Determine the size of our data
  562. //
  563. int quad_count = quad_list.Count ();
  564. int vert_count = vert_list.Count ();
  565. int poly_count = quad_count * 2;
  566. //
  567. // Don't build the buffers if there's nothing to render in this layer
  568. //
  569. if (poly_count == 0 || vert_count == 0) {
  570. return ;
  571. }
  572. //
  573. // Allocate the vertex and index buffers
  574. //
  575. material_pass->IndexBuffers[pass_type] = new DX8IndexBufferClass (poly_count * 3);
  576. material_pass->VertexBuffers[pass_type] = new DX8VertexBufferClass (DX8_FVF_XYZNDUV1, vert_count);
  577. //
  578. // Write index data to index buffers
  579. //
  580. { // scope for lock
  581. int col_count = (GridPointsX - 1);
  582. //
  583. // Lock the index buffer
  584. //
  585. IndexBufferClass::WriteLockClass lock (material_pass->IndexBuffers[pass_type]);
  586. unsigned short * indices = lock.Get_Index_Array();
  587. //
  588. // Now, compose the triangles by indexing the verts into the vertex buffer
  589. //
  590. int ib_index = 0;
  591. for (int index = 0; index < quad_count; index ++) {
  592. //
  593. // Determine which quad we're rendering
  594. //
  595. int quad_index = quad_list[index];
  596. int quad_y_pos = (quad_index / col_count);
  597. int quad_x_pos = quad_index - (quad_y_pos * col_count);
  598. //
  599. // Determine the "starting" vertex index from the current quad
  600. //
  601. int curr_src_index = (quad_y_pos * GridPointsX) + quad_x_pos;
  602. //
  603. // Calculate the 4 vertex indices that compose this quad
  604. //
  605. int v0_index = curr_src_index;
  606. int v1_index = curr_src_index + 1;
  607. int v2_index = curr_src_index + GridPointsX + 1;
  608. int v3_index = curr_src_index + GridPointsX;
  609. //
  610. // Add the current quad to the index buffer
  611. //
  612. indices[ib_index ++] = (unsigned short)vertex_index_map[v0_index];
  613. indices[ib_index ++] = (unsigned short)vertex_index_map[v2_index];
  614. indices[ib_index ++] = (unsigned short)vertex_index_map[v3_index];
  615. indices[ib_index ++] = (unsigned short)vertex_index_map[v2_index];
  616. indices[ib_index ++] = (unsigned short)vertex_index_map[v0_index];
  617. indices[ib_index ++] = (unsigned short)vertex_index_map[v1_index];
  618. }
  619. } // end scope for lock
  620. const Vector3 white (1.0F, 1.0F, 1.0F);
  621. {
  622. //
  623. // Lock the vertex buffer
  624. //
  625. VertexBufferClass::WriteLockClass lock (material_pass->VertexBuffers[pass_type]);
  626. VertexFormatXYZNDUV1 *vertices = (VertexFormatXYZNDUV1 *)lock.Get_Vertex_Array ();
  627. //
  628. // Specify some default values
  629. //
  630. const static Vector3 default_normal (0.0F, 0.0F, 0.0F);
  631. const static Vector2 default_uv (0.0F, 0.0F);
  632. //
  633. // Write each vertex's definition to the dynamic vertex buffer
  634. //
  635. for (int index = 0; index < vert_count; index ++) {
  636. int vert_index = vert_list[index];
  637. //
  638. // Set the vertex position and normal
  639. //
  640. vertices[index].x = Grid[vert_index].X;
  641. vertices[index].y = Grid[vert_index].Y;
  642. vertices[index].z = Grid[vert_index].Z;
  643. vertices[index].nx = GridNormals[vert_index].X;
  644. vertices[index].ny = GridNormals[vert_index].Y;
  645. vertices[index].nz = GridNormals[vert_index].Z;
  646. //
  647. // Set the UV mapping
  648. //
  649. vertices[index].u1 = material_pass->GridUVs[vert_index].X;
  650. vertices[index].v1 = material_pass->GridUVs[vert_index].Y;
  651. //
  652. // Set the vertex color
  653. //
  654. if (pass_type == RenegadeTerrainMaterialPassClass::PASS_BASE) {
  655. vertices[index].diffuse = DX8Wrapper::Convert_Color (VertexColors[vert_index], 1.0F);
  656. } else {
  657. //
  658. // Compose a vertex color using the vertex alpha
  659. //
  660. float alpha = material_pass->VertexAlpha[vert_index];
  661. vertices[index].diffuse = DX8Wrapper::Convert_Color (VertexColors[vert_index], alpha);
  662. }
  663. }
  664. }
  665. return ;
  666. }
  667. //////////////////////////////////////////////////////////////////////
  668. //
  669. // Initialize_Material
  670. //
  671. //////////////////////////////////////////////////////////////////////
  672. void
  673. RenegadeTerrainPatchClass::Initialize_Material (void)
  674. {
  675. //
  676. // Allocate the vertex material
  677. //
  678. WWASSERT (BaseMaterial == NULL);
  679. BaseMaterial = NEW_REF(VertexMaterialClass, ());
  680. BaseMaterial->Set_Ambient (1.0F, 1.0F, 1.0F);
  681. BaseMaterial->Set_Diffuse (1.0F, 1.0F, 1.0F);
  682. BaseMaterial->Set_Specular (0, 0, 0);
  683. BaseMaterial->Set_Emissive (0.0F, 0.0F, 0.0F);
  684. BaseMaterial->Set_Opacity (1.0F);
  685. BaseMaterial->Set_Shininess (0.0F);
  686. BaseMaterial->Set_Lighting (true);
  687. LayerMaterial = NEW_REF(VertexMaterialClass, ());
  688. LayerMaterial->Set_Ambient (1.0F, 1.0F, 1.0F);
  689. LayerMaterial->Set_Diffuse (1.0F, 1.0F, 1.0F);
  690. LayerMaterial->Set_Specular (0, 0, 0);
  691. LayerMaterial->Set_Emissive (0.0F, 0.0F, 0.0F);
  692. LayerMaterial->Set_Opacity (1.0F);
  693. LayerMaterial->Set_Shininess (0.0F);
  694. LayerMaterial->Set_Lighting (true);
  695. LayerMaterial->Set_Ambient_Color_Source (VertexMaterialClass::COLOR1);
  696. LayerMaterial->Set_Diffuse_Color_Source (VertexMaterialClass::COLOR1);
  697. //
  698. // Initialize the shader
  699. //
  700. BaseShader = ShaderClass::_PresetOpaqueShader;
  701. LayerShader = ShaderClass::_PresetAlphaShader;
  702. BaseShader.Set_Fog_Func(ShaderClass::FOG_ENABLE);
  703. LayerShader.Set_Fog_Func(ShaderClass::FOG_ENABLE);
  704. return ;
  705. }
  706. //////////////////////////////////////////////////////////////////////
  707. //
  708. // Free_Materials
  709. //
  710. //////////////////////////////////////////////////////////////////////
  711. void
  712. RenegadeTerrainPatchClass::Free_Materials (void)
  713. {
  714. //
  715. // Free each material pass
  716. //
  717. for (int index = 0; index < MaterialPassList.Count (); index ++) {
  718. delete MaterialPassList[index];
  719. }
  720. MaterialPassList.Delete_All ();
  721. return ;
  722. }
  723. //////////////////////////////////////////////////////////////////////
  724. //
  725. // Get_Obj_Space_Bounding_Sphere
  726. //
  727. //////////////////////////////////////////////////////////////////////
  728. void
  729. RenegadeTerrainPatchClass::Get_Obj_Space_Bounding_Sphere (SphereClass &sphere) const
  730. {
  731. float delta_x = BoundingBoxMax.X - BoundingBoxMin.X;
  732. float delta_y = BoundingBoxMax.Y - BoundingBoxMin.Y;
  733. float delta_z = BoundingBoxMax.Z - BoundingBoxMin.Z;
  734. sphere.Center.X = BoundingBoxMin.X + (delta_x * 0.5F);
  735. sphere.Center.Y = BoundingBoxMin.Y + (delta_y * 0.5F);
  736. sphere.Center.Z = BoundingBoxMin.Z + (delta_z * 0.5F);
  737. //
  738. // Determine which radius to use as the largest delta
  739. //
  740. sphere.Radius = max (delta_x, delta_y);
  741. sphere.Radius = max (sphere.Radius, delta_z);
  742. sphere.Radius += 1.0F;
  743. //sphere.Radius *= 2.0F;
  744. // sphere.Center.Set (0, 0, 0);
  745. // sphere.Radius = 1000.0F;
  746. return ;
  747. }
  748. //////////////////////////////////////////////////////////////////////
  749. //
  750. // Get_Obj_Space_Bounding_Box
  751. //
  752. //////////////////////////////////////////////////////////////////////
  753. void
  754. RenegadeTerrainPatchClass::Get_Obj_Space_Bounding_Box (AABoxClass &box) const
  755. {
  756. float delta_x = BoundingBoxMax.X - BoundingBoxMin.X;
  757. float delta_y = BoundingBoxMax.Y - BoundingBoxMin.Y;
  758. float delta_z = BoundingBoxMax.Z - BoundingBoxMin.Z;
  759. box.Center.X = BoundingBoxMin.X + (delta_x * 0.5F);
  760. box.Center.Y = BoundingBoxMin.Y + (delta_y * 0.5F);
  761. box.Center.Z = BoundingBoxMin.Z + (delta_z * 0.5F);
  762. //
  763. // Fill in the extents of this box (really they are half-extents)
  764. //
  765. box.Extent.X = (delta_x) + 1.0F;
  766. box.Extent.Y = (delta_y) + 1.0F;
  767. box.Extent.Z = (delta_z) + 1.0F;
  768. // box.Center.Set (0, 0, 0);;
  769. // box.Extent.Set (500.0F, 500.0F, 500.0F);
  770. return ;
  771. }
  772. //////////////////////////////////////////////////////////////////////
  773. //
  774. // Cast_OBBox
  775. //
  776. //////////////////////////////////////////////////////////////////////
  777. bool
  778. RenegadeTerrainPatchClass::Cast_OBBox (OBBoxCollisionTestClass &boxtest)
  779. {
  780. //
  781. // Skip this object if it doesn't match the collision type
  782. //
  783. if ((Get_Collision_Type() & boxtest.CollisionType) == 0) {
  784. return false;
  785. }
  786. if (boxtest.Result->StartBad) {
  787. return false;
  788. }
  789. //
  790. // Get the world to object space transform
  791. //
  792. Matrix3D world_to_obj_tm;
  793. Get_Inverse_Transform (world_to_obj_tm);
  794. //
  795. // Transform the OBBox into heightfield space
  796. //
  797. OBBoxClass obj_space_box;
  798. OBBoxClass::Transform (world_to_obj_tm, boxtest.Box, &obj_space_box);
  799. //
  800. // Transform the movement vector into heightfield space
  801. //
  802. Vector3 obj_space_move = world_to_obj_tm * boxtest.Move;
  803. //
  804. // Create a new coliision box test object in heightfield space
  805. //
  806. OBBoxCollisionTestClass obj_space_test (obj_space_box, obj_space_move,
  807. boxtest.Result, boxtest.CollisionType);
  808. //
  809. // Calculate what grid cells this box is "possibly" intersecting
  810. //
  811. int min_x = Get_Quad_Index_X (obj_space_test.SweepMin.X);
  812. int min_y = Get_Quad_Index_Y (obj_space_test.SweepMin.Y);
  813. int max_x = Get_Quad_Index_X (obj_space_test.SweepMax.X);
  814. int max_y = Get_Quad_Index_Y (obj_space_test.SweepMax.Y);
  815. int closest_index = 0;
  816. //
  817. // Now check all the quads in this region
  818. //
  819. bool retval = false;
  820. for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
  821. for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
  822. int start_index = Grid_Index (x_pos, y_pos);
  823. int v0_index = start_index;
  824. int v1_index = start_index + 1;
  825. int v2_index = start_index + GridPointsX + 1;
  826. int v3_index = start_index + GridPointsX;
  827. //
  828. // Compose the two triangles for the collision check
  829. //
  830. TriClass tri1;
  831. TriClass tri2;
  832. Vector3 norm1 (0, 0, 0);
  833. Vector3 norm2 (0, 0, 0);
  834. tri1.N = &norm1;
  835. tri2.N = &norm2;
  836. tri1.V[0] = &Grid[v0_index];
  837. tri1.V[1] = &Grid[v2_index];
  838. tri1.V[2] = &Grid[v3_index];
  839. tri2.V[0] = &Grid[v2_index];
  840. tri2.V[1] = &Grid[v0_index];
  841. tri2.V[2] = &Grid[v1_index];
  842. tri1.Compute_Normal ();
  843. tri2.Compute_Normal ();
  844. //
  845. // Do the collision test to determine if the box intersects either of these triangles
  846. //
  847. float percent = obj_space_test.Result->Fraction;
  848. retval |= CollisionMath::Collide (obj_space_test.Box, obj_space_test.Move, tri1, Vector3 (0, 0, 0), obj_space_test.Result);
  849. retval |= CollisionMath::Collide (obj_space_test.Box, obj_space_test.Move, tri2, Vector3 (0, 0, 0), obj_space_test.Result);
  850. //
  851. // Is this the closest collision yet?
  852. //
  853. if (obj_space_test.Result->Fraction < percent) {
  854. closest_index = v0_index;
  855. }
  856. }
  857. }
  858. //
  859. // Make sure to return our pointer to the caller
  860. //
  861. if (retval) {
  862. //
  863. // Determine which surface type was hit
  864. //
  865. if (MaterialPassList.Count () > 0) {
  866. int best_pass = 0;
  867. float best_alpha = 0;
  868. for (int index = 0; index < MaterialPassList.Count (); index ++) {
  869. float alpha = MaterialPassList[index]->VertexAlpha[closest_index];
  870. if (alpha > best_alpha && alpha != 1.0F) {
  871. best_alpha = alpha;
  872. best_pass = index;
  873. }
  874. }
  875. if (MaterialPassList[best_pass]->Material != NULL) {
  876. boxtest.Result->SurfaceType = MaterialPassList[best_pass]->Material->Get_Surface_Type ();
  877. }
  878. }
  879. //
  880. // Transform the result back into world-space
  881. //
  882. if (boxtest.Result->ComputeContactPoint) {
  883. boxtest.Result->ContactPoint = Get_Transform () * boxtest.Result->ContactPoint;
  884. }
  885. boxtest.CollidedRenderObj = this;
  886. }
  887. return retval;
  888. }
  889. //////////////////////////////////////////////////////////////////////
  890. //
  891. // Intersect_AABox
  892. //
  893. //////////////////////////////////////////////////////////////////////
  894. bool
  895. RenegadeTerrainPatchClass::Intersect_AABox (AABoxIntersectionTestClass &boxtest)
  896. {
  897. //
  898. // Skip this object if it doesn't match the collision type
  899. //
  900. if ((Get_Collision_Type() & boxtest.CollisionType) == 0) {
  901. return false;
  902. }
  903. //
  904. // First, transform the box into heightfield (object) space
  905. //
  906. Matrix3D world_to_obj_tm;
  907. Get_Inverse_Transform (world_to_obj_tm);
  908. AABoxClass obj_space_box;
  909. AABoxClass::Transform (world_to_obj_tm, boxtest.Box, &obj_space_box);
  910. //
  911. // Calculate what grid cells this box is "possibly" intersecting
  912. //
  913. int min_x = Get_Quad_Index_X (obj_space_box.Center.X - obj_space_box.Extent.X);
  914. int min_y = Get_Quad_Index_Y (obj_space_box.Center.Y - obj_space_box.Extent.Y);
  915. int max_x = Get_Quad_Index_X (obj_space_box.Center.X + obj_space_box.Extent.X);
  916. int max_y = Get_Quad_Index_Y (obj_space_box.Center.Y + obj_space_box.Extent.Y);
  917. //
  918. // Now check all the quads in this region
  919. //
  920. bool retval = false;
  921. for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
  922. for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
  923. int start_index = Grid_Index (x_pos, y_pos);
  924. int quad_index = y_pos * (GridPointsX - 1) + x_pos;
  925. //
  926. // Skip this quad if its hidden
  927. //
  928. if (QuadFlags[quad_index] & QF_HIDDEN) {
  929. continue;
  930. }
  931. int v0_index = start_index;
  932. int v1_index = start_index + 1;
  933. int v2_index = start_index + GridPointsX + 1;
  934. int v3_index = start_index + GridPointsX;
  935. //
  936. // Compose the two triangles for the collision check
  937. //
  938. TriClass tri1;
  939. TriClass tri2;
  940. Vector3 norm1 (0, 0, 0);
  941. Vector3 norm2 (0, 0, 0);
  942. tri1.N = &norm1;
  943. tri2.N = &norm2;
  944. tri1.V[0] = &Grid[v0_index];
  945. tri1.V[1] = &Grid[v2_index];
  946. tri1.V[2] = &Grid[v3_index];
  947. tri2.V[0] = &Grid[v2_index];
  948. tri2.V[1] = &Grid[v0_index];
  949. tri2.V[2] = &Grid[v1_index];
  950. tri1.Compute_Normal ();
  951. tri2.Compute_Normal ();
  952. //
  953. // Do the collision test to determine if the box intersects either of these triangles
  954. //
  955. retval |= (CollisionMath::Overlap_Test (obj_space_box, tri1) != CollisionMath::OUTSIDE);
  956. retval |= (CollisionMath::Overlap_Test (obj_space_box, tri2) != CollisionMath::OUTSIDE);
  957. }
  958. }
  959. //
  960. // Make sure to return our pointer to the caller
  961. //
  962. /*if (retval) {
  963. boxtest.CollidedRenderObj = this;
  964. }*/
  965. return retval;
  966. }
  967. //////////////////////////////////////////////////////////////////////
  968. //
  969. // Intersect_OBBox
  970. //
  971. //////////////////////////////////////////////////////////////////////
  972. bool
  973. RenegadeTerrainPatchClass::Intersect_OBBox (OBBoxIntersectionTestClass &boxtest)
  974. {
  975. //
  976. // Skip this object if it doesn't match the collision type
  977. //
  978. if ((Get_Collision_Type() & boxtest.CollisionType) == 0) {
  979. return false;
  980. }
  981. //
  982. // First, transform the box into heightfield (object) space
  983. //
  984. Matrix3D world_to_obj_tm;
  985. Get_Inverse_Transform (world_to_obj_tm);
  986. OBBoxClass obj_space_box;
  987. OBBoxClass::Transform (world_to_obj_tm, boxtest.Box, &obj_space_box);
  988. OBBoxIntersectionTestClass obj_space_test (obj_space_box, boxtest.CollisionType);
  989. const AABoxClass &obj_space_aabox = obj_space_test.BoundingBox;
  990. //
  991. // Calculate what grid cells this box is "possibly" intersecting
  992. //
  993. int min_x = Get_Quad_Index_X (obj_space_aabox.Center.X - obj_space_aabox.Extent.X);
  994. int min_y = Get_Quad_Index_Y (obj_space_aabox.Center.Y - obj_space_aabox.Extent.Y);
  995. int max_x = Get_Quad_Index_X (obj_space_aabox.Center.X + obj_space_aabox.Extent.X);
  996. int max_y = Get_Quad_Index_Y (obj_space_aabox.Center.Y + obj_space_aabox.Extent.Y);
  997. //
  998. // Now check all the quads in this region
  999. //
  1000. bool retval = false;
  1001. for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
  1002. for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
  1003. int start_index = Grid_Index (x_pos, y_pos);
  1004. int v0_index = start_index;
  1005. int v1_index = start_index + 1;
  1006. int v2_index = start_index + GridPointsX + 1;
  1007. int v3_index = start_index + GridPointsX;
  1008. //
  1009. // Compose the two triangles for the collision check
  1010. //
  1011. TriClass tri1;
  1012. TriClass tri2;
  1013. Vector3 norm1 (0, 0, 0);
  1014. Vector3 norm2 (0, 0, 0);
  1015. tri1.N = &norm1;
  1016. tri2.N = &norm2;
  1017. tri1.V[0] = &Grid[v0_index];
  1018. tri1.V[1] = &Grid[v2_index];
  1019. tri1.V[2] = &Grid[v3_index];
  1020. tri2.V[0] = &Grid[v2_index];
  1021. tri2.V[1] = &Grid[v0_index];
  1022. tri2.V[2] = &Grid[v1_index];
  1023. tri1.Compute_Normal ();
  1024. tri2.Compute_Normal ();
  1025. //
  1026. // Do the collision test to determine if the box intersects either of these triangles
  1027. //
  1028. retval |= (CollisionMath::Overlap_Test (obj_space_box, tri1) != CollisionMath::OUTSIDE);
  1029. retval |= (CollisionMath::Overlap_Test (obj_space_box, tri2) != CollisionMath::OUTSIDE);
  1030. }
  1031. }
  1032. //
  1033. // Make sure to return our pointer to the caller
  1034. //
  1035. /*if (retval) {
  1036. boxtest.CollidedRenderObj = this;
  1037. }*/
  1038. return retval;
  1039. }
  1040. //////////////////////////////////////////////////////////////////////
  1041. //
  1042. // Cast_AABox
  1043. //
  1044. //////////////////////////////////////////////////////////////////////
  1045. bool
  1046. RenegadeTerrainPatchClass::Cast_AABox (AABoxCollisionTestClass &boxtest)
  1047. {
  1048. //
  1049. // Skip this object if it doesn't match the collision type
  1050. //
  1051. if ((Get_Collision_Type() & boxtest.CollisionType) == 0) {
  1052. return false;
  1053. }
  1054. if (boxtest.Result->StartBad) {
  1055. return false;
  1056. }
  1057. //
  1058. // First, transform the boxtest into heightfield (object) space
  1059. //
  1060. Matrix3D world_to_obj_tm;
  1061. Get_Inverse_Transform (world_to_obj_tm);
  1062. boxtest.Transform (world_to_obj_tm);
  1063. //
  1064. // Calculate what grid cells this box is "possibly" intersecting
  1065. //
  1066. int min_x = Get_Quad_Index_X (boxtest.SweepMin.X);
  1067. int min_y = Get_Quad_Index_Y (boxtest.SweepMin.Y);
  1068. int max_x = Get_Quad_Index_X (boxtest.SweepMax.X);
  1069. int max_y = Get_Quad_Index_Y (boxtest.SweepMax.Y);
  1070. int closest_index = 0;
  1071. //
  1072. // Now check all the quads in this region
  1073. //
  1074. bool retval = false;
  1075. for (int y_pos = min_y; y_pos <= max_y; y_pos ++) {
  1076. for (int x_pos = min_x; x_pos <= max_x; x_pos ++) {
  1077. int start_index = Grid_Index (x_pos, y_pos);
  1078. int quad_index = y_pos * (GridPointsX - 1) + x_pos;
  1079. //
  1080. // Skip this quad if its hidden
  1081. //
  1082. if (QuadFlags[quad_index] & QF_HIDDEN) {
  1083. continue;
  1084. }
  1085. int v0_index = start_index;
  1086. int v1_index = start_index + 1;
  1087. int v2_index = start_index + GridPointsX + 1;
  1088. int v3_index = start_index + GridPointsX;
  1089. //
  1090. // Compose the two triangles for the collision check
  1091. //
  1092. TriClass tri1;
  1093. TriClass tri2;
  1094. Vector3 norm1 (0, 0, 0);
  1095. Vector3 norm2 (0, 0, 0);
  1096. tri1.N = &norm1;
  1097. tri2.N = &norm2;
  1098. tri1.V[0] = &Grid[v0_index];
  1099. tri1.V[1] = &Grid[v2_index];
  1100. tri1.V[2] = &Grid[v3_index];
  1101. tri2.V[0] = &Grid[v2_index];
  1102. tri2.V[1] = &Grid[v0_index];
  1103. tri2.V[2] = &Grid[v1_index];
  1104. tri1.Compute_Normal ();
  1105. tri2.Compute_Normal ();
  1106. //
  1107. // Do the collision test to determine if the box intersects either of these triangles
  1108. //
  1109. float percent = boxtest.Result->Fraction;
  1110. retval |= CollisionMath::Collide (boxtest.Box, boxtest.Move, tri1, boxtest.Result);
  1111. retval |= CollisionMath::Collide (boxtest.Box, boxtest.Move, tri2, boxtest.Result);
  1112. //
  1113. // Is this the closest collision yet?
  1114. //
  1115. if (boxtest.Result->Fraction < percent) {
  1116. closest_index = v0_index;
  1117. }
  1118. }
  1119. }
  1120. //
  1121. // Make sure to return our pointer to the caller
  1122. //
  1123. if (retval) {
  1124. //
  1125. // Determine which surface type was hit
  1126. //
  1127. if (MaterialPassList.Count () > 0) {
  1128. int best_pass = 0;
  1129. float best_alpha = 0;
  1130. for (int index = 0; index < MaterialPassList.Count (); index ++) {
  1131. float alpha = MaterialPassList[index]->VertexAlpha[closest_index];
  1132. if (alpha > best_alpha && alpha != 1.0F) {
  1133. best_alpha = alpha;
  1134. best_pass = index;
  1135. }
  1136. }
  1137. if (MaterialPassList[best_pass]->Material != NULL) {
  1138. boxtest.Result->SurfaceType = MaterialPassList[best_pass]->Material->Get_Surface_Type ();
  1139. }
  1140. }
  1141. //
  1142. // Transform the result back into world-space
  1143. //
  1144. boxtest.Transform (Get_Transform ());
  1145. boxtest.CollidedRenderObj = this;
  1146. }
  1147. return retval;
  1148. }
  1149. //////////////////////////////////////////////////////////////////////
  1150. //
  1151. // Cast_Ray
  1152. //
  1153. //////////////////////////////////////////////////////////////////////
  1154. bool
  1155. RenegadeTerrainPatchClass::Cast_Ray (RayCollisionTestClass &raytest)
  1156. {
  1157. //
  1158. // Skip this object if it doesn't match the collision type
  1159. //
  1160. if ( (Get_Collision_Type() & raytest.CollisionType) == 0 ||
  1161. raytest.Result->StartBad)
  1162. {
  1163. return false;
  1164. }
  1165. bool retval = false;
  1166. #if 0
  1167. CastResultStruct temp_result = (*raytest.Result);
  1168. retval = Brute_Force_Cast_Ray (raytest);
  1169. static bool do_it = false;
  1170. if (do_it) {
  1171. do_it_start:
  1172. CastResultStruct brute_result = (*raytest.Result);
  1173. (*raytest.Result) = temp_result;
  1174. bool retval2 = Cast_Non_Vertical_Ray (raytest);
  1175. if (do_it && (retval != retval2 || brute_result.Fraction != raytest.Result->Fraction)) {
  1176. (*raytest.Result) = temp_result;
  1177. goto do_it_start;
  1178. }
  1179. }
  1180. #else
  1181. retval = Cast_Non_Vertical_Ray (raytest);
  1182. #endif
  1183. //
  1184. // Test for completely vertical ray
  1185. //
  1186. /*if ( raytest.Ray.Get_DP ().X == 0.0F &&
  1187. raytest.Ray.Get_DP ().Y == 0.0F)
  1188. {
  1189. retval = Cast_Vertical_Ray (raytest);
  1190. } else {
  1191. retval = Cast_Non_Vertical_Ray (raytest);
  1192. }*/
  1193. return retval;
  1194. }
  1195. //////////////////////////////////////////////////////////////////////
  1196. //
  1197. // Cast_Vertical_Ray
  1198. //
  1199. //////////////////////////////////////////////////////////////////////
  1200. bool
  1201. RenegadeTerrainPatchClass::Cast_Vertical_Ray (RayCollisionTestClass &raytest)
  1202. {
  1203. //
  1204. // Sanity check
  1205. //
  1206. if (raytest.Ray.Get_DP ().Z == 0) {
  1207. return false;
  1208. }
  1209. bool retval = false;
  1210. //
  1211. // First, transform the ray into heightfield (object) space
  1212. //
  1213. Matrix3D world_to_obj_tm;
  1214. Get_Inverse_Transform (world_to_obj_tm);
  1215. const Vector3 &world_p0 = raytest.Ray.Get_P0 ();
  1216. const Vector3 &world_p1 = raytest.Ray.Get_P1 ();
  1217. Vector3 p0;
  1218. Vector3 p1;
  1219. Matrix3D::Transform_Vector (world_to_obj_tm, world_p0, &p0);
  1220. Matrix3D::Transform_Vector (world_to_obj_tm, world_p1, &p1);
  1221. //
  1222. // Build a line segement from the transformed points that we'll use later
  1223. // for collision detection.
  1224. //
  1225. LineSegClass line_seg (p0, p1);
  1226. //
  1227. // Is this point somewhere over or under the grid?
  1228. //
  1229. if ( p0.X >= BoundingBoxMin.X && p0.Y >= BoundingBoxMin.Y &&
  1230. p0.X <= BoundingBoxMax.X && p0.Y <= BoundingBoxMax.Y)
  1231. {
  1232. int quad_index_x = Get_Quad_Index_X (p0.X);
  1233. int quad_index_y = Get_Quad_Index_Y (p0.Y);
  1234. //
  1235. // Simply test this quad to see if the ray collides with it...
  1236. //
  1237. retval = Collide_Quad (line_seg, quad_index_x, quad_index_y, *raytest.Result);
  1238. }
  1239. return retval;
  1240. }
  1241. //////////////////////////////////////////////////////////////////////
  1242. //
  1243. // Brute_Force_Cast_Ray
  1244. //
  1245. //////////////////////////////////////////////////////////////////////
  1246. bool
  1247. RenegadeTerrainPatchClass::Brute_Force_Cast_Ray (RayCollisionTestClass &raytest)
  1248. {
  1249. bool retval = false;
  1250. //
  1251. // First, transform the ray into heightfield (object) space
  1252. //
  1253. Matrix3D world_to_obj_tm;
  1254. Get_Inverse_Transform (world_to_obj_tm);
  1255. const Vector3 &world_p0 = raytest.Ray.Get_P0 ();
  1256. const Vector3 &world_p1 = raytest.Ray.Get_P1 ();
  1257. Vector3 p0;
  1258. Vector3 p1;
  1259. Matrix3D::Transform_Vector (world_to_obj_tm, world_p0, &p0);
  1260. Matrix3D::Transform_Vector (world_to_obj_tm, world_p1, &p1);
  1261. //
  1262. // Build a line segement from the transformed points that we'll use later
  1263. // for collision detection.
  1264. //
  1265. LineSegClass line_seg (p0, p1);
  1266. //
  1267. // Now check all the quads in this region
  1268. //
  1269. for (int y_pos = 0; y_pos < (GridPointsY-1); y_pos ++) {
  1270. for (int x_pos = 0; x_pos < (GridPointsX-1); x_pos ++) {
  1271. int quad_index = y_pos * (GridPointsX - 1) + x_pos;
  1272. //
  1273. // Skip this quad if its hidden
  1274. //
  1275. if (QuadFlags[quad_index] & QF_HIDDEN) {
  1276. continue;
  1277. }
  1278. Collide_Quad (line_seg, x_pos, y_pos, *raytest.Result);
  1279. }
  1280. }
  1281. if (retval) {
  1282. raytest.CollidedRenderObj = this;
  1283. }
  1284. return retval;
  1285. }
  1286. //////////////////////////////////////////////////////////////////////
  1287. //
  1288. // Cast_Non_Vertical_Ray
  1289. //
  1290. //////////////////////////////////////////////////////////////////////
  1291. bool
  1292. RenegadeTerrainPatchClass::Cast_Non_Vertical_Ray (RayCollisionTestClass &raytest)
  1293. {
  1294. bool retval = false;
  1295. //
  1296. // First, transform the ray into heightfield (object) space
  1297. //
  1298. Matrix3D world_to_obj_tm;
  1299. Get_Inverse_Transform (world_to_obj_tm);
  1300. const Vector3 &world_p0 = raytest.Ray.Get_P0 ();
  1301. const Vector3 &world_p1 = raytest.Ray.Get_P1 ();
  1302. Vector3 p0;
  1303. Vector3 p1;
  1304. Matrix3D::Transform_Vector (world_to_obj_tm, world_p0, &p0);
  1305. Matrix3D::Transform_Vector (world_to_obj_tm, world_p1, &p1);
  1306. //
  1307. // Build a line segement from the transformed points that we'll use later
  1308. // for collision detection.
  1309. //
  1310. LineSegClass line_seg (p0, p1);
  1311. const Vector3 &delta_p = line_seg.Get_DP ();
  1312. //
  1313. // Calculate where on the grid we need to search
  1314. //
  1315. int start_cell_x = Get_Quad_Index_X (p0.X);
  1316. int start_cell_y = Get_Quad_Index_Y (p0.Y);
  1317. int end_cell_x = Get_Quad_Index_X (p1.X);
  1318. int end_cell_y = Get_Quad_Index_Y (p1.Y);
  1319. //
  1320. // Determine how many vertical grid lines to search
  1321. //
  1322. int x_inc = 1;
  1323. if (start_cell_x > end_cell_x) {
  1324. x_inc = -1;
  1325. }
  1326. //
  1327. // Determine how many horizontal grid lines to search
  1328. //
  1329. int y_inc = 1;
  1330. if (start_cell_y > end_cell_y) {
  1331. y_inc = -1;
  1332. }
  1333. int cells_x = ::abs (end_cell_x - start_cell_x);
  1334. int cells_y = ::abs (end_cell_y - start_cell_y);
  1335. //
  1336. // Determine if we need to offset the cell we're searching each
  1337. // time we stop at a vertical or horizontal grid intersection
  1338. //
  1339. int cell_x_offset = 0;
  1340. int cell_y_offset = 0;
  1341. if (x_inc > 0) {
  1342. cell_x_offset = -1;
  1343. }
  1344. if (y_inc > 0) {
  1345. cell_y_offset = -1;
  1346. }
  1347. CastResultStruct vertical_result = *(raytest.Result);
  1348. CastResultStruct horizontal_result = *(raytest.Result);
  1349. bool did_vertical_collide = false;
  1350. bool did_horizontal_collide = false;
  1351. //
  1352. // Now, determine what the actual starting and ending cell coordinates are...
  1353. //
  1354. Vector2i cell_coord_p0 (start_cell_x, start_cell_y);
  1355. Vector2i cell_coord_p1 (end_cell_x, end_cell_x);
  1356. if (delta_p.X != 0.0F) {
  1357. float clamped_p0_x = WWMath::Clamp (p0.X, BoundingBoxMin.X, BoundingBoxMax.X);
  1358. float clamped_p1_x = WWMath::Clamp (p1.X, BoundingBoxMin.X, BoundingBoxMax.X);
  1359. float clamped_p0_y = p0.Y + (delta_p.Y * ((clamped_p0_x - p0.X) / delta_p.X));
  1360. float clamped_p1_y = p0.Y + (delta_p.Y * ((clamped_p1_x - p0.X) / delta_p.X));
  1361. cell_coord_p0.Set (Get_Quad_Index_X (clamped_p0_x), Get_Quad_Index_Y (clamped_p0_y));
  1362. cell_coord_p1.Set (Get_Quad_Index_X (clamped_p1_x), Get_Quad_Index_Y (clamped_p1_y));
  1363. } else if (delta_p.Y != 0.0F) {
  1364. float clamped_p0_y = WWMath::Clamp (p0.Y, BoundingBoxMin.Y, BoundingBoxMax.Y);
  1365. float clamped_p1_y = WWMath::Clamp (p1.Y, BoundingBoxMin.Y, BoundingBoxMax.Y);
  1366. float clamped_p0_x = p0.X + (delta_p.X * ((clamped_p0_y - p0.Y) / delta_p.Y));
  1367. float clamped_p1_x = p0.X + (delta_p.X * ((clamped_p1_y - p0.Y) / delta_p.Y));
  1368. cell_coord_p0.Set (Get_Quad_Index_X (clamped_p0_x), Get_Quad_Index_Y (clamped_p0_y));
  1369. cell_coord_p1.Set (Get_Quad_Index_X (clamped_p1_x), Get_Quad_Index_Y (clamped_p1_y));
  1370. }
  1371. //
  1372. // Test quad of triangles in the starting cell to see if you need go no further.
  1373. //
  1374. if (Collide_Quad (line_seg, cell_coord_p0.I, cell_coord_p0.J, vertical_result)) {
  1375. did_vertical_collide = true;
  1376. } else {
  1377. //
  1378. // Test vertical grid lines first
  1379. //
  1380. if (delta_p.X != 0.0F) {
  1381. //
  1382. // Find the first intersection point moving along vertical grid lines
  1383. //
  1384. for (int curr_x = start_cell_x; cells_x >= 0; cells_x --, curr_x += x_inc) {
  1385. //
  1386. // Determine where the ray intersects this grid line
  1387. //
  1388. float x_pos = Get_Grid_Line_Pos_X (curr_x);
  1389. float percent = (x_pos - p0.X) / delta_p.X;
  1390. //
  1391. // Don't test the cell if its outside the range of the line segment
  1392. //
  1393. if (percent >= 0 && percent <= 1.0F) {
  1394. float y_pos = p0.Y + (delta_p.Y * percent);
  1395. //
  1396. // Now determine what grid cell this is
  1397. //
  1398. int cell_x = curr_x + cell_x_offset;
  1399. int cell_y = Get_Quad_Index_Y (y_pos, false);
  1400. if (Is_Valid_Quad (cell_x, cell_y)) {
  1401. //
  1402. // Test this quad to see if either of its triangles intersect
  1403. //
  1404. if (Collide_Quad (line_seg, cell_x, cell_y, vertical_result)) {
  1405. did_vertical_collide = true;
  1406. break;
  1407. }
  1408. }
  1409. }
  1410. }
  1411. }
  1412. //
  1413. // Test horizontal grid lines next
  1414. //
  1415. if (delta_p.Y != 0.0F) {
  1416. //
  1417. // Find the first intersection point moving along horizontal grid lines
  1418. //
  1419. for (int curr_y = start_cell_y; cells_y >= 0; cells_y --, curr_y += y_inc) {
  1420. //
  1421. // Determine where the ray intersects this grid line
  1422. //
  1423. float y_pos = Get_Grid_Line_Pos_Y (curr_y);
  1424. float percent = (y_pos - p0.Y) / delta_p.Y;
  1425. //
  1426. // Don't test the cell if its outside the range of the line segment
  1427. //
  1428. if (percent >= 0 && percent <= 1.0F) {
  1429. float x_pos = p0.X + (delta_p.X * percent);
  1430. //
  1431. // Now determine what grid cell this is
  1432. //
  1433. int cell_y = curr_y + cell_y_offset;
  1434. int cell_x = Get_Quad_Index_X (x_pos, false);
  1435. if (Is_Valid_Quad (cell_x, cell_y)) {
  1436. //
  1437. // Test this quad to see if either of its triangles intersect
  1438. //
  1439. if (Collide_Quad (line_seg, cell_x, cell_y, horizontal_result)) {
  1440. did_horizontal_collide = true;
  1441. break;
  1442. }
  1443. }
  1444. }
  1445. }
  1446. }
  1447. //
  1448. // Make sure to test the ending cell
  1449. //
  1450. if (did_vertical_collide == false && did_horizontal_collide == false) {
  1451. if (Collide_Quad (line_seg, cell_coord_p1.I, cell_coord_p1.J, vertical_result)) {
  1452. did_vertical_collide = true;
  1453. }
  1454. }
  1455. }
  1456. //
  1457. // Did either collide?
  1458. //
  1459. if (did_vertical_collide || did_horizontal_collide) {
  1460. retval = true;
  1461. //
  1462. // Determine which result was hit first
  1463. //
  1464. if (did_vertical_collide && did_horizontal_collide) {
  1465. //
  1466. // Both triangles intersected, so find the one that hit first.
  1467. //
  1468. if (vertical_result.Fraction < horizontal_result.Fraction) {
  1469. *(raytest.Result) = vertical_result;
  1470. } else {
  1471. *(raytest.Result) = horizontal_result;
  1472. }
  1473. } else if (did_vertical_collide) {
  1474. *(raytest.Result) = vertical_result;
  1475. } else {
  1476. *(raytest.Result) = horizontal_result;
  1477. }
  1478. }
  1479. return retval;
  1480. }
  1481. //////////////////////////////////////////////////////////////////////
  1482. //
  1483. // Get_Factory
  1484. //
  1485. //////////////////////////////////////////////////////////////////////
  1486. const PersistFactoryClass &
  1487. RenegadeTerrainPatchClass::Get_Factory (void) const
  1488. {
  1489. return _TerrainPatchFactory;
  1490. }
  1491. //////////////////////////////////////////////////////////////////////
  1492. //
  1493. // Save
  1494. //
  1495. //////////////////////////////////////////////////////////////////////
  1496. bool
  1497. RenegadeTerrainPatchClass::Save (ChunkSaveClass &csave)
  1498. {
  1499. //
  1500. // Write the variables
  1501. //
  1502. csave.Begin_Chunk (CHUNKID_VARIABLES);
  1503. WRITE_MICRO_CHUNK (csave, VARID_GRID_PTS_X, GridPointsX);
  1504. WRITE_MICRO_CHUNK (csave, VARID_GRID_PTS_Y, GridPointsY);
  1505. WRITE_MICRO_CHUNK (csave, VARID_GRID_PT_COUNT, GridPointCount);
  1506. WRITE_MICRO_CHUNK (csave, VARID_GRID_BBOX_MIN, BoundingBoxMin);
  1507. WRITE_MICRO_CHUNK (csave, VARID_GRID_BBOX_MAX, BoundingBoxMax);
  1508. WRITE_MICRO_CHUNK (csave, VARID_GRID_DENSITY, Density);
  1509. WRITE_MICRO_CHUNK (csave, VARID_IS_PRELIT, IsPreLit);
  1510. csave.End_Chunk ();
  1511. //
  1512. // Now, write out the "array" of heights
  1513. //
  1514. csave.Begin_Chunk (CHUNKID_HEIGHTS);
  1515. for (int index = 0; index < GridPointCount; index ++) {
  1516. csave.Write (&Grid[index].X, sizeof (float) * 3);
  1517. }
  1518. csave.End_Chunk ();
  1519. //
  1520. // Now, write out the "array" of normals
  1521. //
  1522. csave.Begin_Chunk (CHUNKID_NORMALS);
  1523. for (index = 0; index < GridPointCount; index ++) {
  1524. csave.Write (&GridNormals[index].X, sizeof (float) * 3);
  1525. }
  1526. csave.End_Chunk ();
  1527. //
  1528. // Now, write out the "array" of vertex colors
  1529. //
  1530. csave.Begin_Chunk (CHUNKID_VERTEX_COLORS);
  1531. for (index = 0; index < GridPointCount; index ++) {
  1532. csave.Write (&VertexColors[index].X, sizeof (float) * 3);
  1533. }
  1534. csave.End_Chunk ();
  1535. //
  1536. // Now, write out the array of quad flags
  1537. //
  1538. csave.Begin_Chunk (CHUNKID_QUAD_FLAGS);
  1539. int quad_count = (GridPointsX - 1) * (GridPointsY - 1);
  1540. csave.Write (QuadFlags, sizeof (uint8) * quad_count);
  1541. csave.End_Chunk ();
  1542. //
  1543. // Now, save each material layer
  1544. //
  1545. csave.Begin_Chunk (CHUNKID_MATERIAL_LAYERS);
  1546. for (index = 0; index < MaterialPassList.Count (); index ++) {
  1547. //
  1548. // Don't save the material information if there' no material configured...
  1549. //
  1550. if (MaterialPassList[index]->Material != NULL) {
  1551. //
  1552. // Save this material layer to its own chunk
  1553. //
  1554. csave.Begin_Chunk (CHUNKID_MATERIAL_LAYER);
  1555. MaterialPassList[index]->Save (csave);
  1556. csave.End_Chunk ();
  1557. }
  1558. }
  1559. csave.End_Chunk ();
  1560. return true;
  1561. }
  1562. //////////////////////////////////////////////////////////////////////
  1563. //
  1564. // Load
  1565. //
  1566. //////////////////////////////////////////////////////////////////////
  1567. bool
  1568. RenegadeTerrainPatchClass::Load (ChunkLoadClass &cload)
  1569. {
  1570. Free_Grid ();
  1571. while (cload.Open_Chunk ()) {
  1572. switch (cload.Cur_Chunk_ID ()) {
  1573. //
  1574. // Load all the variables from this chunk
  1575. //
  1576. case CHUNKID_VARIABLES:
  1577. Load_Variables (cload);
  1578. break;
  1579. case CHUNKID_HEIGHTS:
  1580. {
  1581. //
  1582. // Read the array of heights
  1583. //
  1584. for (int index = 0; index < GridPointCount; index ++) {
  1585. cload.Read (&Grid[index].X, sizeof (float) * 3);
  1586. }
  1587. break;
  1588. }
  1589. case CHUNKID_NORMALS:
  1590. {
  1591. //
  1592. // Read the array of normals
  1593. //
  1594. for (int index = 0; index < GridPointCount; index ++) {
  1595. cload.Read (&GridNormals[index].X, sizeof (float) * 3);
  1596. }
  1597. break;
  1598. }
  1599. case CHUNKID_VERTEX_COLORS:
  1600. {
  1601. //
  1602. // Read the array of vertex colors
  1603. //
  1604. for (int index = 0; index < GridPointCount; index ++) {
  1605. cload.Read (&VertexColors[index].X, sizeof (float) * 3);
  1606. }
  1607. break;
  1608. }
  1609. case CHUNKID_QUAD_FLAGS:
  1610. {
  1611. //
  1612. // Read the array of quad flags
  1613. //
  1614. int quad_count = (GridPointsX - 1) * (GridPointsY - 1);
  1615. cload.Read (QuadFlags, sizeof (uint8) * quad_count);
  1616. break;
  1617. }
  1618. case CHUNKID_MATERIAL_LAYERS:
  1619. Load_Materials (cload);
  1620. break;
  1621. }
  1622. cload.Close_Chunk ();
  1623. }
  1624. Invalidate_Cached_Bounding_Volumes();
  1625. return true;
  1626. }
  1627. ////////////////////////////////////////////////////////////////
  1628. //
  1629. // Load_Variables
  1630. //
  1631. ////////////////////////////////////////////////////////////////
  1632. void
  1633. RenegadeTerrainPatchClass::Load_Variables (ChunkLoadClass &cload)
  1634. {
  1635. while (cload.Open_Micro_Chunk ()) {
  1636. switch (cload.Cur_Micro_Chunk_ID ()) {
  1637. //
  1638. // Read each of the microchunks
  1639. //
  1640. READ_MICRO_CHUNK (cload, VARID_GRID_PTS_X, GridPointsX);
  1641. READ_MICRO_CHUNK (cload, VARID_GRID_PTS_Y, GridPointsY);
  1642. READ_MICRO_CHUNK (cload, VARID_GRID_PT_COUNT, GridPointCount);
  1643. READ_MICRO_CHUNK (cload, VARID_GRID_BBOX_MIN, BoundingBoxMin);
  1644. READ_MICRO_CHUNK (cload, VARID_GRID_BBOX_MAX, BoundingBoxMax);
  1645. READ_MICRO_CHUNK (cload, VARID_GRID_DENSITY, Density);
  1646. READ_MICRO_CHUNK (cload, VARID_IS_PRELIT, IsPreLit);
  1647. }
  1648. cload.Close_Micro_Chunk ();
  1649. }
  1650. Set_Has_User_Lighting(IsPreLit);
  1651. //
  1652. // Allocate and initialize the grid and supporting data structures
  1653. //
  1654. Allocate_Grid ();
  1655. return ;
  1656. }
  1657. ////////////////////////////////////////////////////////////////
  1658. //
  1659. // Load_Materials
  1660. //
  1661. ////////////////////////////////////////////////////////////////
  1662. void
  1663. RenegadeTerrainPatchClass::Load_Materials (ChunkLoadClass &cload)
  1664. {
  1665. while (cload.Open_Chunk ()) {
  1666. switch (cload.Cur_Chunk_ID ()) {
  1667. case CHUNKID_MATERIAL_LAYER:
  1668. {
  1669. //
  1670. // Create and load the material
  1671. //
  1672. RenegadeTerrainMaterialPassClass *material = new RenegadeTerrainMaterialPassClass;
  1673. material->Load (cload);
  1674. //
  1675. // Add the material to our list
  1676. //
  1677. MaterialPassList.Add (material);
  1678. break;
  1679. }
  1680. }
  1681. cload.Close_Chunk ();
  1682. }
  1683. return ;
  1684. }
  1685. ////////////////////////////////////////////////////////////////
  1686. //
  1687. // Add_Material
  1688. //
  1689. ////////////////////////////////////////////////////////////////
  1690. int
  1691. RenegadeTerrainPatchClass::Add_Material (TerrainMaterialClass *material)
  1692. {
  1693. //
  1694. // Add a reference to the material
  1695. //
  1696. if (material != NULL) {
  1697. material->Add_Ref ();
  1698. }
  1699. //
  1700. // Allocate a new pass for this material
  1701. //
  1702. RenegadeTerrainMaterialPassClass *material_pass = new RenegadeTerrainMaterialPassClass;
  1703. material_pass->Allocate (GridPointCount);
  1704. material_pass->Material = material;
  1705. //
  1706. // Add this material
  1707. //
  1708. int retval = MaterialPassList.Count ();
  1709. MaterialPassList.Add (material_pass);
  1710. return retval;
  1711. }
  1712. ////////////////////////////////////////////////////////////////
  1713. //
  1714. // Reset_Material_Passes
  1715. //
  1716. ////////////////////////////////////////////////////////////////
  1717. void
  1718. RenegadeTerrainPatchClass::Reset_Material_Passes (void)
  1719. {
  1720. //
  1721. // Reset each of the materials
  1722. //
  1723. for (int index = 0; index < MaterialPassList.Count (); index ++) {
  1724. MaterialPassList[index]->Reset ();
  1725. }
  1726. return ;
  1727. }
  1728. ////////////////////////////////////////////////////////////////
  1729. //
  1730. // Update_UVs
  1731. //
  1732. ////////////////////////////////////////////////////////////////
  1733. void
  1734. RenegadeTerrainPatchClass::Update_UVs (void)
  1735. {
  1736. for (int y_pos = 0; y_pos < GridPointsY; y_pos ++) {
  1737. int start_index = (y_pos * GridPointsX);
  1738. for (int x_pos = 0; x_pos < GridPointsX; x_pos ++) {
  1739. //
  1740. // Calculate the UV coordinate for this texture at this vertex
  1741. //
  1742. for (int index = 0; index < MaterialPassList.Count (); index ++) {
  1743. if (MaterialPassList[index]->Material != NULL) {
  1744. float meters_per_texture = MaterialPassList[index]->Material->Get_Meters_Per_Tile ();
  1745. float u_value = Grid[start_index + x_pos].X / meters_per_texture;
  1746. float v_value = Grid[start_index + x_pos].Y / meters_per_texture;
  1747. //
  1748. // Handle mirrored UVs
  1749. //
  1750. if (MaterialPassList[index]->Material->Are_UVs_Mirrored ()) {
  1751. int u_int_value = WWMath::Float_To_Int_Floor (u_value);
  1752. int v_int_value = WWMath::Float_To_Int_Floor (v_value);
  1753. u_value = u_value - u_int_value;
  1754. v_value = v_value - v_int_value;
  1755. if (u_int_value & 1) {
  1756. u_value = 1.0F - u_value;
  1757. }
  1758. if (v_int_value & 1) {
  1759. v_value = 1.0F - v_value;
  1760. }
  1761. }
  1762. MaterialPassList[index]->GridUVs[start_index + x_pos].Set (u_value, v_value);
  1763. }
  1764. }
  1765. }
  1766. }
  1767. AreBuffersDirty = true;
  1768. return ;
  1769. }
  1770. ////////////////////////////////////////////////////////////////
  1771. //
  1772. // Update_Vertex_Render_Lists
  1773. //
  1774. ////////////////////////////////////////////////////////////////
  1775. void
  1776. RenegadeTerrainPatchClass::Update_Vertex_Render_Lists (void)
  1777. {
  1778. int col_count = GridPointsX - 1;
  1779. //
  1780. // Loop over each material layer
  1781. //
  1782. for (int index = 0; index < MaterialPassList.Count (); index ++) {
  1783. //
  1784. // Loop over each pass for this layer
  1785. //
  1786. RenegadeTerrainMaterialPassClass *material_pass = MaterialPassList[index];
  1787. for (int pass = 0; pass < RenegadeTerrainMaterialPassClass::PASS_COUNT; pass ++) {
  1788. //
  1789. // Loop over each quad that is rendered in this material pass
  1790. //
  1791. for (int quad_index = 0; quad_index < material_pass->QuadList[pass].Count (); quad_index ++) {
  1792. //
  1793. // Determine the coordinate for this quad
  1794. //
  1795. int real_quad_index = material_pass->QuadList[pass][quad_index];
  1796. int quad_y = (real_quad_index / col_count);
  1797. int quad_x = real_quad_index - (quad_y * col_count);
  1798. //
  1799. // Get the vertex indices that define the four corners of this quad
  1800. //
  1801. int curr_src_index = Grid_Index (quad_x, quad_y);
  1802. int v0_index = curr_src_index;
  1803. int v1_index = curr_src_index + 1;
  1804. int v2_index = curr_src_index + GridPointsX + 1;
  1805. int v3_index = curr_src_index + GridPointsX;
  1806. //
  1807. // Add the verts from this quad to the render list
  1808. //
  1809. if (material_pass->VertexIndexMap[pass][v0_index] == -1) {
  1810. material_pass->VertexIndexMap[pass][v0_index] = material_pass->VertexRenderList[pass].Count ();
  1811. material_pass->VertexRenderList[pass].Add (v0_index);
  1812. }
  1813. if (material_pass->VertexIndexMap[pass][v1_index] == -1) {
  1814. material_pass->VertexIndexMap[pass][v1_index] = material_pass->VertexRenderList[pass].Count ();
  1815. material_pass->VertexRenderList[pass].Add (v1_index);
  1816. }
  1817. if (material_pass->VertexIndexMap[pass][v2_index] == -1) {
  1818. material_pass->VertexIndexMap[pass][v2_index] = material_pass->VertexRenderList[pass].Count ();
  1819. material_pass->VertexRenderList[pass].Add (v2_index);
  1820. }
  1821. if (material_pass->VertexIndexMap[pass][v3_index] == -1) {
  1822. material_pass->VertexIndexMap[pass][v3_index] = material_pass->VertexRenderList[pass].Count ();
  1823. material_pass->VertexRenderList[pass].Add (v3_index);
  1824. }
  1825. }
  1826. }
  1827. }
  1828. AreBuffersDirty = true;
  1829. return ;
  1830. }
  1831. ////////////////////////////////////////////////////////////////
  1832. //
  1833. // Peek_Material_Pass
  1834. //
  1835. ////////////////////////////////////////////////////////////////
  1836. RenegadeTerrainMaterialPassClass *
  1837. RenegadeTerrainPatchClass::Get_Material_Pass (int index, TerrainMaterialClass *material)
  1838. {
  1839. //
  1840. // Grow the list as necessary
  1841. //
  1842. while (index >= MaterialPassList.Count ()) {
  1843. Add_Material (NULL);
  1844. }
  1845. //
  1846. // Ensure the material is correct at this layer
  1847. //
  1848. if (MaterialPassList[index]->Material != material) {
  1849. REF_PTR_SET (MaterialPassList[index]->Material, material);
  1850. }
  1851. return MaterialPassList[index];
  1852. }