PathfindSectorBuilder.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  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/PathfindSectorBuilder.cpp $Revision:: 2 $*
  25. * *
  26. *---------------------------------------------------------------------------------------------*
  27. * Functions: *
  28. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  29. #include "stdafx.h"
  30. #include "pathfindsectorbuilder.h"
  31. #include "phys3.h"
  32. #include "filemgr.h"
  33. #include "editorassetmgr.h"
  34. #include "_assetmgr.h"
  35. #include "humanphys.h"
  36. #include "utils.h"
  37. #include "editorphys.h"
  38. #include "collisiongroups.h"
  39. #include "sceneeditor.h"
  40. #include "boxrobj.h"
  41. #include "pathfind.h"
  42. #include "systimer.h"
  43. //////////////////////////////////////////////////////////////////////////
  44. // Static member initialization
  45. //////////////////////////////////////////////////////////////////////////
  46. BodyBoxCullObj *BodyBoxCullObj::m_First = NULL;
  47. //////////////////////////////////////////////////////////////////////////
  48. // Local constants
  49. //////////////////////////////////////////////////////////////////////////
  50. class SimDirInfoClass
  51. {
  52. public:
  53. SimDirInfoClass (void) { }
  54. SimDirInfoClass (float _heading, float _speed, const Vector3 &_move, const Vector3 &min, const Vector3 &max)
  55. : heading (_heading), speed (_speed), move (_move), min_move (min), max_move (max) { }
  56. float heading;
  57. float speed;
  58. Vector3 move;
  59. Vector3 min_move;
  60. Vector3 max_move;
  61. };
  62. /*const SIM_DIR_INFO DIR_INFO[DIR_MAX] =
  63. {
  64. SIM_DIR_INFO ( 0.0F, Vector3 ( 0.9F, -0.1F, -1.5F), Vector3 ( 2.0F, 0.1F, 1.5F) ),
  65. SIM_DIR_INFO ( (float)DEG_TO_RAD (90), Vector3 (-0.1F, 0.9F, -1.5F), Vector3 ( 0.1F, 2.0F, 1.5F) ),
  66. SIM_DIR_INFO ( (float)DEG_TO_RAD (180), Vector3 (-2.0F, -0.1F, -1.5F), Vector3 (-0.9F, 0.1F, 1.5F) ),
  67. SIM_DIR_INFO ( (float)DEG_TO_RAD (270), Vector3 (-0.1F, -2.0F, -1.5F), Vector3 ( 0.1F, -0.9F, 1.5F) )
  68. };*/
  69. //
  70. // Note: The vector members are in "percentage of bounding box" units.
  71. //
  72. static const SimDirInfoClass DIR_INFO[DIR_MAX] =
  73. {
  74. SimDirInfoClass ( 0.0F, 0, Vector3 ( 1.0F, 0, 0), Vector3 ( 0.95F, -0.05F, -2.0F), Vector3 ( 1.2F, 0.05F, 2.0F) ),
  75. SimDirInfoClass ( (float)DEG_TO_RAD (90), 0, Vector3 ( 0, 1.0F, 0), Vector3 (-0.05F, 0.95F, -2.0F), Vector3 ( 0.05F, 1.2F, 2.0F) ),
  76. SimDirInfoClass ( (float)DEG_TO_RAD (180), 0, Vector3 (-1.0F, 0, 0), Vector3 ( -1.2F, -0.05F, -2.0F), Vector3 (-0.95F, 0.05F, 2.0F) ),
  77. SimDirInfoClass ( (float)DEG_TO_RAD (270), 0, Vector3 ( 0, -1.0F, 0), Vector3 (-0.05F, -1.2F, -2.0F), Vector3 ( 0.05F, -0.95F, 2.0F) )
  78. };
  79. //////////////////////////////////////////////////////////////////////////
  80. //
  81. // PathfindSectorBuilderClass
  82. //
  83. //////////////////////////////////////////////////////////////////////////
  84. PathfindSectorBuilderClass::PathfindSectorBuilderClass (void)
  85. : m_PhysicsSim (NULL),
  86. m_RecurseLevel (0),
  87. m_RepartitionCount (0),
  88. m_CurrentSector (NULL),
  89. m_SimBoundingBox (Vector3 (0.5F, 0.5F, 1.8F)),
  90. m_DirInfo (NULL),
  91. m_bCancel (false)
  92. {
  93. //
  94. // Create an instance of the commando render object we can
  95. // use to simulate character movement
  96. //
  97. CString full_path;
  98. full_path = Get_File_Mgr ()->Make_Full_Path ("Characters\\Commando");
  99. _pThe3DAssetManager->Set_Current_Directory (full_path);
  100. RenderObjClass *commando_obj = WW3DAssetManager::Get_Instance()->Create_Render_Obj ("COMMANDO");
  101. if (commando_obj != NULL) {
  102. //
  103. // Attempt to find the collision box for this physics object
  104. //
  105. RenderObjClass *collision_box = NULL;
  106. if (commando_obj->Class_ID () == RenderObjClass::CLASSID_DISTLOD) {
  107. RenderObjClass *lod0 = commando_obj->Get_Sub_Object(0);
  108. collision_box = lod0->Get_Sub_Object_By_Name ("WORLDBOX");
  109. MEMBER_RELEASE (lod0);
  110. } else {
  111. collision_box = commando_obj->Get_Sub_Object_By_Name ("WORLDBOX");
  112. }
  113. //
  114. // Store the exents of the collision box for use in our simulation
  115. //
  116. if (collision_box != NULL) {
  117. const AABoxClass &box = collision_box->Get_Bounding_Box ();
  118. m_SimBoundingBox = box.Extent * 2;
  119. MEMBER_RELEASE (collision_box);
  120. }
  121. //
  122. // Build an array of simulation information (specific to this
  123. // physics object).
  124. //
  125. m_DirInfo = new SimDirInfoClass[DIR_MAX];
  126. for (int index = 0; index < DIR_MAX; index ++) {
  127. m_DirInfo[index] = DIR_INFO[index];
  128. m_DirInfo[index].move.X *= m_SimBoundingBox.X;
  129. m_DirInfo[index].move.Y *= m_SimBoundingBox.Y;
  130. m_DirInfo[index].move.Z *= m_SimBoundingBox.Z;
  131. m_DirInfo[index].min_move.X *= m_SimBoundingBox.X;
  132. m_DirInfo[index].min_move.Y *= m_SimBoundingBox.Y;
  133. m_DirInfo[index].min_move.Z *= m_SimBoundingBox.Z;
  134. m_DirInfo[index].max_move.X *= m_SimBoundingBox.X;
  135. m_DirInfo[index].max_move.Y *= m_SimBoundingBox.Y;
  136. m_DirInfo[index].max_move.Z *= m_SimBoundingBox.Z;
  137. m_DirInfo[index].speed = m_DirInfo[index].move.Length ();
  138. }
  139. //
  140. // Create the physics object we will use for character simulation
  141. //
  142. m_PhysicsSim = new HumanPhysClass;
  143. m_PhysicsSim->Set_Model (commando_obj);
  144. m_PhysicsSim->Set_Controller (&m_Controller);
  145. m_PhysicsSim->Set_Collision_Group (0);
  146. MEMBER_RELEASE (commando_obj);
  147. //
  148. // Setup the culling system for the boxes.
  149. //
  150. float max_extent = max (m_SimBoundingBox.X, m_SimBoundingBox.Y);
  151. max_extent = max (m_SimBoundingBox.Z, max_extent);
  152. Vector3 min;
  153. Vector3 max;
  154. PhysicsSceneClass::Get_Instance ()->Get_Level_Extents (min, max);
  155. m_BodyBoxCullingSystem.Re_Partition (min, max, max_extent * 5);
  156. }
  157. return ;
  158. }
  159. //////////////////////////////////////////////////////////////////////////
  160. //
  161. // ~PathfindSectorBuilderClass
  162. //
  163. //////////////////////////////////////////////////////////////////////////
  164. PathfindSectorBuilderClass::~PathfindSectorBuilderClass (void)
  165. {
  166. //MEMBER_RELEASE (m_PhysicsSim);
  167. Free_Sectors ();
  168. SAFE_DELETE_ARRAY (m_DirInfo);
  169. return ;
  170. }
  171. //////////////////////////////////////////////////////////////////////////
  172. //
  173. // Generate_Sectors
  174. //
  175. //////////////////////////////////////////////////////////////////////////
  176. void
  177. PathfindSectorBuilderClass::Generate_Sectors (const Vector3 &start_pos)
  178. {
  179. bool sectors_displayed = PathfindClass::Get_Instance ()->Are_Sectors_Displayed ();
  180. bool portals_displayed = PathfindClass::Get_Instance ()->Are_Portals_Displayed ();
  181. PathfindClass::Get_Instance ()->Display_Sectors (false);
  182. PathfindClass::Get_Instance ()->Display_Portals (false);
  183. //
  184. // Start fresh
  185. //
  186. Free_Sectors ();
  187. PathfindClass::Get_Instance ()->Reset_Sectors ();
  188. Import_Raw_Data ();
  189. m_RecurseLevel = 0;
  190. m_RepartitionCount = 0;
  191. m_bCancel = false;
  192. //
  193. // Normalize the starting position. We do this so that we can start as many
  194. // different 'generations' in the world and they will all match up perfectly.
  195. //
  196. Vector3 normalized_start_pos = start_pos;
  197. normalized_start_pos.X = (int(start_pos.X / m_SimBoundingBox.X)) * m_SimBoundingBox.X;
  198. normalized_start_pos.Y = (int(start_pos.Y / m_SimBoundingBox.Y)) * m_SimBoundingBox.Y;
  199. //
  200. // Start flood filling from the given position
  201. //
  202. m_CurrentSector = Mark_Sector (normalized_start_pos);
  203. Floodfill (normalized_start_pos);
  204. DWORD start_ticks = TIMEGETTIME ();
  205. //
  206. // Process all the sectors in our queue
  207. //
  208. while (m_FloodFillProcessList.Count () > 0) {
  209. m_CurrentSector = m_FloodFillProcessList[0];
  210. m_FloodFillProcessList.Delete (0);
  211. Floodfill (m_CurrentSector->Get_Transform ().Get_Translation ());
  212. m_bCancel = ((TIMEGETTIME () - start_ticks) > 20000000L);
  213. }
  214. //
  215. // Compress the sectors into the largest possible rectangular regions
  216. //
  217. DWORD before_ticks = TIMEGETTIME ();
  218. Compress_Sectors ();
  219. DWORD after_ticks = TIMEGETTIME ();
  220. CString message;
  221. message.Format ("Time spent compressing: %d secs.\r\n", (after_ticks-before_ticks)/1000);
  222. ::Ouput_Message (message);
  223. //
  224. // Add the sectors to our global pathfind object
  225. //
  226. for (int index = 0; index < m_SectorList.Count (); index ++) {
  227. PathfindSectorClass *sector = m_SectorList[index];
  228. if (sector->Is_Valid ()) {
  229. PathfindClass::Get_Instance ()->Add_Sector (sector);
  230. }
  231. }
  232. //
  233. // Turn the debug display back on if necessary
  234. //
  235. PathfindClass::Get_Instance ()->Display_Sectors (sectors_displayed);
  236. PathfindClass::Get_Instance ()->Display_Portals (portals_displayed);
  237. return ;
  238. }
  239. float Re_Orient_Vector (const Vector3 &pos, const Vector3 &look_at)
  240. {
  241. float dx = (look_at[0] - pos[0]);
  242. float dy = (look_at[1] - pos[1]);
  243. float len = (float)sqrt(dx*dx + dy*dy);
  244. float angle = 0;
  245. if (fabs (dx) >= fabs(dy)) {
  246. double cosy = 0;
  247. if (len != 0.0f) {
  248. cosy = dx/len;
  249. } else {
  250. cosy = 1.0f;
  251. }
  252. angle = ::acos (cosy);
  253. } else {
  254. double siny = 0;
  255. if (len != 0.0f) {
  256. siny = dy/len;
  257. } else {
  258. siny = 0.0f;
  259. }
  260. angle = ::asin (siny);
  261. }
  262. return angle;
  263. }
  264. //////////////////////////////////////////////////////////////////////////
  265. //
  266. // Do_Physics_Sim
  267. //
  268. //////////////////////////////////////////////////////////////////////////
  269. void
  270. PathfindSectorBuilderClass::Do_Physics_Sim
  271. (
  272. const Vector3 & start_pos,
  273. PATHFIND_DIR direction
  274. )
  275. {
  276. //
  277. // Position the physics object at the test location
  278. //
  279. Matrix3D transform (start_pos);
  280. m_PhysicsSim->Set_Transform (transform);
  281. //
  282. // Determine how far the simulation can move in each of the 3 directions
  283. // and still be considered 'valid'.
  284. //
  285. Vector3 expected_move = m_DirInfo[direction].move;
  286. Vector3 min_exceptable_move = m_DirInfo[direction].min_move;
  287. Vector3 max_exceptable_move = m_DirInfo[direction].max_move;
  288. //
  289. // Set-up the simulation to move one 'step' in the given direction
  290. //
  291. m_Controller.Reset ();
  292. m_Controller.Set_Move_Forward (1);
  293. m_PhysicsSim->Set_Normalized_Speed (m_DirInfo[direction].speed);
  294. m_PhysicsSim->Set_Heading (m_DirInfo[direction].heading);
  295. Vector3 expected_pos = start_pos + expected_move;
  296. //
  297. // Do the simulation
  298. //
  299. Vector3 new_pos;
  300. Vector3 move_vector;
  301. bool found = false;
  302. bool on_ground = false;
  303. #if 0
  304. bool should_continue = true;
  305. for (int attempt = 0; (attempt < 50) && should_continue; attempt ++) {
  306. //
  307. // Break the physics timestep down into small steps (however
  308. // make sure it processes for exactly 1 sec).
  309. //
  310. for (int index = 0; index < 2; index ++) {
  311. m_PhysicsSim->Timestep (0.05F);
  312. }
  313. new_pos = m_PhysicsSim->Get_Position ();
  314. move_vector = new_pos - start_pos;
  315. //
  316. // Did we move into the expected range?
  317. //
  318. if ((move_vector.X >= min_exceptable_move.X) &&
  319. (move_vector.Y >= min_exceptable_move.Y) &&
  320. (move_vector.Z >= min_exceptable_move.Z) &&
  321. (move_vector.X <= max_exceptable_move.X) &&
  322. (move_vector.Y <= max_exceptable_move.Y) &&
  323. (move_vector.Z <= max_exceptable_move.Z))
  324. {
  325. should_continue = false;
  326. found = true;
  327. } else {
  328. float angle = Re_Orient_Vector (new_pos, expected_pos);
  329. m_PhysicsSim->Set_Heading (angle);
  330. }
  331. }
  332. on_ground = m_PhysicsSim->Is_In_Contact ();
  333. #else
  334. float half_height = m_SimBoundingBox.Z * 0.5F;
  335. float movement = (4.9F + (m_SimBoundingBox.Z * 0.15F));
  336. //m_PhysicsSim->Set_Transform (Matrix3D (expected_pos));
  337. AABoxClass box;
  338. box.Center = expected_pos;
  339. box.Center.Z += (m_SimBoundingBox.Z * 0.75F);
  340. box.Extent.X = m_SimBoundingBox.X * 0.5F;
  341. box.Extent.Y = m_SimBoundingBox.Y * 0.5F;
  342. box.Extent.Z = half_height;
  343. CastResultStruct result;
  344. PhysAABoxCollisionTestClass test( box,
  345. Vector3 (0, 0, -movement),
  346. &result,
  347. m_PhysicsSim->Get_Collision_Group (),
  348. COLLISION_TYPE_PHYSICAL);
  349. PhysicsSceneClass::Get_Instance ()->Cast_AABox (test);
  350. found = (result.StartBad == false) && (result.Normal.Z > 0.7F);
  351. if (found) {
  352. on_ground = (result.Fraction < 1.0F);
  353. new_pos.Z = box.Center.Z - half_height - (movement * result.Fraction) + 0.001F;
  354. } else {
  355. int test = 0;
  356. }
  357. #endif
  358. if (found && on_ground) {
  359. //
  360. // Force the x and y values to snap to the expected position
  361. //
  362. new_pos.X = start_pos.X + expected_move.X;
  363. new_pos.Y = start_pos.Y + expected_move.Y;
  364. //
  365. // Is this cell already marked?
  366. //
  367. BodyBoxCullObj *occupant = Get_Sector_Occupant (new_pos);
  368. if (occupant == NULL) {
  369. //
  370. // Mark this cell and add it to the list of sectors
  371. // that need to be recursed.
  372. //
  373. occupant = Mark_Sector (new_pos);
  374. m_FloodFillProcessList.Add (occupant);
  375. }
  376. //
  377. // Let the sector know who its new neighbor is
  378. //
  379. if (occupant != m_CurrentSector) {
  380. m_CurrentSector->Set_Neighbor (direction, occupant);
  381. }
  382. }
  383. return ;
  384. }
  385. //////////////////////////////////////////////////////////////////////////
  386. //
  387. // Floodfill
  388. //
  389. //////////////////////////////////////////////////////////////////////////
  390. void
  391. PathfindSectorBuilderClass::Floodfill (const Vector3 &start_pos)
  392. {
  393. //
  394. // Do a simulation in each of the four directions from the
  395. // current cell.
  396. //
  397. if (m_bCancel == false) {
  398. Do_Physics_Sim (start_pos, DIR_FORWARD);
  399. Do_Physics_Sim (start_pos, DIR_LEFT);
  400. Do_Physics_Sim (start_pos, DIR_BACKWARD);
  401. Do_Physics_Sim (start_pos, DIR_RIGHT);
  402. //
  403. // Let the current sector 'know' its been completely processed
  404. //
  405. m_CurrentSector->Needs_Processing (false);
  406. }
  407. //
  408. // Allow the user to cancel this operation
  409. //
  410. if (!m_bCancel && (::GetAsyncKeyState (VK_ESCAPE) < 0)) {
  411. m_bCancel = true;
  412. ::MessageBox (NULL, "Operation cancelled.", "Pathfind", MB_OK | MB_SETFOREGROUND);
  413. }
  414. return ;
  415. }
  416. //////////////////////////////////////////////////////////////////////////
  417. //
  418. // Get_Sector_Occupant
  419. //
  420. //////////////////////////////////////////////////////////////////////////
  421. BodyBoxCullObj *
  422. PathfindSectorBuilderClass::Get_Sector_Occupant (const Vector3 &pos)
  423. {
  424. Vector3 box_center_pos = pos;
  425. //box_center_pos.Z += (m_SimBoundingBox.Z * 0.1F);
  426. //
  427. // Collect all the objects in our culling system that occupy the
  428. // given point
  429. //
  430. m_BodyBoxCullingSystem.Reset_Collection ();
  431. m_BodyBoxCullingSystem.Collect_Objects (box_center_pos);
  432. BodyBoxCullObj *body_box = m_BodyBoxCullingSystem.Get_First_Collected_Object ();
  433. if (body_box == NULL) {
  434. box_center_pos = pos;
  435. box_center_pos.Z += (m_SimBoundingBox.Z * 0.25F);
  436. m_BodyBoxCullingSystem.Reset_Collection ();
  437. m_BodyBoxCullingSystem.Collect_Objects (box_center_pos);
  438. body_box = m_BodyBoxCullingSystem.Get_First_Collected_Object ();
  439. }
  440. if (body_box == NULL) {
  441. box_center_pos = pos;
  442. box_center_pos.Z += (m_SimBoundingBox.Z * 0.5F);
  443. m_BodyBoxCullingSystem.Reset_Collection ();
  444. m_BodyBoxCullingSystem.Collect_Objects (box_center_pos);
  445. body_box = m_BodyBoxCullingSystem.Get_First_Collected_Object ();
  446. }
  447. //
  448. // If we found any objects in the culling system, then the sector is occupied
  449. //
  450. return body_box;
  451. }
  452. //////////////////////////////////////////////////////////////////////////
  453. //
  454. // Mark_Sector
  455. //
  456. //////////////////////////////////////////////////////////////////////////
  457. void
  458. PathfindSectorBuilderClass::Mark_Sector (BodyBoxCullObj *body_box)
  459. {
  460. //
  461. // Add the culling object to our AABTree culling system. (This
  462. // effectively marks the cell as 'occupied').
  463. //
  464. body_box->Add_Ref ();
  465. m_BodyBoxCullingSystem.Add_Object (body_box);
  466. BodyBoxCullObj::Add (body_box);
  467. //
  468. // Repartition the culling system every few hundred
  469. // entries.
  470. //
  471. /*m_RepartitionCount ++;
  472. if (m_RepartitionCount > 1000) {
  473. m_RepartitionCount = 0;
  474. m_BodyBoxCullingSystem.Re_Partition ();
  475. }*/
  476. return ;
  477. }
  478. //////////////////////////////////////////////////////////////////////////
  479. //
  480. // Mark_Sector
  481. //
  482. //////////////////////////////////////////////////////////////////////////
  483. BodyBoxCullObj *
  484. PathfindSectorBuilderClass::Mark_Sector (const Vector3 &pos)
  485. {
  486. //
  487. // Create a culling system object to represent this 'sector' and
  488. // add it to our AABTree culling system. (This effectively marks
  489. // the cell as 'occupied').
  490. //
  491. BodyBoxCullObj *body_box = new BodyBoxCullObj;
  492. AABoxClass box;
  493. box.Center = pos;
  494. box.Extent.X = m_SimBoundingBox.X * 0.5F;
  495. box.Extent.Y = m_SimBoundingBox.Y * 0.5F;
  496. box.Extent.Z = m_SimBoundingBox.Z * 0.5F;
  497. body_box->Set_Bounding_Box (box);
  498. body_box->Init_Transform (pos);
  499. Mark_Sector (body_box);
  500. body_box->Release_Ref ();
  501. return body_box;
  502. }
  503. //////////////////////////////////////////////////////////////////////////
  504. //
  505. // Compress_Sectors
  506. //
  507. //////////////////////////////////////////////////////////////////////////
  508. void
  509. PathfindSectorBuilderClass::Compress_Sectors (void)
  510. {
  511. BODY_BOX_LIST body_box_list;
  512. int sectors = 0;
  513. int largest_area = 0;
  514. int largest_area_right = 0;
  515. int largest_area_down = 0;
  516. BodyBoxCullObj *upper_left_ptr = NULL;
  517. //
  518. // Keep looping until we've compressed all the body boxes into sectors
  519. //
  520. while (BodyBoxCullObj::Get_First () != NULL) {
  521. //
  522. // Find the largest possible rectangular area that's 'free'
  523. // in the world.
  524. //
  525. largest_area = 0;
  526. for ( BodyBoxCullObj *cull_obj = BodyBoxCullObj::Get_First ();
  527. cull_obj != NULL;
  528. cull_obj = cull_obj->Get_Next ())
  529. {
  530. int max_down = 0;
  531. int max_right = 0;
  532. int max_area = 0;
  533. int curr_down = 65536;
  534. int curr_right = 1;
  535. //
  536. // Find the largest rectangular area that this box is the upper-left
  537. // corner for.
  538. //
  539. BodyBoxCullObj *neighbor = cull_obj;
  540. Vector3 pos = cull_obj->Get_Position ();
  541. float min_z_pos = pos.Z - m_SimBoundingBox.Z;
  542. float max_z_pos = pos.Z + (m_SimBoundingBox.Z * 1.5F);
  543. while (neighbor != NULL)
  544. {
  545. curr_down = min (neighbor->Get_Count (DIR_BACKWARD, min_z_pos, max_z_pos) + 1, curr_down);
  546. //
  547. // Determine if this region (right x down) contains the
  548. // largest area so far.
  549. //
  550. int area = (curr_down * curr_right);
  551. if (area > max_area) {
  552. max_area = area;
  553. max_down = curr_down;
  554. max_right = curr_right;
  555. }
  556. //
  557. // Move to the next cell to the right
  558. //
  559. curr_right ++;
  560. neighbor = neighbor->Next_Valid (DIR_RIGHT, min_z_pos, max_z_pos);
  561. }
  562. if (max_area > largest_area) {
  563. upper_left_ptr = cull_obj;
  564. largest_area = max_area;
  565. largest_area_right = max_right;
  566. largest_area_down = max_down;
  567. }
  568. }
  569. //
  570. // Debug info
  571. //
  572. if (largest_area > 1) {
  573. CString message;
  574. message.Format ("Combining cells: width = %d, height = %d, area = %d\r\n", largest_area_right, largest_area_down, largest_area);
  575. ::Ouput_Message (message);
  576. sectors ++;
  577. }
  578. Vector3 min_point (100000, 100000, 100000);
  579. Vector3 max_point (-100000, -100000, -100000);
  580. PathfindSectorClass *sector = new PathfindSectorClass;
  581. //
  582. // Remove all the cells that compose this area from the list
  583. //
  584. BodyBoxCullObj *curr_cell = upper_left_ptr;
  585. for (int right = 0; (right < largest_area_right) && (curr_cell != NULL); right ++) {
  586. BodyBoxCullObj *down_obj = curr_cell;
  587. for (int down = 0; (down < largest_area_down) && (down_obj != NULL); down ++) {
  588. Matrix3D transform = down_obj->Get_Transform ();
  589. Vector3 position = transform.Get_Translation ();
  590. min_point.X = min (min_point.X, position.X - (m_SimBoundingBox.X / 2));
  591. min_point.Y = min (min_point.Y, position.Y - (m_SimBoundingBox.Y / 2));
  592. min_point.Z = min (min_point.Z, position.Z);
  593. max_point.X = max (max_point.X, position.X + (m_SimBoundingBox.X / 2));
  594. max_point.Y = max (max_point.Y, position.Y + (m_SimBoundingBox.Y / 2));
  595. max_point.Z = max (max_point.Z, position.Z + m_SimBoundingBox.Z);
  596. ASSERT (down_obj->Is_Taken () == false);
  597. down_obj->Set_Sector (sector);
  598. body_box_list.Add (down_obj);
  599. down_obj->Remove ();
  600. down_obj->Set_Taken ();
  601. down_obj = down_obj->Peek_Neighbor (DIR_BACKWARD);
  602. }
  603. curr_cell = curr_cell->Peek_Neighbor (DIR_RIGHT);
  604. }
  605. //
  606. // Sumbit this sector
  607. //
  608. if (largest_area > 1) {
  609. AABoxClass bounding_box;
  610. bounding_box.Center = min_point + ((max_point - min_point) / 2.0F);
  611. bounding_box.Extent.X = (max_point.X - min_point.X) / 2;
  612. bounding_box.Extent.Y = (max_point.Y - min_point.Y) / 2;
  613. bounding_box.Extent.Z = (max_point.Z - min_point.Z) / 2;
  614. sector->Set_Bounding_Box (bounding_box);
  615. m_SectorList.Add (sector);
  616. } else {
  617. sector->Set_Valid (false);
  618. m_SectorList.Add (sector);
  619. }
  620. }
  621. //
  622. // Build portals
  623. //
  624. for (int index = 0; index < body_box_list.Count (); index ++) {
  625. BodyBoxCullObj *cull_obj = body_box_list[index];
  626. PathfindSectorClass *sector = cull_obj->Peek_Sector ();
  627. if (sector->Is_Valid ()) {
  628. //
  629. // Make portals out of this box for any of the four directions
  630. //
  631. for (int dir = 0; dir < DIR_MAX; dir ++) {
  632. if (cull_obj->Is_New_Portal ((PATHFIND_DIR)dir)) {
  633. PathfindPortalClass *portal = cull_obj->Make_Portal ((PATHFIND_DIR)dir, m_SimBoundingBox);
  634. sector->Add_Portal (portal);
  635. }
  636. }
  637. }
  638. }
  639. //
  640. // Free the body box culling objects
  641. //
  642. for (index = 0; index < body_box_list.Count (); index ++) {
  643. //
  644. // Remove the body box from the culling system
  645. //
  646. BodyBoxCullObj *cull_obj = body_box_list[index];
  647. m_BodyBoxCullingSystem.Remove_Object (cull_obj);
  648. //
  649. // Save the body box in the global list so we can combine
  650. // generated sections later (if we want). Also usefull
  651. // for debugging purposes.
  652. //
  653. cull_obj->Reset_Portal_Info ();
  654. cull_obj->Set_Taken (false);
  655. cull_obj->Set_Sector (NULL);
  656. PathfindClass::Get_Instance ()->Add_Body_Box (cull_obj);
  657. MEMBER_RELEASE (cull_obj);
  658. }
  659. body_box_list.Delete_All ();
  660. //
  661. // Debug info
  662. //
  663. CString message;
  664. message.Format ("Total sectors: %d\r\n", sectors);
  665. ::Ouput_Message (message);
  666. return ;
  667. }
  668. ///////////////////////////////////////////////////////////////////////////
  669. //
  670. // Free_Sectors
  671. //
  672. ///////////////////////////////////////////////////////////////////////////
  673. void
  674. PathfindSectorBuilderClass::Free_Sectors (void)
  675. {
  676. //
  677. // Free all the sector objects
  678. //
  679. for (int index = 0; index < m_SectorList.Count (); index ++) {
  680. PathfindSectorClass *sector = m_SectorList[index];
  681. MEMBER_RELEASE (sector);
  682. }
  683. m_SectorList.Delete_All ();
  684. return ;
  685. }
  686. ///////////////////////////////////////////////////////////////////////////
  687. //
  688. // Import_Raw_Data
  689. //
  690. ///////////////////////////////////////////////////////////////////////////
  691. void
  692. PathfindSectorBuilderClass::Import_Raw_Data (void)
  693. {
  694. BODY_BOX_LIST &raw_list = PathfindClass::Get_Instance ()->Get_Raw_Data ();
  695. //
  696. // Add all the body boxes from a previous build to the system.
  697. //
  698. for (int index = 0; index < raw_list.Count (); index ++) {
  699. BodyBoxCullObj *body_box = raw_list[index];
  700. Mark_Sector (body_box);
  701. //
  702. // Add this box to our process queue if it wasn't processed
  703. // the first time around.
  704. //
  705. if (body_box->Needs_Processing ()) {
  706. m_FloodFillProcessList.Add (body_box);
  707. }
  708. }
  709. PathfindClass::Get_Instance ()->Reset_Generated_Data ();
  710. return ;
  711. }