Pathfind.cpp 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994
  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/Pathfind.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 10/15/01 7:44p $*
  29. * *
  30. * $Revision:: 35 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "pathfind.h"
  36. #include "pathfindportal.h"
  37. #include "path.h"
  38. #include "pscene.h"
  39. #include "boxrobj.h"
  40. #include "decophys.h"
  41. #include "mesh.h"
  42. #include "assetmgr.h"
  43. #include "chunkio.h"
  44. #include "wwphysids.h"
  45. #include "pathdebugplotter.h"
  46. #include "persistfactory.h"
  47. #include "waypath.h"
  48. #include "waypoint.h"
  49. #include "wwmemlog.h"
  50. #include "heightdb.h"
  51. #include "colmathaabox.h"
  52. ///////////////////////////////////////////////////////////////////////////
  53. // Local prototypes
  54. ///////////////////////////////////////////////////////////////////////////
  55. int __cdecl fnCompareWaypathPortalsCallback (const void *elem1, const void *elem2);
  56. bool Find_Intersection_Point (const AABoxClass &box, const Vector3 &p0, const Vector3 &p1, float *percent, Vector3 *intersection_point);
  57. void Add_New_Portal_To_List (DynamicVectorClass<PathfindWaypathPortalClass *> &portal_list, PathfindSectorClass *dest_sector1, PathfindSectorClass *dest_sector2, int waypath_id, int waypoint_index, const Vector3 &portal_pos, float percent);
  58. ///////////////////////////////////////////////////////////////////////////
  59. // Constants
  60. ///////////////////////////////////////////////////////////////////////////
  61. const int MAX_TEMP_PORTALS = 20;
  62. ///////////////////////////////////////////////////////////////////////////
  63. // Static data initialization
  64. ///////////////////////////////////////////////////////////////////////////
  65. static int _NextTempPortalID = PathfindClass::TEMP_PORTAL_ID_START;
  66. ///////////////////////////////////////////////////////////////////////////
  67. // Save/Load stuff
  68. ///////////////////////////////////////////////////////////////////////////
  69. enum
  70. {
  71. CHUNKID_DATABASE = 0x01060635,
  72. XXX_CHUNKID_SECTOR,
  73. CHUNKID_PORTAL,
  74. CHUNKID_SECTOR_CULLING_SYSTEM,
  75. CHUNKID_SECTOR,
  76. CHUNKID_SECTOR_CULL_TREE,
  77. CHUNKID_SECTOR_LINKAGE,
  78. CHUNKID_SECTOR_OBJECT,
  79. CHUNKID_HEIGHTDB,
  80. CHUNKID_ACTION_PORTAL,
  81. CHUNKID_WAYPATH_PORTAL,
  82. CHUNKID_PATHFIND_SECTOR_OBJECT
  83. };
  84. ///////////////////////////////////////////////////////////////////////////
  85. // Static member initialization
  86. ///////////////////////////////////////////////////////////////////////////
  87. PathfindClass *PathfindClass::_Pathfinder = NULL;
  88. int PathfindClass::_MemoryFootprint = 0;
  89. ///////////////////////////////////////////////////////////////////////////
  90. //
  91. // PathfindClass
  92. //
  93. ///////////////////////////////////////////////////////////////////////////
  94. PathfindClass::PathfindClass (void)
  95. : m_SectorsDisplayed (false),
  96. m_PortalsDisplayed (false),
  97. m_Plotter (NULL),
  98. m_SectorList(1000), // 1000's of these
  99. m_PortalList(5000), // 10.000's of these
  100. m_SectorDisplayList(1000), // 1000's of these (debug only)
  101. m_WaypathList(30) // 5-30 of these?
  102. {
  103. WWASSERT (_Pathfinder == NULL);
  104. _Pathfinder = this;
  105. // Set good estimates of growth steps
  106. m_SectorList.Set_Growth_Step(500);
  107. m_PortalList.Set_Growth_Step(2500);
  108. m_SectorDisplayList.Set_Growth_Step(500);
  109. m_WaypathList.Set_Growth_Step(10);
  110. m_Plotter = new PathDebugPlotterClass;
  111. //
  112. // Initialize the height database
  113. //
  114. HeightDBClass::Initialize ();
  115. return ;
  116. }
  117. ///////////////////////////////////////////////////////////////////////////
  118. //
  119. // ~PathfindClass
  120. //
  121. ///////////////////////////////////////////////////////////////////////////
  122. PathfindClass::~PathfindClass (void)
  123. {
  124. Reset_Sectors ();
  125. Reset_Portals ();
  126. Reset_Waypaths ();
  127. _Pathfinder = NULL;
  128. REF_PTR_RELEASE (m_Plotter);
  129. //
  130. // Free the height database
  131. //
  132. HeightDBClass::Shutdown ();
  133. return ;
  134. }
  135. ///////////////////////////////////////////////////////////////////////////
  136. //
  137. // Add_Sector
  138. //
  139. ///////////////////////////////////////////////////////////////////////////
  140. void
  141. PathfindClass::Add_Sector (PathfindSectorClass *sector, bool add_to_tree)
  142. {
  143. WWASSERT (sector != NULL);
  144. if (sector != NULL) {
  145. sector->Add_Ref ();
  146. //
  147. // Add this sector to the culling system (if needs be)
  148. //
  149. if (add_to_tree) {
  150. m_SectorTree.Add_Object (sector);
  151. }
  152. //
  153. // Add this sector to our linear list (for housekeeping)
  154. //
  155. m_SectorList.Add (sector);
  156. //
  157. // For debugging purposes, add this object to our
  158. // memory footprint counter.
  159. //
  160. _MemoryFootprint += sizeof (PathfindSectorClass);
  161. }
  162. return ;
  163. }
  164. ///////////////////////////////////////////////////////////////////////////
  165. //
  166. // Add_Portal
  167. //
  168. ///////////////////////////////////////////////////////////////////////////
  169. int
  170. PathfindClass::Add_Portal (PathfindPortalClass *portal)
  171. {
  172. //
  173. // Add this portal to the housekeeping list
  174. //
  175. portal->Add_Ref ();
  176. m_PortalList.Add (portal);
  177. //
  178. // Assign the portal a unique ID
  179. //
  180. int portal_id = m_PortalList.Count () - 1;
  181. portal->Set_ID (portal_id);
  182. //
  183. // Add the size of this portal to the pool.
  184. //
  185. _MemoryFootprint += sizeof (PathfindPortalClass);
  186. return portal_id;
  187. }
  188. ///////////////////////////////////////////////////////////////////////////
  189. //
  190. // Add_Waypath_Portal
  191. //
  192. ///////////////////////////////////////////////////////////////////////////
  193. int
  194. PathfindClass::Add_Waypath_Portal (PathfindWaypathPortalClass *portal)
  195. {
  196. //
  197. // Add this portal to the housekeeping list
  198. //
  199. portal->Add_Ref ();
  200. m_WaypathPortalList.Add (portal);
  201. //
  202. // Assign the portal a unique ID
  203. //
  204. int portal_id = (m_WaypathPortalList.Count () - 1) + WAYPATH_PORTAL_ID_START;
  205. portal->Set_ID (portal_id);
  206. //
  207. // Add the size of this portal to the pool.
  208. //
  209. _MemoryFootprint += sizeof (PathfindWaypathPortalClass);
  210. return portal_id;
  211. }
  212. ///////////////////////////////////////////////////////////////////////////
  213. //
  214. // Add_Temporary_Portal
  215. //
  216. ///////////////////////////////////////////////////////////////////////////
  217. int
  218. PathfindClass::Add_Temporary_Portal
  219. (
  220. PathfindSectorClass *sector_from,
  221. PathfindSectorClass *sector_to,
  222. const Vector3 & start_pos,
  223. const Vector3 & dest_pos
  224. )
  225. {
  226. int retval = 0;
  227. if (sector_from != NULL && sector_to != NULL) {
  228. //
  229. // Check to see if there is already a portal between the two sectors
  230. //
  231. bool found = false;
  232. for (int index = 0; index < sector_from->Get_Portal_Count (); index ++) {
  233. PathfindPortalClass *portal = sector_from->Peek_Portal (index);
  234. PathfindSectorClass *sector = portal->Peek_Dest_Sector (sector_from);
  235. //
  236. // Is this portal's destination sector the one we are looking for?
  237. //
  238. if (sector == sector_to) {
  239. found = true;
  240. break;
  241. }
  242. }
  243. //
  244. // If there isn't already a portal between the sectors, then add one...
  245. //
  246. if (found == false) {
  247. AABoxClass portal_box (start_pos, Vector3 (0.25F, 0.25F, 1.0F));
  248. PathfindActionPortalClass *new_portal = new PathfindActionPortalClass;
  249. new_portal->Set_Bounding_Box (portal_box);
  250. new_portal->Add_Dest_Sector (sector_to);
  251. new_portal->Set_Entrance_Sector (sector_from);
  252. new_portal->Set_Action_Type (PathClass::ACTION_LEAP);
  253. new_portal->Set_Destination (dest_pos);
  254. new_portal->Set_ID (_NextTempPortalID ++);
  255. //
  256. // Add this portal to the housekeeping list
  257. //
  258. m_TemporaryPortalList.Add (new_portal);
  259. sector_from->Add_Portal (new_portal->Get_ID ());
  260. //
  261. // Add the size of this portal to the pool.
  262. //
  263. _MemoryFootprint += sizeof (PathfindActionPortalClass);
  264. retval = new_portal->Get_ID ();
  265. //
  266. // Have we reached the maximum number of temporary portals?
  267. //
  268. if (m_TemporaryPortalList.Count () > MAX_TEMP_PORTALS) {
  269. //
  270. // Remove the first temp portal in the list
  271. //
  272. PathfindActionPortalClass *old_portal = (PathfindActionPortalClass *)m_TemporaryPortalList[0];
  273. if (old_portal != NULL) {
  274. //
  275. // Remove the portal from which ever sectors reference it
  276. //
  277. PathfindSectorClass *sector = old_portal->Get_Entrance_Sector ();
  278. if (sector != NULL) {
  279. sector->Remove_Portal (old_portal->Get_ID ());
  280. }
  281. //
  282. // Remove the portal from our list and free our hold on it
  283. //
  284. m_TemporaryPortalList.Delete (0);
  285. old_portal->Release_Ref ();
  286. }
  287. }
  288. }
  289. }
  290. return retval;
  291. }
  292. ///////////////////////////////////////////////////////////////////////////
  293. //
  294. // Save
  295. //
  296. ///////////////////////////////////////////////////////////////////////////
  297. bool
  298. PathfindClass::Save (ChunkSaveClass &csave)
  299. {
  300. WWMEMLOG(MEM_PATHFIND);
  301. csave.Begin_Chunk (CHUNKID_DATABASE);
  302. //
  303. // Save the sectors and portals
  304. //
  305. bool retval = Save_Culling_System (csave);
  306. retval &= Save_Portals (csave);
  307. retval &= Save_Waypaths (csave);
  308. //
  309. // Save the height database for flying vehicles
  310. //
  311. csave.Begin_Chunk (CHUNKID_HEIGHTDB);
  312. HeightDBClass::Save (csave);
  313. csave.End_Chunk ();
  314. csave.End_Chunk ();
  315. return retval;
  316. }
  317. ///////////////////////////////////////////////////////////////////////////
  318. //
  319. // Save_Portals
  320. //
  321. ///////////////////////////////////////////////////////////////////////////
  322. bool
  323. PathfindClass::Save_Portals (ChunkSaveClass &csave)
  324. {
  325. bool retval = true;
  326. int count = m_PortalList.Count ();
  327. for (int index = 0; index < count && retval; index ++) {
  328. PathfindPortalClass *portal = m_PortalList[index];
  329. //
  330. // Determine what type of chunk to save this portal in
  331. //
  332. if (portal->As_PathfindActionPortalClass () != NULL) {
  333. csave.Begin_Chunk (CHUNKID_ACTION_PORTAL);
  334. } else {
  335. csave.Begin_Chunk (CHUNKID_PORTAL);
  336. }
  337. //
  338. // Write this portal out ot its own chunk
  339. //
  340. retval &= portal->Save (csave);
  341. csave.End_Chunk ();
  342. }
  343. //
  344. // Now save the waypath portals
  345. //
  346. count = m_WaypathPortalList.Count ();
  347. for (index = 0; index < count && retval; index ++) {
  348. PathfindPortalClass *portal = m_WaypathPortalList[index];
  349. //
  350. // Write this portal out ot its own chunk
  351. //
  352. csave.Begin_Chunk (CHUNKID_WAYPATH_PORTAL);
  353. retval &= portal->Save (csave);
  354. csave.End_Chunk ();
  355. }
  356. return retval;
  357. }
  358. ///////////////////////////////////////////////////////////////////////////
  359. //
  360. // Load
  361. //
  362. ///////////////////////////////////////////////////////////////////////////
  363. bool
  364. PathfindClass::Load (ChunkLoadClass &cload)
  365. {
  366. WWMEMLOG(MEM_PATHFIND);
  367. Reset_Sectors ();
  368. Reset_Portals ();
  369. Reset_Waypaths ();
  370. bool retval = true;
  371. //
  372. // Make sure we are reading data from the right file...
  373. //
  374. if ( cload.Open_Chunk () != true ||
  375. cload.Cur_Chunk_ID () != CHUNKID_DATABASE)
  376. {
  377. retval = false;
  378. }
  379. //
  380. // Read all the chunks...
  381. //
  382. while (retval && cload.Open_Chunk ()) {
  383. switch (cload.Cur_Chunk_ID ()) {
  384. case CHUNKID_HEIGHTDB:
  385. retval &= HeightDBClass::Load (cload);
  386. break;
  387. case CHUNKID_SECTOR_CULLING_SYSTEM:
  388. retval &= Load_Culling_System (cload);
  389. break;
  390. case CHUNKID_PORTAL:
  391. {
  392. PathfindPortalClass *portal = new PathfindPortalClass;
  393. retval &= Load_Portal (cload, portal);
  394. }
  395. break;
  396. case CHUNKID_ACTION_PORTAL:
  397. {
  398. PathfindActionPortalClass *portal = new PathfindActionPortalClass;
  399. retval &= Load_Portal (cload, portal);
  400. }
  401. break;
  402. case CHUNKID_WAYPATH_PORTAL:
  403. {
  404. PathfindWaypathPortalClass *portal = new PathfindWaypathPortalClass;
  405. retval &= Load_Portal (cload, portal);
  406. }
  407. break;
  408. default:
  409. {
  410. //
  411. // Load the object from the chunk (if possible)
  412. //
  413. PersistFactoryClass *factory = SaveLoadSystemClass::Find_Persist_Factory (cload.Cur_Chunk_ID ());
  414. if (factory != NULL) {
  415. PersistClass *object = factory->Load (cload);
  416. //
  417. // Were we successful?
  418. //
  419. if (object != NULL) {
  420. //
  421. // If this is a waypath, then register it with the system
  422. //
  423. if (cload.Cur_Chunk_ID () == PHYSICS_CHUNKID_WAYPATH) {
  424. Add_Waypath ((WaypathClass *)object);
  425. ((WaypathClass *)object)->Release_Ref ();
  426. }
  427. } else {
  428. WWDEBUG_SAY (("Unknown chunk ID 0x%X\r\n", cload.Cur_Chunk_ID ()));
  429. retval = false;
  430. }
  431. }
  432. }
  433. break;
  434. }
  435. cload.Close_Chunk ();
  436. }
  437. cload.Close_Chunk ();
  438. return retval;
  439. }
  440. ///////////////////////////////////////////////////////////////////////////
  441. //
  442. // Save_Sector
  443. //
  444. ///////////////////////////////////////////////////////////////////////////
  445. bool
  446. PathfindClass::Save_Sector
  447. (
  448. ChunkSaveClass & csave,
  449. PathfindSectorClass * sector
  450. )
  451. {
  452. bool retval = true;
  453. csave.Begin_Chunk (CHUNKID_SECTOR);
  454. //
  455. // Write this sector out to its own chunk
  456. //
  457. bool save_linkage = true;
  458. if (sector->As_PathfindWaypathSectorClass () != NULL) {
  459. csave.Begin_Chunk (CHUNKID_PATHFIND_SECTOR_OBJECT);
  460. save_linkage = false;
  461. } else {
  462. csave.Begin_Chunk (CHUNKID_SECTOR_OBJECT);
  463. }
  464. retval &= sector->Save (csave);
  465. csave.End_Chunk ();
  466. //
  467. // Now write the sector's AABTreeCullSystem linkage to a chunk
  468. //
  469. if (save_linkage) {
  470. csave.Begin_Chunk (CHUNKID_SECTOR_LINKAGE);
  471. m_SectorTree.Save_Object_Linkage (csave, sector);
  472. csave.End_Chunk ();
  473. }
  474. csave.End_Chunk ();
  475. return retval;
  476. }
  477. ///////////////////////////////////////////////////////////////////////////
  478. //
  479. // Load_Sector
  480. //
  481. ///////////////////////////////////////////////////////////////////////////
  482. bool
  483. PathfindClass::Load_Sector (ChunkLoadClass &cload)
  484. {
  485. bool retval = true;
  486. PathfindSectorClass *sector = NULL;
  487. //
  488. // Read all the chunks...
  489. //
  490. while (cload.Open_Chunk ()) {
  491. switch (cload.Cur_Chunk_ID ()) {
  492. case CHUNKID_SECTOR_OBJECT:
  493. sector = new PathfindSectorClass;
  494. retval &= sector->Load (cload);
  495. break;
  496. case CHUNKID_PATHFIND_SECTOR_OBJECT:
  497. sector = new PathfindWaypathSectorClass;
  498. retval &= sector->Load (cload);
  499. //
  500. // Add this sector to our housekeeping list
  501. //
  502. Add_Sector (sector, false);
  503. break;
  504. case CHUNKID_SECTOR_LINKAGE:
  505. WWASSERT (sector != NULL);
  506. if (sector != NULL) {
  507. //
  508. // Read the linkage information from the chunk
  509. //
  510. m_SectorTree.Load_Object_Linkage (cload, sector);
  511. //
  512. // Add this sector to our housekeeping list
  513. //
  514. Add_Sector (sector, false);
  515. }
  516. break;
  517. default:
  518. WWDEBUG_SAY (("Unknown chunk ID 0x%X\r\n", cload.Cur_Chunk_ID ()));
  519. retval = false;
  520. break;
  521. }
  522. cload.Close_Chunk ();
  523. }
  524. REF_PTR_RELEASE (sector);
  525. return retval;
  526. }
  527. ///////////////////////////////////////////////////////////////////////////
  528. //
  529. // Load_Portal
  530. //
  531. ///////////////////////////////////////////////////////////////////////////
  532. bool
  533. PathfindClass::Load_Portal (ChunkLoadClass &cload, PathfindPortalClass *portal)
  534. {
  535. //
  536. // Load the portal's data from its chunk
  537. // and add it to our global portal list.
  538. //
  539. bool retval = portal->Load (cload);
  540. if (retval) {
  541. //
  542. // Now add this portal to its list
  543. //
  544. if (portal->As_PathfindWaypathPortalClass () == NULL) {
  545. m_PortalList.Add (portal);
  546. } else {
  547. m_WaypathPortalList.Add (portal->As_PathfindWaypathPortalClass ());
  548. }
  549. //
  550. // Add the size of this portal to the pool.
  551. //
  552. _MemoryFootprint += sizeof (PathfindPortalClass);
  553. }
  554. return retval;
  555. }
  556. ///////////////////////////////////////////////////////////////////////////
  557. //
  558. // Get_Sector_Index
  559. //
  560. ///////////////////////////////////////////////////////////////////////////
  561. int
  562. PathfindClass::Get_Sector_Index (PathfindSectorClass *sector)
  563. {
  564. int index = m_SectorList.Count ();
  565. bool keep_going = true;
  566. while (keep_going && index --) {
  567. keep_going = (m_SectorList[index] != sector);
  568. }
  569. return index;
  570. }
  571. static inline bool Clip_Point (Vector3 *point, const AABoxClass &box)
  572. {
  573. Vector3 temp_point = *point;
  574. //
  575. // Clip the temporary point
  576. //
  577. temp_point.X = max (temp_point.X, box.Center.X - box.Extent.X);
  578. temp_point.Y = max (temp_point.Y, box.Center.Y - box.Extent.Y);
  579. temp_point.Z = max (temp_point.Z, box.Center.Z - box.Extent.Z);
  580. temp_point.X = min (temp_point.X, box.Center.X + box.Extent.X);
  581. temp_point.Y = min (temp_point.Y, box.Center.Y + box.Extent.Y);
  582. temp_point.Z = min (temp_point.Z, box.Center.Z + box.Extent.Z);
  583. //
  584. // Did the clip change the point?
  585. //
  586. bool retval = (point->X != temp_point.X);
  587. retval |= (point->Y != temp_point.Y);
  588. retval |= (point->Z != temp_point.Z);
  589. //
  590. // Pass the new point back to the caller
  591. //
  592. (*point) = temp_point;
  593. return retval;
  594. }
  595. ///////////////////////////////////////////////////////////////////////////
  596. //
  597. // Collect_Sectors
  598. //
  599. ///////////////////////////////////////////////////////////////////////////
  600. void
  601. PathfindClass::Collect_Sectors
  602. (
  603. DynamicVectorClass<PathfindSectorClass *> & list,
  604. const AABoxClass & box,
  605. PathfindSectorClass * exclude_sector
  606. )
  607. {
  608. //
  609. // Collect all the sectors this box intersects
  610. //
  611. m_SectorTree.Reset_Collection ();
  612. m_SectorTree.Collect_Objects (box);
  613. //
  614. // Return the list of sectors to the caller
  615. //
  616. PathfindSectorClass *sector = NULL;
  617. for ( sector = m_SectorTree.Get_First_Collected_Object ();
  618. sector != NULL;
  619. sector = m_SectorTree.Get_Next_Collected_Object (sector))
  620. {
  621. if (sector != exclude_sector) {
  622. //
  623. // Ignore sectors that are 0 size
  624. //
  625. const AABoxClass &box = sector->Get_Bounding_Box ();
  626. if (box.Extent.X != 0 && box.Extent.Y != 0 && box.Extent.Z != 0) {
  627. list.Add (sector);
  628. }
  629. }
  630. }
  631. return ;
  632. }
  633. ///////////////////////////////////////////////////////////////////////////
  634. //
  635. // Find_Sector
  636. //
  637. ///////////////////////////////////////////////////////////////////////////
  638. PathfindSectorClass *
  639. PathfindClass::Find_Sector
  640. (
  641. const Vector3 & position,
  642. float sector_fudge,
  643. PathfindSectorClass *exclude_sector
  644. )
  645. {
  646. //
  647. // Collect all the sectors this position intersects (hopefully
  648. // will be only one).
  649. //
  650. SphereClass sphere (position, sector_fudge);
  651. m_SectorTree.Reset_Collection ();
  652. m_SectorTree.Collect_Objects (sphere);
  653. float closest = 99999.9F;
  654. //
  655. // Find an acceptable sector to return
  656. //
  657. PathfindSectorClass *closest_sector = NULL;
  658. PathfindSectorClass *sector = NULL;
  659. for ( sector = m_SectorTree.Get_First_Collected_Object ();
  660. sector != NULL;
  661. sector = m_SectorTree.Get_Next_Collected_Object (sector))
  662. {
  663. if (sector != exclude_sector) {
  664. //
  665. // Ignore sectors that are small
  666. //
  667. const AABoxClass &box = sector->Get_Bounding_Box ();
  668. if (box.Extent.X > 0.325F && box.Extent.Y > 0.325F && box.Extent.Z > WWMATH_EPSILON) {
  669. //
  670. // Clip the player's position to this sector
  671. //
  672. Vector3 clipped_pos = position;
  673. ::Clip_Point (&clipped_pos, box);
  674. //
  675. // Check to see if this clipped position is the closest yet
  676. //
  677. float dist = (clipped_pos - position).Length ();
  678. if (dist < closest) {
  679. closest = dist;
  680. closest_sector = sector;
  681. }
  682. }
  683. }
  684. }
  685. //
  686. // Return the first (and hopefully only) sector
  687. //
  688. return closest_sector;
  689. }
  690. ///////////////////////////////////////////////////////////////////////////
  691. //
  692. // Reset_Sectors
  693. //
  694. ///////////////////////////////////////////////////////////////////////////
  695. void
  696. PathfindClass::Reset_Sectors (void)
  697. {
  698. Display_Sectors (false);
  699. Display_Portals (false);
  700. //
  701. // Release our hold on each of the sectors
  702. //
  703. for (int index = 0; index < m_SectorList.Count (); index ++) {
  704. PathfindSectorClass *sector = m_SectorList[index];
  705. //
  706. // Remove this sector from the culling system (if necessary)
  707. //
  708. if (sector->As_PathfindWaypathSectorClass () == NULL) {
  709. m_SectorTree.Remove_Object (sector);
  710. }
  711. REF_PTR_RELEASE (sector);
  712. }
  713. m_SectorList.Delete_All ();
  714. _MemoryFootprint = 0;
  715. return ;
  716. }
  717. ///////////////////////////////////////////////////////////////////////////
  718. //
  719. // Reset_Portals
  720. //
  721. ///////////////////////////////////////////////////////////////////////////
  722. void
  723. PathfindClass::Reset_Portals (void)
  724. {
  725. Display_Portals (false);
  726. //
  727. // Release our hold on each of the portals
  728. //
  729. for (int index = 0; index < m_PortalList.Count (); index ++) {
  730. PathfindPortalClass *portal = m_PortalList[index];
  731. REF_PTR_RELEASE (portal);
  732. }
  733. //
  734. // Release our hold on each of the temporary portals
  735. //
  736. for (index = 0; index < m_TemporaryPortalList.Count (); index ++) {
  737. PathfindPortalClass *portal = m_TemporaryPortalList[index];
  738. REF_PTR_RELEASE (portal);
  739. }
  740. //
  741. // Release our hold on each of the waypath portals
  742. //
  743. for (index = 0; index < m_WaypathPortalList.Count (); index ++) {
  744. PathfindPortalClass *portal = m_WaypathPortalList[index];
  745. REF_PTR_RELEASE (portal);
  746. }
  747. //
  748. // Reset the lists
  749. //
  750. m_PortalList.Delete_All ();
  751. m_TemporaryPortalList.Delete_All ();
  752. m_WaypathPortalList.Delete_All ();
  753. //
  754. // Reset the starting IDs
  755. //
  756. _NextTempPortalID = TEMP_PORTAL_ID_START;
  757. return ;
  758. }
  759. ///////////////////////////////////////////////////////////////////////////
  760. //
  761. // Display_Sectors
  762. //
  763. ///////////////////////////////////////////////////////////////////////////
  764. void
  765. PathfindClass::Display_Sectors (bool onoff)
  766. {
  767. //
  768. // Start fresh
  769. //
  770. m_SectorDisplayWidgets.Reset_Debug_Widget_List ();
  771. if (onoff) {
  772. //
  773. // Add boxes to our debug widget system to represent the sectors
  774. //
  775. for (int index = 0; index < m_SectorList.Count (); index ++) {
  776. PathfindSectorClass *sector = m_SectorList[index];
  777. const AABoxClass &bounding_box = sector->Get_Bounding_Box ();
  778. //
  779. // Add a randomly colored box to the widget system
  780. //
  781. float red = 0;
  782. float green = 0.5F + float(rand () % 128) / 256.0F;
  783. float blue = 0.5F + float(rand () % 128) / 256.0F;
  784. m_SectorDisplayWidgets.Add_Debug_AABox (bounding_box, Vector3 (red, green, blue));
  785. }
  786. }
  787. m_SectorsDisplayed = onoff;
  788. return ;
  789. }
  790. ///////////////////////////////////////////////////////////////////////////
  791. //
  792. // Display_Portals
  793. //
  794. ///////////////////////////////////////////////////////////////////////////
  795. void
  796. PathfindClass::Display_Portals (bool onoff)
  797. {
  798. //
  799. // Start fresh
  800. //
  801. m_PortalDisplayWidgets.Reset_Debug_Widget_List ();
  802. //
  803. // Add boxes to our debug widget system to represent the sectors
  804. //
  805. if (onoff) {
  806. for (int index = 0; index < m_PortalList.Count (); index ++) {
  807. PathfindPortalClass *portal = m_PortalList[index];
  808. AABoxClass bounding_box;
  809. portal->Get_Bounding_Box (bounding_box);
  810. //
  811. // Choose a color for this box
  812. //
  813. Vector3 color (1, 1, 1);
  814. if (portal->Is_Two_Way_Portal () == false) {
  815. color.Set (1, 0, 0);
  816. }
  817. //
  818. // Add a box to the system
  819. //
  820. m_PortalDisplayWidgets.Add_Debug_AABox (bounding_box, color);
  821. }
  822. }
  823. m_PortalsDisplayed = onoff;
  824. return ;
  825. }
  826. ///////////////////////////////////////////////////////////////////////////
  827. //
  828. // Add_Waypath
  829. //
  830. ///////////////////////////////////////////////////////////////////////////
  831. void
  832. PathfindClass::Add_Waypath (WaypathClass *waypath)
  833. {
  834. if (waypath != NULL) {
  835. waypath->Add_Ref ();
  836. m_WaypathList.Add (waypath);
  837. }
  838. return ;
  839. }
  840. ///////////////////////////////////////////////////////////////////////////
  841. //
  842. // Remove_Waypath
  843. //
  844. ///////////////////////////////////////////////////////////////////////////
  845. bool
  846. PathfindClass::Remove_Waypath (WaypathClass *waypath)
  847. {
  848. bool retval = false;
  849. if (waypath != NULL) {
  850. int index = m_WaypathList.Count ();
  851. while (index -- && (retval == false)) {
  852. WaypathClass *curr_waypath = m_WaypathList[index];
  853. //
  854. // If this is the waypath we are looking for
  855. // then remove it from the list and release our
  856. // hold on it.
  857. //
  858. if (curr_waypath == waypath) {
  859. REF_PTR_RELEASE (waypath);
  860. m_WaypathList.Delete (index);
  861. retval = true;
  862. }
  863. }
  864. }
  865. return retval;
  866. }
  867. ///////////////////////////////////////////////////////////////////////////
  868. //
  869. // Find_Waypath
  870. //
  871. ///////////////////////////////////////////////////////////////////////////
  872. WaypathClass *
  873. PathfindClass::Find_Waypath (int id) const
  874. {
  875. WaypathClass *waypath = NULL;
  876. //
  877. // Loop over all the paths in our list until we've
  878. // found the one are looking for.
  879. //
  880. int index = m_WaypathList.Count ();
  881. while (index -- && (waypath == NULL)) {
  882. WaypathClass *curr_waypath = m_WaypathList[index];
  883. //
  884. // Is this the waypath we are looking for?
  885. //
  886. if (curr_waypath->Get_ID () == id) {
  887. waypath = curr_waypath;
  888. }
  889. }
  890. return waypath;
  891. }
  892. ///////////////////////////////////////////////////////////////////////////
  893. //
  894. // Count_Waypaths_Starting_In_Box
  895. //
  896. ///////////////////////////////////////////////////////////////////////////
  897. int PathfindClass::Count_Waypaths_Starting_In_Box (const AABoxClass & box)
  898. {
  899. //
  900. // Loop over all the paths in our list counting the ones whose
  901. // start points are contained inside the given box.
  902. //
  903. int count = 0;
  904. for (int index=0; index<m_WaypathList.Count(); index++) {
  905. WaypathClass *curr_waypath = m_WaypathList[index];
  906. if (CollisionMath::Overlap_Test(box,curr_waypath->Get_Point(0)->Get_Position()) == CollisionMath::INSIDE) {
  907. count++;
  908. }
  909. }
  910. return count;
  911. }
  912. ///////////////////////////////////////////////////////////////////////////
  913. //
  914. // Get_Waypath_Starting_In_Box
  915. //
  916. ///////////////////////////////////////////////////////////////////////////
  917. WaypathClass * PathfindClass::Get_Waypath_Starting_In_Box (const AABoxClass & box,int i)
  918. {
  919. //
  920. // Loop over all the paths in our list counting down until
  921. // we get to the i'th one that starts in the given box.
  922. //
  923. WaypathClass * path = NULL;
  924. int count = i;
  925. for (int index=0; index<m_WaypathList.Count(); index++) {
  926. WaypathClass *curr_waypath = m_WaypathList[index];
  927. if (CollisionMath::Overlap_Test(box,curr_waypath->Get_Point(0)->Get_Position()) == CollisionMath::INSIDE) {
  928. if (count == 0) {
  929. path = curr_waypath;
  930. break;
  931. } else {
  932. count--;
  933. }
  934. }
  935. }
  936. if (path != NULL) {
  937. path->Add_Ref();
  938. }
  939. return path;
  940. }
  941. ///////////////////////////////////////////////////////////////////////////
  942. //
  943. // Reset_Waypaths
  944. //
  945. ///////////////////////////////////////////////////////////////////////////
  946. void
  947. PathfindClass::Reset_Waypaths (void)
  948. {
  949. //
  950. // Release our hold on each of the waypaths
  951. //
  952. int index = m_WaypathList.Count ();
  953. while (index --) {
  954. WaypathClass *waypath = m_WaypathList[index];
  955. REF_PTR_RELEASE (waypath);
  956. }
  957. //
  958. // Remove all the waypaths from our list
  959. //
  960. m_WaypathList.Delete_All ();
  961. return ;
  962. }
  963. //////////////////////////////////////////////////////////////////////////////////
  964. //
  965. // Save_Waypaths
  966. //
  967. //////////////////////////////////////////////////////////////////////////////////
  968. bool
  969. PathfindClass::Save_Waypaths (ChunkSaveClass &csave)
  970. {
  971. //
  972. // Loop over all the waypaths
  973. //
  974. int index = m_WaypathList.Count ();
  975. while (index --) {
  976. WaypathClass *waypath = m_WaypathList[index];
  977. //
  978. // Save this waypath object
  979. //
  980. csave.Begin_Chunk (waypath->Get_Factory ().Chunk_ID ());
  981. waypath->Get_Factory ().Save (csave, waypath);
  982. csave.End_Chunk ();
  983. //
  984. // Now save all the waypoints that this waypath contains
  985. //
  986. int waypoint_index = waypath->Get_Point_Count ();
  987. while (waypoint_index --) {
  988. WaypointClass *waypoint = waypath->Get_Point (waypoint_index);
  989. //
  990. // Save this waypoint object
  991. //
  992. csave.Begin_Chunk (waypoint->Get_Factory ().Chunk_ID ());
  993. waypoint->Get_Factory ().Save (csave, waypoint);
  994. csave.End_Chunk ();
  995. }
  996. }
  997. return true;
  998. }
  999. //////////////////////////////////////////////////////////////////////////////////
  1000. //
  1001. // Save_Culling_System
  1002. //
  1003. //////////////////////////////////////////////////////////////////////////////////
  1004. bool
  1005. PathfindClass::Save_Culling_System (ChunkSaveClass &csave)
  1006. {
  1007. bool retval = true;
  1008. csave.Begin_Chunk (CHUNKID_SECTOR_CULLING_SYSTEM);
  1009. //
  1010. // Write the AABTreeCullSystem we use for sector lookups
  1011. // to the chunk
  1012. //
  1013. csave.Begin_Chunk (CHUNKID_SECTOR_CULL_TREE);
  1014. m_SectorTree.Save (csave);
  1015. csave.End_Chunk ();
  1016. //
  1017. // Now write the contents of the tree (the sector list)
  1018. // to the chunk
  1019. //
  1020. int count = m_SectorList.Count ();
  1021. for (int index = 0; index < count && retval; index ++) {
  1022. Save_Sector (csave, m_SectorList[index]);
  1023. }
  1024. csave.End_Chunk ();
  1025. return retval;
  1026. }
  1027. //////////////////////////////////////////////////////////////////////////////////
  1028. //
  1029. // Load_Culling_System
  1030. //
  1031. //////////////////////////////////////////////////////////////////////////////////
  1032. bool
  1033. PathfindClass::Load_Culling_System (ChunkLoadClass &cload)
  1034. {
  1035. bool retval = true;
  1036. //
  1037. // Read all the chunks...
  1038. //
  1039. while (cload.Open_Chunk ()) {
  1040. switch (cload.Cur_Chunk_ID ()) {
  1041. case CHUNKID_SECTOR_CULL_TREE:
  1042. m_SectorTree.Load (cload);
  1043. break;
  1044. case CHUNKID_SECTOR:
  1045. retval &= Load_Sector (cload);
  1046. break;
  1047. default:
  1048. WWDEBUG_SAY (("Unknown chunk ID 0x%X\r\n", cload.Cur_Chunk_ID ()));
  1049. retval = false;
  1050. break;
  1051. }
  1052. cload.Close_Chunk ();
  1053. }
  1054. return retval;
  1055. }
  1056. //////////////////////////////////////////////////////////////////////////////////
  1057. //
  1058. // Re_Partition_Sector_Tree
  1059. //
  1060. //////////////////////////////////////////////////////////////////////////////////
  1061. void
  1062. PathfindClass::Re_Partition_Sector_Tree (void)
  1063. {
  1064. m_SectorTree.Re_Partition ();
  1065. return ;
  1066. }
  1067. /////////////////////////////////////////////////////////////////////////
  1068. //
  1069. // Peek_Portal
  1070. //
  1071. /////////////////////////////////////////////////////////////////////////
  1072. PathfindPortalClass *
  1073. PathfindClass::Peek_Portal (int portal_index)
  1074. {
  1075. PathfindPortalClass *portal = NULL;
  1076. if (portal_index >= TEMP_PORTAL_ID_START) {
  1077. //
  1078. // Try to find the portal in our temporary portal list
  1079. //
  1080. for (int index = 0; index < m_TemporaryPortalList.Count (); index ++) {
  1081. //
  1082. // Is this the portal we are looking for?
  1083. //
  1084. if (portal_index == (int)m_TemporaryPortalList[index]->Get_ID ()) {
  1085. portal = m_TemporaryPortalList[index];
  1086. break;
  1087. }
  1088. }
  1089. } else if (portal_index >= WAYPATH_PORTAL_ID_START) {
  1090. //
  1091. // Look for the portal in our waypath portal list
  1092. //
  1093. portal = m_WaypathPortalList[portal_index - WAYPATH_PORTAL_ID_START];
  1094. } else if (portal_index >= 0) {
  1095. portal = m_PortalList[portal_index];
  1096. }
  1097. return portal;
  1098. }
  1099. //////////////////////////////////////////////////////////////////////////////////
  1100. //
  1101. // Get_Height_Value
  1102. //
  1103. //////////////////////////////////////////////////////////////////////////////////
  1104. float
  1105. PathfindClass::Get_Height_Value (const Vector3 &pos)
  1106. {
  1107. return HeightDBClass::Get_Height (pos);
  1108. }
  1109. //////////////////////////////////////////////////////////////////////////////////
  1110. //
  1111. // Find_Random_Spot
  1112. //
  1113. //////////////////////////////////////////////////////////////////////////////////
  1114. static DynamicVectorClass<AABoxClass> box_list;
  1115. bool
  1116. PathfindClass::Find_Random_Spot
  1117. (
  1118. const Vector3 &center,
  1119. float max_dist,
  1120. Vector3 * dest
  1121. )
  1122. {
  1123. bool retval = false;
  1124. //
  1125. // Lookup the starting sector we'll search from
  1126. //
  1127. PathfindSectorClass *start_sector = Find_Sector (center, 0.1F, NULL);
  1128. if (start_sector != NULL) {
  1129. box_list.Reset_Active();
  1130. //
  1131. // Start a list of valid 'regions' with the bounding box of the
  1132. // current sector
  1133. //
  1134. box_list.Add (start_sector->Get_Bounding_Box ());
  1135. //
  1136. // Square the distances for quick compares
  1137. //
  1138. float max_dist2 = max_dist * max_dist;
  1139. //
  1140. // Loop over all connecting sectors and add any of them that meet
  1141. // the distance requirements to our list
  1142. //
  1143. for (int index = 0; index < start_sector->Get_Portal_Count (); index ++) {
  1144. PathfindPortalClass *portal = start_sector->Peek_Portal (index);
  1145. PathfindSectorClass *dest_sector = portal->Peek_Dest_Sector (start_sector);
  1146. if (dest_sector != NULL) {
  1147. const AABoxClass &bounding_box = dest_sector->Get_Bounding_Box ();
  1148. //
  1149. // Find a "close" point to the box from the starting position
  1150. //
  1151. Vector3 closest_pt = center;
  1152. Clip_Point (&closest_pt, bounding_box);
  1153. //
  1154. // Is this box within the maximum acceptable distance?
  1155. //
  1156. float dist_to_box2 = (center - closest_pt).Length2 ();
  1157. if (dist_to_box2 < max_dist2) {
  1158. box_list.Add (bounding_box);
  1159. }
  1160. }
  1161. }
  1162. //
  1163. // Pick a random box from the list
  1164. //
  1165. int count = box_list.Count ();
  1166. int box_index = (rand () % count);
  1167. const AABoxClass &box = box_list[box_index];
  1168. //
  1169. // Find a "close" point to the box from the starting position
  1170. //
  1171. Vector3 closest_pt = center;
  1172. Clip_Point (&closest_pt, box);
  1173. //
  1174. // Now add a random vector to this point that will stay
  1175. // inside the requirements
  1176. //
  1177. float curr_dist = (closest_pt - center).Length ();
  1178. float max_allowable_dist = (max_dist - curr_dist);
  1179. (*dest) = closest_pt;
  1180. dest->X += WWMath::Random_Float (-max_allowable_dist, max_allowable_dist);
  1181. dest->Y += WWMath::Random_Float (-max_allowable_dist, max_allowable_dist);
  1182. //
  1183. // Now, simply make sure the point stays inside the pathfind sector
  1184. //
  1185. Clip_Point (dest, box);
  1186. retval = true;
  1187. } else if (m_SectorList.Count () == 0) {
  1188. //
  1189. // If we don't have any pathfind data, then simply choose a random point
  1190. //
  1191. (*dest) = center;
  1192. dest->X += WWMath::Random_Float (-max_dist, max_dist);
  1193. dest->Y += WWMath::Random_Float (-max_dist, max_dist);
  1194. retval = true;
  1195. }
  1196. return retval;
  1197. }
  1198. //////////////////////////////////////////////////////////////////////////////////
  1199. //
  1200. // Free_Waypath_Sectors_And_Portals
  1201. //
  1202. //////////////////////////////////////////////////////////////////////////////////
  1203. void
  1204. PathfindClass::Free_Waypath_Sectors_And_Portals (void)
  1205. {
  1206. //
  1207. // Release our hold on each of the waypath portals
  1208. //
  1209. for (int index = 0; index < m_WaypathPortalList.Count (); index ++) {
  1210. PathfindPortalClass *portal = m_WaypathPortalList[index];
  1211. //
  1212. // Remove this portal from the sectors that reference it
  1213. //
  1214. uint16 dest_sector1_id = portal->Get_Dest_Sector1 ();
  1215. uint16 dest_sector2_id = portal->Get_Dest_Sector2 ();
  1216. PathfindSectorClass *dest_sector1 = Peek_Sector (dest_sector1_id);
  1217. PathfindSectorClass *dest_sector2 = Peek_Sector (dest_sector2_id);
  1218. if (dest_sector1 != NULL) {
  1219. dest_sector1->Remove_Portal (portal->Get_ID ());
  1220. }
  1221. if (dest_sector2 != NULL) {
  1222. dest_sector2->Remove_Portal (portal->Get_ID ());
  1223. }
  1224. REF_PTR_RELEASE (portal);
  1225. }
  1226. //
  1227. // Loop over all the sectors in our list
  1228. //
  1229. for (index = 0; index < m_SectorList.Count (); index ++) {
  1230. PathfindSectorClass *sector = m_SectorList[index];
  1231. //
  1232. // Is this sector a waypath sector?
  1233. //
  1234. if (sector->As_PathfindWaypathSectorClass () != NULL) {
  1235. //
  1236. // Free the sector and remove it from the list
  1237. //
  1238. REF_PTR_RELEASE (sector);
  1239. m_SectorList.Delete (index);
  1240. index --;
  1241. }
  1242. }
  1243. //
  1244. // Reset the portal list
  1245. //
  1246. m_WaypathPortalList.Delete_All ();
  1247. return ;
  1248. }
  1249. //////////////////////////////////////////////////////////////////////////////////
  1250. //
  1251. // Generate_Waypath_Sectors_And_Portals
  1252. //
  1253. //////////////////////////////////////////////////////////////////////////////////
  1254. void
  1255. PathfindClass::Generate_Waypath_Sectors_And_Portals (void)
  1256. {
  1257. //
  1258. // Start fresh
  1259. //
  1260. Free_Waypath_Sectors_And_Portals ();
  1261. //
  1262. // Loop over all the waypaths in the system
  1263. //
  1264. for (int index = 0; index < m_WaypathList.Count (); index ++) {
  1265. WaypathClass *waypath = m_WaypathList[index];
  1266. //
  1267. // Only process waypaths that can be used during a path solve
  1268. //
  1269. if (waypath->Get_Flag (WaypathClass::FLAG_INNATE_PATHFIND)) {
  1270. Generate_Waypath_Sector_And_Portals (waypath);
  1271. }
  1272. }
  1273. return ;
  1274. }
  1275. //////////////////////////////////////////////////////////////////////////////////
  1276. //
  1277. // Generate_Waypath_Sector_And_Portals
  1278. //
  1279. //////////////////////////////////////////////////////////////////////////////////
  1280. void
  1281. PathfindClass::Generate_Waypath_Sector_And_Portals (WaypathClass *waypath)
  1282. {
  1283. DynamicVectorClass<PathfindWaypathPortalClass *> portal_list;
  1284. //
  1285. // Get information about this waypath
  1286. //
  1287. int waypoint_count = waypath->Get_Point_Count ();
  1288. int waypath_id = waypath->Get_ID ();
  1289. //
  1290. // Create a new "waypath sector" that will hold all the enter and
  1291. // exit portals for us
  1292. //
  1293. PathfindWaypathSectorClass *new_sector = new PathfindWaypathSectorClass;
  1294. new_sector->Set_Waypath_ID (waypath_id);
  1295. new_sector->Set_Bounding_Box (AABoxClass (Vector3 (0, 0, 0), Vector3 (0, 0, 0)));
  1296. //
  1297. // Register this new sector with the pathfind system
  1298. //
  1299. Add_Sector (new_sector, false);
  1300. //
  1301. // Add a portal around each waypoint in the path
  1302. //
  1303. for (int index = 0; index < waypoint_count; index ++) {
  1304. WaypointClass *waypoint = waypath->Get_Point (index);
  1305. const Vector3 &waypoint_pos = waypoint->Get_Position ();
  1306. //
  1307. // Check to see if there is a pathfind sector at the waypoint position...
  1308. // if there is, then we should create a new portal at this position.
  1309. //
  1310. PathfindSectorClass *sector_at_pos = Find_Sector (waypoint_pos, 0.125F);
  1311. if (sector_at_pos != NULL) {
  1312. //
  1313. // Add a new portal to the list at this position
  1314. //
  1315. ::Add_New_Portal_To_List (portal_list, new_sector, sector_at_pos, waypath_id,
  1316. index, waypoint_pos, 0);
  1317. }
  1318. }
  1319. //
  1320. // Now add a portal everywhere the waypath intersects a pathfind sector
  1321. //
  1322. Add_Intersection_Portals_To_List (portal_list, waypath, new_sector);
  1323. if (portal_list.Count () > 0) {
  1324. //
  1325. // Now sort the portals based on occurance within the path
  1326. //
  1327. ::qsort (&portal_list[0], portal_list.Count (),
  1328. sizeof (PathfindWaypathPortalClass *), fnCompareWaypathPortalsCallback);
  1329. //
  1330. // Register the portals with the system and add them to their sectors
  1331. //
  1332. for (int portal_index = 0; portal_index < portal_list.Count (); portal_index ++) {
  1333. PathfindWaypathPortalClass *curr_portal = portal_list[portal_index];
  1334. //
  1335. // Register the portal
  1336. //
  1337. int portal_id = Add_Waypath_Portal (curr_portal);
  1338. //
  1339. // Lookup the sectors this portal will connect
  1340. //
  1341. uint16 dest_sector1_id = curr_portal->Get_Dest_Sector1 ();
  1342. uint16 dest_sector2_id = curr_portal->Get_Dest_Sector2 ();
  1343. PathfindSectorClass *dest_sector1 = Peek_Sector (dest_sector1_id);
  1344. PathfindSectorClass *dest_sector2 = Peek_Sector (dest_sector2_id);
  1345. //
  1346. // Add this portal to the sectors
  1347. //
  1348. if (dest_sector1 != NULL) {
  1349. dest_sector1->Add_Portal (portal_id);
  1350. }
  1351. if (dest_sector2 != NULL) {
  1352. dest_sector2->Add_Portal (portal_id);
  1353. }
  1354. //
  1355. // Release our hold on this portal
  1356. //
  1357. REF_PTR_RELEASE (curr_portal);
  1358. }
  1359. }
  1360. //
  1361. // Now release our hold on the sector we just created
  1362. //
  1363. REF_PTR_RELEASE (new_sector);
  1364. return ;
  1365. }
  1366. //////////////////////////////////////////////////////////////////////////////////
  1367. //
  1368. // Add_Intersection_Portals_To_List
  1369. //
  1370. //////////////////////////////////////////////////////////////////////////////////
  1371. void
  1372. PathfindClass::Add_Intersection_Portals_To_List
  1373. (
  1374. DynamicVectorClass<PathfindWaypathPortalClass *> & portal_list,
  1375. WaypathClass * waypath,
  1376. PathfindWaypathSectorClass * dest_sector
  1377. )
  1378. {
  1379. int waypoint_count = waypath->Get_Point_Count ();
  1380. int waypath_id = waypath->Get_ID ();
  1381. //
  1382. // For this waypath, find all the pathfind sectors it intersects
  1383. //
  1384. for (int waypoint_index = 0; waypoint_index < waypoint_count - 1; waypoint_index ++) {
  1385. WaypointClass *waypoint1 = waypath->Get_Point (waypoint_index);
  1386. WaypointClass *waypoint2 = waypath->Get_Point (waypoint_index + 1);
  1387. const Vector3 &pos1 = waypoint1->Get_Position ();
  1388. const Vector3 &pos2 = waypoint2->Get_Position ();
  1389. //
  1390. // Build a bounding box around the two points
  1391. //
  1392. Vector3 extent = (pos2 - pos1) * 0.5F;
  1393. Vector3 center = pos1 + extent;
  1394. extent.X = max (WWMath::Fabs (extent.X), 1.0F);
  1395. extent.Y = max (WWMath::Fabs (extent.Y), 1.0F);
  1396. extent.Z = max (WWMath::Fabs (extent.Z), 1.0F);
  1397. AABoxClass bounding_box (center, extent);
  1398. //
  1399. // Find all the pathfind sectors in this bounding box
  1400. //
  1401. m_SectorTree.Reset_Collection ();
  1402. m_SectorTree.Collect_Objects (bounding_box);
  1403. //
  1404. // Loop over all the sectors that this line segment could possibly intersect
  1405. //
  1406. PathfindSectorClass *sector = NULL;
  1407. for ( sector = m_SectorTree.Peek_First_Collected_Object ();
  1408. sector != NULL;
  1409. sector = m_SectorTree.Peek_Next_Collected_Object (sector))
  1410. {
  1411. const AABoxClass &sector_box = sector->Get_Bounding_Box ();
  1412. //
  1413. // Find where this line segment intersects the sector (if at all)
  1414. //
  1415. float percent = 0;
  1416. Vector3 portal_pos;
  1417. if (::Find_Intersection_Point (sector_box, pos1, pos2, &percent, &portal_pos)) {
  1418. //
  1419. // Add a new portal to the list at this position
  1420. //
  1421. ::Add_New_Portal_To_List (portal_list, dest_sector, sector, waypath_id,
  1422. waypoint_index, portal_pos, percent);
  1423. }
  1424. }
  1425. }
  1426. return ;
  1427. }
  1428. //////////////////////////////////////////////////////////////////////////////////
  1429. //
  1430. // Add_New_Portal_To_List
  1431. //
  1432. //////////////////////////////////////////////////////////////////////////////////
  1433. void
  1434. Add_New_Portal_To_List
  1435. (
  1436. DynamicVectorClass<PathfindWaypathPortalClass *> & portal_list,
  1437. PathfindSectorClass * dest_sector1,
  1438. PathfindSectorClass * dest_sector2,
  1439. int waypath_id,
  1440. int waypoint_index,
  1441. const Vector3 & portal_pos,
  1442. float percent
  1443. )
  1444. {
  1445. const Vector3 portal_extent (0.5F, 0.5F, 1.0F);
  1446. //
  1447. // Create a position along this waypath for the waypoint
  1448. //
  1449. WaypathPositionClass waypath_pos;
  1450. waypath_pos.Set_Waypath_ID (waypath_id);
  1451. waypath_pos.Set_Waypoint_Index (waypoint_index);
  1452. waypath_pos.Set_Percent (percent);
  1453. //
  1454. // Allcoate and configure a new portal
  1455. //
  1456. PathfindWaypathPortalClass *new_portal = new PathfindWaypathPortalClass;
  1457. new_portal->Set_Waypath_Pos (waypath_pos);
  1458. new_portal->Set_Bounding_Box (AABoxClass (portal_pos, portal_extent));
  1459. if (dest_sector1 != NULL) {
  1460. new_portal->Add_Dest_Sector (dest_sector1);
  1461. }
  1462. if (dest_sector2 != NULL) {
  1463. new_portal->Add_Dest_Sector (dest_sector2);
  1464. }
  1465. //
  1466. // Add this portal to the list
  1467. //
  1468. portal_list.Add (new_portal);
  1469. return ;
  1470. }
  1471. //////////////////////////////////////////////////////////////////////////////////
  1472. //
  1473. // Find_Intersection_Point
  1474. //
  1475. //////////////////////////////////////////////////////////////////////////////////
  1476. bool
  1477. Find_Intersection_Point
  1478. (
  1479. const AABoxClass & box,
  1480. const Vector3 & p0,
  1481. const Vector3 & p1,
  1482. float * percent,
  1483. Vector3 * intersection_point
  1484. )
  1485. {
  1486. bool retval = false;
  1487. //
  1488. // Find the locations of each of the 6 "planes"
  1489. // we will be testing against
  1490. //
  1491. float x1 = box.Center.X - box.Extent.X;
  1492. float x2 = box.Center.X + box.Extent.X;
  1493. float y1 = box.Center.Y - box.Extent.Y;
  1494. float y2 = box.Center.Y + box.Extent.Y;
  1495. float z1 = box.Center.Z - box.Extent.Z;
  1496. float z2 = box.Center.Z + box.Extent.Z;
  1497. float t_values[6] = { -1, -1, -1, -1, -1, -1 };
  1498. Vector3 delta = p1 - p0;
  1499. //
  1500. // Find the "t" values where the line intersects the
  1501. // 2 "Y-Z" planes
  1502. //
  1503. if (delta.X != 0) {
  1504. t_values[0] = (x1 - p0.X) / delta.X;
  1505. t_values[1] = (x2 - p0.X) / delta.X;
  1506. }
  1507. //
  1508. // Find the "t" values where the line intersects the
  1509. // 2 "X-Z" planes
  1510. //
  1511. if (delta.Y != 0) {
  1512. t_values[2] = (y1 - p0.Y) / delta.Y;
  1513. t_values[3] = (y2 - p0.Y) / delta.Y;
  1514. }
  1515. //
  1516. // Find the "t" values where the line intersects the
  1517. // 2 "X-Y" planes
  1518. //
  1519. if (delta.Z != 0) {
  1520. t_values[4] = (z1 - p0.Z) / delta.Z;
  1521. t_values[5] = (z2 - p0.Z) / delta.Z;
  1522. }
  1523. //
  1524. // Loop over all the "t" values we've calculated
  1525. //
  1526. (*percent) = 2.0F;
  1527. for (int index = 0; index < 6; index ++) {
  1528. //
  1529. // Is this "t" value the smallest in-range value we've
  1530. // found yet?
  1531. //
  1532. if ( t_values[index] >= 0 &&
  1533. t_values[index] <= 1.0F &&
  1534. t_values[index] < (*percent))
  1535. {
  1536. //
  1537. // Find the point which exists at this "t" value along the line segment
  1538. //
  1539. Vector3 point = p0 + delta * t_values[index];
  1540. //
  1541. // If this point isn't outside the box, then we'll consider it good enough
  1542. //
  1543. if ( (WWMath::Fabs (point.X - box.Center.X) <= (box.Extent.X + WWMATH_EPSILON)) &&
  1544. (WWMath::Fabs (point.Y - box.Center.Y) <= (box.Extent.Y + WWMATH_EPSILON)) &&
  1545. (WWMath::Fabs (point.Z - box.Center.Z) <= (box.Extent.Z + WWMATH_EPSILON)))
  1546. {
  1547. (*percent) = t_values[index];
  1548. (*intersection_point) = point;
  1549. retval = true;
  1550. }
  1551. }
  1552. }
  1553. return retval;
  1554. }
  1555. ////////////////////////////////////////////////////////////////
  1556. //
  1557. // fnCompareWaypathPortalsCallback
  1558. //
  1559. ////////////////////////////////////////////////////////////////
  1560. int __cdecl
  1561. fnCompareWaypathPortalsCallback
  1562. (
  1563. const void *elem1,
  1564. const void *elem2
  1565. )
  1566. {
  1567. WWASSERT (elem1 != NULL);
  1568. WWASSERT (elem2 != NULL);
  1569. PathfindWaypathPortalClass *portal1 = *((PathfindWaypathPortalClass **)elem1);
  1570. PathfindWaypathPortalClass *portal2 = *((PathfindWaypathPortalClass **)elem2);
  1571. const WaypathPositionClass &pos1 = portal1->Get_Waypath_Pos ();
  1572. const WaypathPositionClass &pos2 = portal2->Get_Waypath_Pos ();
  1573. //
  1574. // Sort based on the waypath segment the portals lie one
  1575. //
  1576. int result = 0;
  1577. if (pos1.Get_Waypoint_Index () < pos2.Get_Waypoint_Index ()) {
  1578. result = -1;
  1579. } else if (pos1.Get_Waypoint_Index () > pos2.Get_Waypoint_Index ()) {
  1580. result = 1;
  1581. } else {
  1582. //
  1583. // If the portals lie on the same segment, then sort by their
  1584. // position along the segment
  1585. //
  1586. if (pos1.Get_Percent () < pos2.Get_Percent ()) {
  1587. result = -1;
  1588. } else if (pos1.Get_Percent () > pos2.Get_Percent ()) {
  1589. result = 1;
  1590. }
  1591. }
  1592. return result;
  1593. }
  1594. //////////////////////////////////////////////////////////////////////////////////
  1595. //
  1596. // Find_Portals
  1597. //
  1598. //////////////////////////////////////////////////////////////////////////////////
  1599. void
  1600. PathfindClass::Find_Portals
  1601. (
  1602. const Vector3 & p0,
  1603. const Vector3 & p1,
  1604. DynamicVectorClass<PathfindPortalClass *> & list,
  1605. bool action_portals_only
  1606. )
  1607. {
  1608. //
  1609. // Loop over all the portals
  1610. //
  1611. for (int index = 0; index < m_PortalList.Count (); index ++) {
  1612. PathfindPortalClass *portal = m_PortalList[index];
  1613. PathfindActionPortalClass *action_portal = portal->As_PathfindActionPortalClass ();
  1614. if (action_portals_only == false || (action_portal != NULL && portal->Get_Action_Type () != PathClass::ACTION_NONE)) {
  1615. //
  1616. // Get the bounding box for the portal
  1617. //
  1618. AABoxClass box;
  1619. portal->Get_Bounding_Box (box);
  1620. //
  1621. // Does the line pass through this box?
  1622. //
  1623. float percent1 = 0;
  1624. Vector3 intersection_pt (0, 0, 0);
  1625. if (::Find_Intersection_Point (box, p0, p1, &percent1, &intersection_pt)) {
  1626. bool is_valid = true;
  1627. //
  1628. // If this is an action portal request, we need to ensure that the
  1629. // line segment passes through to the exit portal
  1630. //
  1631. if (action_portals_only) {
  1632. is_valid = false;
  1633. PathfindSectorClass *sector = action_portal->Peek_Dest_Sector (NULL);
  1634. if (sector != NULL) {
  1635. //
  1636. // Get a pointer to the exit portal
  1637. //
  1638. PathfindPortalClass *exit_portal = action_portal->Get_Exit_Portal ();
  1639. if (exit_portal != NULL) {
  1640. //
  1641. // Does the line segment pass through to the exit portal?
  1642. //
  1643. AABoxClass exit_box;
  1644. exit_portal->Get_Bounding_Box (exit_box);
  1645. if (::Find_Intersection_Point (exit_box, p0, p1,
  1646. &percent1, &intersection_pt))
  1647. {
  1648. float dist1 = (box.Center - p0).Length2 ();
  1649. float dist2 = (exit_box.Center - p0).Length2 ();
  1650. if (dist1 < dist2) {
  1651. is_valid = true;
  1652. }
  1653. }
  1654. }
  1655. }
  1656. }
  1657. //
  1658. // Add the portal to the list
  1659. //
  1660. if (is_valid) {
  1661. list.Add (portal);
  1662. }
  1663. }
  1664. }
  1665. }
  1666. return ;
  1667. }
  1668. //////////////////////////////////////////////////////////////////////////////////
  1669. //
  1670. // Render_Debug_Widgets
  1671. //
  1672. //////////////////////////////////////////////////////////////////////////////////
  1673. void
  1674. PathfindClass::Render_Debug_Widgets (RenderInfoClass &rinfo)
  1675. {
  1676. m_SectorDisplayWidgets.Render_Debug_Widgets (rinfo);
  1677. m_PortalDisplayWidgets.Render_Debug_Widgets (rinfo);
  1678. return ;
  1679. }