heightdb.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  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 : LevelEdit *
  23. * *
  24. * $Archive:: /Commando/Code/wwphys/heightdb.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 1/19/01 1:48p $*
  29. * *
  30. * $Revision:: 5 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "heightdb.h"
  36. #include "pscene.h"
  37. #include "coltest.h"
  38. #include "phys.h"
  39. #include "physcoltest.h"
  40. #include "mesh.h"
  41. #include "meshmdl.h"
  42. #include "chunkio.h"
  43. /////////////////////////////////////////////////////////////////////////
  44. // Constants
  45. /////////////////////////////////////////////////////////////////////////
  46. const float HEIGHT_OFFSET = 2.0F;
  47. enum
  48. {
  49. CHUNKID_VARIABLES = 0x07310202,
  50. CHUNKID_HEIGHT_ARRAY
  51. };
  52. enum
  53. {
  54. VARID_NUM_POINTS_X = 0x01,
  55. VARID_NUM_POINTS_Y,
  56. VARID_PATCH_SIZE,
  57. VARID_LEV_MIN,
  58. VARID_LEV_MAX
  59. };
  60. /////////////////////////////////////////////////////////////////////////
  61. // Static member initialization
  62. /////////////////////////////////////////////////////////////////////////
  63. float * HeightDBClass::m_HeightArray = NULL;
  64. int HeightDBClass::m_NumPointsX = 0;
  65. int HeightDBClass::m_NumPointsY = 0;
  66. float HeightDBClass::m_PatchSize = 8;
  67. Vector3 HeightDBClass::m_LevelMin (0, 0, 0);
  68. Vector3 HeightDBClass::m_LevelMax (0, 0, 0);
  69. /////////////////////////////////////////////////////////////////////////
  70. //
  71. // HeightDBClass
  72. //
  73. /////////////////////////////////////////////////////////////////////////
  74. HeightDBClass::HeightDBClass (void)
  75. {
  76. return ;
  77. }
  78. /////////////////////////////////////////////////////////////////////////
  79. //
  80. // ~HeightDBClass
  81. //
  82. /////////////////////////////////////////////////////////////////////////
  83. HeightDBClass::~HeightDBClass (void)
  84. {
  85. return ;
  86. }
  87. /////////////////////////////////////////////////////////////////////////
  88. //
  89. // Initialize
  90. //
  91. /////////////////////////////////////////////////////////////////////////
  92. void
  93. HeightDBClass::Initialize (void)
  94. {
  95. return ;
  96. }
  97. /////////////////////////////////////////////////////////////////////////
  98. //
  99. // Shutdown
  100. //
  101. /////////////////////////////////////////////////////////////////////////
  102. void
  103. HeightDBClass::Shutdown (void)
  104. {
  105. Free_Data ();
  106. return ;
  107. }
  108. /////////////////////////////////////////////////////////////////////////
  109. //
  110. // Get_Height
  111. //
  112. /////////////////////////////////////////////////////////////////////////
  113. float
  114. HeightDBClass::Get_Height (const Vector3 &pos)
  115. {
  116. float height = pos.Z;
  117. if (m_HeightArray != NULL && m_NumPointsX > 0 && m_NumPointsY > 0) {
  118. float percent_x = (pos.X - m_LevelMin.X) / (m_LevelMax.X - m_LevelMin.X);
  119. float percent_y = (pos.Y - m_LevelMin.Y) / (m_LevelMax.Y - m_LevelMin.Y);
  120. //
  121. // Is the position inside our data set?
  122. //
  123. if (percent_x >= 0 && percent_x <= 1.0F && percent_y >= 0 && percent_y <= 1.0F) {
  124. int entry_ul_x = (m_NumPointsX - 1) * percent_x;
  125. int entry_ul_y = (m_NumPointsY - 1) * percent_y;
  126. int entry_ur_x = entry_ul_x + 1;
  127. int entry_ur_y = entry_ul_y;
  128. int entry_lr_x = entry_ul_x + 1;
  129. int entry_lr_y = entry_ul_y + 1;
  130. int entry_ll_x = entry_ul_x;
  131. int entry_ll_y = entry_ul_y + 1;
  132. if (entry_ul_x + 1 >= m_NumPointsX) {
  133. entry_ur_x = entry_ul_x;
  134. entry_lr_x = entry_ul_x;
  135. }
  136. if (entry_ul_y + 1 >= m_NumPointsY) {
  137. entry_lr_y = entry_ul_y;
  138. entry_ll_y = entry_ul_y;
  139. }
  140. float *ul_entry = Get_Height_Entry (entry_ul_y, entry_ul_x);
  141. float *ur_entry = Get_Height_Entry (entry_ur_y, entry_ur_x);
  142. float *lr_entry = Get_Height_Entry (entry_lr_y, entry_lr_x);
  143. float *ll_entry = Get_Height_Entry (entry_ll_y, entry_ll_x);
  144. float local_per_x = ((pos.X - m_LevelMin.X) - (entry_ul_x * m_PatchSize)) / m_PatchSize;
  145. float local_per_y = ((pos.Y - m_LevelMin.Y) - (entry_ul_y * m_PatchSize)) / m_PatchSize;
  146. //
  147. // Take the weighted average of the 4 corner values
  148. //
  149. float h1 = (1 - local_per_x) * (1 - local_per_y) * (*ul_entry);
  150. float h2 = (local_per_x) * (1 - local_per_y) * (*ur_entry);
  151. float h3 = (local_per_x) * (local_per_y) * (*lr_entry);
  152. float h4 = (1 - local_per_x) * (local_per_y) * (*ll_entry);
  153. height = h1 + h2 + h3 + h4;
  154. }
  155. }
  156. return height;
  157. }
  158. /////////////////////////////////////////////////////////////////////////
  159. //
  160. // Generate
  161. //
  162. /////////////////////////////////////////////////////////////////////////
  163. void
  164. HeightDBClass::Generate (void)
  165. {
  166. Free_Data ();
  167. PhysicsSceneClass::Get_Instance ()->Get_Level_Extents (m_LevelMin, m_LevelMax);
  168. float generate_patch_size = m_PatchSize / 2;
  169. //
  170. // Determine how big a heightmap we will need to store data for this level
  171. //
  172. m_NumPointsX = ((m_LevelMax.X - m_LevelMin.X) / generate_patch_size) + 1;
  173. m_NumPointsY = ((m_LevelMax.Y - m_LevelMin.Y) / generate_patch_size) + 1;
  174. int entries = m_NumPointsX * m_NumPointsY;
  175. m_HeightArray = new float[entries];
  176. //
  177. // Build a heightmap by casting rays through the level vertically at each grid intersection
  178. //
  179. int row = 0;
  180. int col = 0;
  181. for (float y_pos = m_LevelMin.Y; y_pos < m_LevelMax.Y; y_pos += generate_patch_size) {
  182. col = 0;
  183. for (float x_pos = m_LevelMin.X; x_pos < m_LevelMax.X; x_pos += generate_patch_size) {
  184. float z_pos = m_LevelMin.Z;
  185. float start_z = m_LevelMax.Z + 250.0F;
  186. float end_z = m_LevelMin.Z - 250.0F;
  187. //
  188. // Setup a raycast at this (x,y) position that goes through the world
  189. //
  190. CastResultStruct result;
  191. LineSegClass ray (Vector3 (x_pos, y_pos, start_z), Vector3 (x_pos, y_pos, end_z));
  192. PhysRayCollisionTestClass raytest (ray, &result, 0, COLLISION_TYPE_PROJECTILE);
  193. PhysicsSceneClass::Get_Instance ()->Cast_Ray (raytest);
  194. //
  195. // Return a pointer to the collided physics object if the
  196. // cast hit something
  197. //
  198. if (result.Fraction < 1.0F) {
  199. z_pos = start_z + (end_z - start_z) * result.Fraction;
  200. }
  201. //
  202. // Store this height value in our array
  203. //
  204. float *height_value = Get_Height_Entry (row, col);
  205. if (height_value != NULL) {
  206. (*height_value) = (z_pos + HEIGHT_OFFSET);
  207. }
  208. //
  209. // Move over one columne
  210. //
  211. col ++;
  212. }
  213. //
  214. // Move down one row
  215. //
  216. row ++;
  217. }
  218. Examine_Level_Geometry ();
  219. //
  220. // Now average out the height values so we don't get large jumps in height
  221. //
  222. for (row = 1; row < m_NumPointsY - 1; row ++) {
  223. for (col = 1; col < m_NumPointsX - 1; col ++) {
  224. //
  225. // Lookup the height values of the 9 entries surrounding the current entry
  226. //
  227. float *z_val1 = Get_Height_Entry (row - 1, col - 1);
  228. float *z_val2 = Get_Height_Entry (row - 1, col);
  229. float *z_val3 = Get_Height_Entry (row - 1, col + 1);
  230. float *z_val4 = Get_Height_Entry (row, col - 1);
  231. float *z_val5 = Get_Height_Entry (row, col);
  232. float *z_val6 = Get_Height_Entry (row, col + 1);
  233. float *z_val7 = Get_Height_Entry (row + 1, col - 1);
  234. float *z_val8 = Get_Height_Entry (row + 1, col);
  235. float *z_val9 = Get_Height_Entry (row + 1, col + 1);
  236. //
  237. // Average the 9 height values
  238. //
  239. float average = ( *z_val1 + *z_val2 + *z_val3 +
  240. *z_val4 + *z_val5 + *z_val6 +
  241. *z_val7 + *z_val8 + *z_val9) / 9.0F;
  242. //
  243. // Now store the larger of the old value and the average in each of these
  244. // 9 entries
  245. //
  246. float edge_avg = average * 0.98F;
  247. *z_val1 = max (*z_val1, edge_avg);
  248. *z_val2 = max (*z_val2, edge_avg);
  249. *z_val3 = max (*z_val3, edge_avg);
  250. *z_val4 = max (*z_val4, edge_avg);
  251. *z_val5 = max (*z_val5, average);
  252. *z_val6 = max (*z_val6, edge_avg);
  253. *z_val7 = max (*z_val7, edge_avg);
  254. *z_val8 = max (*z_val8, edge_avg);
  255. *z_val9 = max (*z_val9, edge_avg);
  256. }
  257. }
  258. int temp_points_x = m_NumPointsX / 2;
  259. int temp_points_y = m_NumPointsY / 2;
  260. float *temp_height_array = new float[temp_points_x * temp_points_y];
  261. for (row = 0; row < temp_points_y; row ++) {
  262. for (col = 0; col < temp_points_x; col ++) {
  263. float *z_val = Get_Height_Entry (row * 2, col * 2);
  264. if (z_val != NULL) {
  265. temp_height_array[(row * temp_points_x) + col] = *z_val;
  266. } else {
  267. temp_height_array[(row * temp_points_x) + col] = 0;
  268. }
  269. }
  270. }
  271. Free_Data ();
  272. m_HeightArray = temp_height_array;
  273. m_NumPointsX = temp_points_x;
  274. m_NumPointsY = temp_points_y;
  275. return ;
  276. }
  277. /////////////////////////////////////////////////////////////////////////
  278. //
  279. // Free_Data
  280. //
  281. /////////////////////////////////////////////////////////////////////////
  282. void
  283. HeightDBClass::Free_Data (void)
  284. {
  285. if (m_HeightArray != NULL) {
  286. delete [] m_HeightArray;
  287. m_HeightArray = NULL;
  288. }
  289. m_NumPointsX = 0;
  290. m_NumPointsY = 0;
  291. return ;
  292. }
  293. /////////////////////////////////////////////////////////////////////////
  294. //
  295. // Examine_Level_Geometry
  296. //
  297. /////////////////////////////////////////////////////////////////////////
  298. void
  299. HeightDBClass::Examine_Level_Geometry (void)
  300. {
  301. RefPhysListIterator it1 = PhysicsSceneClass::Get_Instance ()->Get_Static_Object_Iterator ();
  302. RefPhysListIterator it2 = PhysicsSceneClass::Get_Instance ()->Get_Static_Anim_Object_Iterator ();
  303. //
  304. // Process the static meshes
  305. //
  306. for (it1.First (); !it1.Is_Done (); it1.Next ()) {
  307. PhysClass *phys_obj = it1.Peek_Obj ();
  308. if (phys_obj != NULL) {
  309. RenderObjClass *model = phys_obj->Peek_Model ();
  310. if (model != NULL) {
  311. Process_Render_Obj (model);
  312. }
  313. }
  314. }
  315. //
  316. // Process the static animated meshes
  317. //
  318. for (it2.First (); !it2.Is_Done (); it2.Next ()) {
  319. PhysClass *phys_obj = it2.Peek_Obj ();
  320. if (phys_obj != NULL) {
  321. RenderObjClass *model = phys_obj->Peek_Model ();
  322. if (model != NULL) {
  323. Process_Render_Obj (model);
  324. }
  325. }
  326. }
  327. return ;
  328. }
  329. /////////////////////////////////////////////////////////////////////////
  330. //
  331. // Process_Render_Obj
  332. //
  333. /////////////////////////////////////////////////////////////////////////
  334. void
  335. HeightDBClass::Process_Render_Obj (RenderObjClass *render_obj)
  336. {
  337. //
  338. // Loop through all the render objects sub-objects
  339. //
  340. int count = render_obj->Get_Num_Sub_Objects ();
  341. for (int index = 0; index < count; index ++) {
  342. // Get a pointer to this subobject
  343. RenderObjClass *sub_object = render_obj->Get_Sub_Object (index);
  344. if (sub_object != NULL) {
  345. Process_Render_Obj (sub_object);
  346. sub_object->Release_Ref ();
  347. }
  348. }
  349. //
  350. // Is this render object a mesh?
  351. //
  352. if ((render_obj->Class_ID () == RenderObjClass::CLASSID_MESH) &&
  353. (render_obj->Get_Collision_Type () & COLLISION_TYPE_PHYSICAL))
  354. {
  355. //
  356. // Pass this mesh off to the generator
  357. //
  358. MeshClass *mesh = static_cast<MeshClass *> (render_obj);
  359. Submit_Mesh (*mesh);
  360. }
  361. return ;
  362. }
  363. /////////////////////////////////////////////////////////////////////////
  364. //
  365. // Submit_Mesh
  366. //
  367. /////////////////////////////////////////////////////////////////////////
  368. void
  369. HeightDBClass::Submit_Mesh (MeshClass &mesh)
  370. {
  371. //
  372. // Get this mesh's polygon information
  373. //
  374. MeshModelClass *model = mesh.Get_Model ();
  375. if (model != NULL) {
  376. const Vector3 *vertex_array = model->Get_Vertex_Array ();
  377. int count = model->Get_Vertex_Count ();
  378. //
  379. // Loop over all the vertices in the mesh, looking for the
  380. // highest z-value in each cell of our grid
  381. //
  382. for (int index = 0; index < count; index ++) {
  383. Vector3 vertex = mesh.Get_Transform () * vertex_array[index];
  384. float percent_x = (vertex.X - m_LevelMin.X) / (m_LevelMax.X - m_LevelMin.X);
  385. float percent_y = (vertex.Y - m_LevelMin.Y) / (m_LevelMax.Y - m_LevelMin.Y);
  386. //
  387. // Is the position inside our data set?
  388. //
  389. if (percent_x >= 0 && percent_x <= 1.0F && percent_y >= 0 && percent_y <= 1.0F) {
  390. int entry_ul_x = (m_NumPointsX - 1) * percent_x;
  391. int entry_ul_y = (m_NumPointsY - 1) * percent_y;
  392. int entry_ur_x = entry_ul_x + 1;
  393. int entry_ur_y = entry_ul_y;
  394. int entry_lr_x = entry_ul_x + 1;
  395. int entry_lr_y = entry_ul_y + 1;
  396. int entry_ll_x = entry_ul_x;
  397. int entry_ll_y = entry_ul_y + 1;
  398. if (entry_ul_x + 1 >= m_NumPointsX) {
  399. entry_ur_x = entry_ul_x;
  400. entry_lr_x = entry_ul_x;
  401. }
  402. if (entry_ul_y + 1 >= m_NumPointsY) {
  403. entry_lr_y = entry_ul_y;
  404. entry_ll_y = entry_ul_y;
  405. }
  406. float *ul_entry = Get_Height_Entry (entry_ul_y, entry_ul_x);
  407. float *ur_entry = Get_Height_Entry (entry_ur_y, entry_ur_x);
  408. float *lr_entry = Get_Height_Entry (entry_lr_y, entry_lr_x);
  409. float *ll_entry = Get_Height_Entry (entry_ll_y, entry_ll_x);
  410. //
  411. // Now, move each of the corner entries up to this new z-value if necessary
  412. //
  413. (*ul_entry) = max (*ul_entry, vertex.Z + HEIGHT_OFFSET);
  414. (*ur_entry) = max (*ur_entry, vertex.Z + HEIGHT_OFFSET);
  415. (*lr_entry) = max (*lr_entry, vertex.Z + HEIGHT_OFFSET);
  416. (*ll_entry) = max (*ll_entry, vertex.Z + HEIGHT_OFFSET);
  417. }
  418. }
  419. }
  420. return ;
  421. }
  422. ///////////////////////////////////////////////////////////////////////
  423. //
  424. // Save
  425. //
  426. ///////////////////////////////////////////////////////////////////////
  427. bool
  428. HeightDBClass::Save (ChunkSaveClass &csave)
  429. {
  430. bool retval = true;
  431. //
  432. // Save the variables to their own chunk
  433. //
  434. csave.Begin_Chunk (CHUNKID_VARIABLES);
  435. WRITE_MICRO_CHUNK (csave, VARID_NUM_POINTS_X, m_NumPointsX);
  436. WRITE_MICRO_CHUNK (csave, VARID_NUM_POINTS_Y, m_NumPointsY);
  437. WRITE_MICRO_CHUNK (csave, VARID_PATCH_SIZE, m_PatchSize);
  438. WRITE_MICRO_CHUNK (csave, VARID_LEV_MIN, m_LevelMin);
  439. WRITE_MICRO_CHUNK (csave, VARID_LEV_MAX, m_LevelMax);
  440. csave.End_Chunk ();
  441. //
  442. // Write the height data to a chunk
  443. //
  444. csave.Begin_Chunk (CHUNKID_HEIGHT_ARRAY);
  445. int entries = m_NumPointsX * m_NumPointsY;
  446. csave.Write (m_HeightArray, entries * sizeof (float));
  447. csave.End_Chunk ();
  448. return retval;
  449. }
  450. ///////////////////////////////////////////////////////////////////////
  451. //
  452. // Load
  453. //
  454. ///////////////////////////////////////////////////////////////////////
  455. bool
  456. HeightDBClass::Load (ChunkLoadClass &cload)
  457. {
  458. Free_Data ();
  459. bool retval = true;
  460. while (cload.Open_Chunk ()) {
  461. switch (cload.Cur_Chunk_ID ()) {
  462. //
  463. // Read the height data from the chunk
  464. //
  465. case CHUNKID_HEIGHT_ARRAY:
  466. {
  467. //
  468. // Allocate enough entries to hold the height data
  469. //
  470. int entries = m_NumPointsX * m_NumPointsY;
  471. m_HeightArray = new float[entries];
  472. cload.Read (m_HeightArray, entries * sizeof (float));
  473. }
  474. break;
  475. case CHUNKID_VARIABLES:
  476. retval &= Load_Variables (cload);
  477. break;
  478. }
  479. cload.Close_Chunk ();
  480. }
  481. return retval;
  482. }
  483. ///////////////////////////////////////////////////////////////////////
  484. //
  485. // Load_Variables
  486. //
  487. ///////////////////////////////////////////////////////////////////////
  488. bool
  489. HeightDBClass::Load_Variables (ChunkLoadClass &cload)
  490. {
  491. bool retval = true;
  492. while (cload.Open_Micro_Chunk ()) {
  493. switch (cload.Cur_Micro_Chunk_ID ()) {
  494. READ_MICRO_CHUNK (cload, VARID_NUM_POINTS_X, m_NumPointsX);
  495. READ_MICRO_CHUNK (cload, VARID_NUM_POINTS_Y, m_NumPointsY);
  496. READ_MICRO_CHUNK (cload, VARID_PATCH_SIZE, m_PatchSize);
  497. READ_MICRO_CHUNK (cload, VARID_LEV_MIN, m_LevelMin);
  498. READ_MICRO_CHUNK (cload, VARID_LEV_MAX, m_LevelMax);
  499. }
  500. cload.Close_Micro_Chunk ();
  501. }
  502. return retval;
  503. }