pathsolve.cpp 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : wwphys *
  23. * *
  24. * $Archive:: /Commando/Code/wwphys/pathsolve.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 12/10/01 12:40p $*
  29. * *
  30. * $Revision:: 20 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "pathsolve.h"
  36. #include <windows.h>
  37. #include "pathfind.h"
  38. #include "pathfindportal.h"
  39. #include "pathnode.h"
  40. #include "pathdebugplotter.h"
  41. #include "pathobject.h"
  42. #include "accessiblephys.h"
  43. #include "chunkio.h"
  44. #include "pathmgr.h"
  45. #include "wwmemlog.h"
  46. #include "systimer.h"
  47. ////////////////////////////////////////////////////////////////
  48. // Save/Load constants
  49. ////////////////////////////////////////////////////////////////
  50. enum
  51. {
  52. CHUNKID_VARIABLES = 0x11040604,
  53. };
  54. enum
  55. {
  56. VARID_STARTPOS = 1,
  57. VARID_DESTPOS,
  58. VARID_START_SECTOR,
  59. VARID_DEST_SECTOR,
  60. VARID_PATH_OBJECT,
  61. VARID_OLD_PTR,
  62. VARID_PRIORITY
  63. };
  64. ///////////////////////////////////////////////////////////////////////////
  65. // Static member initialization
  66. ///////////////////////////////////////////////////////////////////////////
  67. __int64 PathSolveClass::_TicksPerMilliSec = 0;
  68. ///////////////////////////////////////////////////////////////////////////
  69. // Local inlines
  70. ///////////////////////////////////////////////////////////////////////////
  71. ///////////////////////////////////////////////////////////////////////////
  72. //
  73. // Get_Time
  74. //
  75. ///////////////////////////////////////////////////////////////////////////
  76. static inline __int64
  77. Get_Time (void)
  78. {
  79. __int64 curr_time = 0;
  80. ::QueryPerformanceCounter ((LARGE_INTEGER *)&curr_time);
  81. return curr_time;
  82. }
  83. ///////////////////////////////////////////////////////////////////////////
  84. //
  85. // Shrink_Box
  86. //
  87. ///////////////////////////////////////////////////////////////////////////
  88. static inline void
  89. Shrink_Box (AABoxClass &box, float width)
  90. {
  91. if (box.Extent.X > width) {
  92. box.Extent.X -= width;
  93. } else {
  94. box.Extent.X = 0.25F;
  95. }
  96. if (box.Extent.Y > width) {
  97. box.Extent.Y -= width;
  98. } else {
  99. box.Extent.Y = 0.25F;
  100. }
  101. return ;
  102. }
  103. ///////////////////////////////////////////////////////////////////////////
  104. //
  105. // Clip_Point
  106. //
  107. ///////////////////////////////////////////////////////////////////////////
  108. static inline bool
  109. Clip_Point (Vector3 *point, const AABoxClass &box)
  110. {
  111. Vector3 temp_point = *point;
  112. //
  113. // Clip the temporary point
  114. //
  115. temp_point.X = max (temp_point.X, box.Center.X - box.Extent.X);
  116. temp_point.Y = max (temp_point.Y, box.Center.Y - box.Extent.Y);
  117. temp_point.Z = max (temp_point.Z, box.Center.Z - box.Extent.Z);
  118. temp_point.X = min (temp_point.X, box.Center.X + box.Extent.X);
  119. temp_point.Y = min (temp_point.Y, box.Center.Y + box.Extent.Y);
  120. temp_point.Z = min (temp_point.Z, box.Center.Z + box.Extent.Z);
  121. //
  122. // Did the clip change the point?
  123. //
  124. bool retval = (point->X != temp_point.X);
  125. retval |= (point->Y != temp_point.Y);
  126. retval |= (point->Z != temp_point.Z);
  127. //
  128. // Pass the new point back to the caller
  129. //
  130. (*point) = temp_point;
  131. return retval;
  132. }
  133. ///////////////////////////////////////////////////////////////////////////
  134. //
  135. // Clip_Point
  136. //
  137. ///////////////////////////////////////////////////////////////////////////
  138. static inline void
  139. Clip_Point (Vector3 *point, const AABoxClass &box, float edge_dist)
  140. {
  141. Vector3 temp_point = *point;
  142. AABoxClass clip_box = box;
  143. if (clip_box.Extent.X > edge_dist) {
  144. clip_box.Extent.X -= edge_dist;
  145. } else {
  146. clip_box.Extent.X = 0;
  147. }
  148. if (clip_box.Extent.Y > edge_dist) {
  149. clip_box.Extent.Y -= edge_dist;
  150. } else {
  151. clip_box.Extent.Y = 0;
  152. }
  153. //
  154. // Clip the temporary point
  155. //
  156. temp_point.X = max (temp_point.X, clip_box.Center.X - clip_box.Extent.X);
  157. temp_point.Y = max (temp_point.Y, clip_box.Center.Y - clip_box.Extent.Y);
  158. temp_point.Z = max (temp_point.Z, clip_box.Center.Z - clip_box.Extent.Z);
  159. temp_point.X = min (temp_point.X, clip_box.Center.X + clip_box.Extent.X);
  160. temp_point.Y = min (temp_point.Y, clip_box.Center.Y + clip_box.Extent.Y);
  161. temp_point.Z = min (temp_point.Z, clip_box.Center.Z + clip_box.Extent.Z);
  162. //
  163. // Pass the new point back to the caller
  164. //
  165. (*point) = temp_point;
  166. return ;
  167. }
  168. ///////////////////////////////////////////////////////////////////////////
  169. //
  170. // PathSolveClass
  171. //
  172. ///////////////////////////////////////////////////////////////////////////
  173. PathSolveClass::PathSolveClass (void)
  174. : m_StartPos (0, 0, 0),
  175. m_DestPos (0, 0, 0),
  176. m_StartSector (NULL),
  177. m_DestSector (NULL),
  178. m_CompletedNode (NULL),
  179. m_State (ERROR_INVALID_START_POS),
  180. m_BinaryHeap (10000),
  181. m_Priority (0.5F),
  182. m_BirthTime (0)
  183. {
  184. //
  185. // Determine how many performance-counter ticks
  186. // per millisecond we will get.
  187. //
  188. if (_TicksPerMilliSec == 0) {
  189. ::QueryPerformanceFrequency ((LARGE_INTEGER *)&_TicksPerMilliSec);
  190. _TicksPerMilliSec /= 1000;
  191. }
  192. m_NodeList.Set_Growth_Step (500);
  193. return ;
  194. }
  195. ///////////////////////////////////////////////////////////////////////////
  196. //
  197. // PathSolveClass
  198. //
  199. ///////////////////////////////////////////////////////////////////////////
  200. PathSolveClass::PathSolveClass (const Vector3 &start, const Vector3 &dest)
  201. : m_StartPos (0, 0, 0),
  202. m_DestPos (0, 0, 0),
  203. m_StartSector (NULL),
  204. m_DestSector (NULL),
  205. m_CompletedNode (NULL),
  206. m_State (ERROR_INVALID_START_POS),
  207. m_BinaryHeap (10000),
  208. m_Priority (0.5F),
  209. m_BirthTime (0)
  210. {
  211. //
  212. // Determine how many performance-counter ticks
  213. // per millisecond we will get.
  214. //
  215. if (_TicksPerMilliSec == 0) {
  216. ::QueryPerformanceFrequency ((LARGE_INTEGER *)&_TicksPerMilliSec);
  217. _TicksPerMilliSec /= 1000;
  218. }
  219. Reset (start, dest);
  220. m_NodeList.Set_Growth_Step (500);
  221. return ;
  222. }
  223. ///////////////////////////////////////////////////////////////////////////
  224. //
  225. // ~PathSolveClass
  226. //
  227. ///////////////////////////////////////////////////////////////////////////
  228. PathSolveClass::~PathSolveClass (void)
  229. {
  230. Reset_Lists ();
  231. return ;
  232. }
  233. ///////////////////////////////////////////////////////////////////////////
  234. //
  235. // Reset
  236. //
  237. ///////////////////////////////////////////////////////////////////////////
  238. PathSolveClass::STATE_DESC
  239. PathSolveClass::Reset
  240. (
  241. const Vector3 &start,
  242. const Vector3 &dest,
  243. float sector_fudge
  244. )
  245. {
  246. WWMEMLOG(MEM_PATHFIND);
  247. Reset_Lists ();
  248. m_StartPos = start;
  249. m_DestPos = dest;
  250. Initialize (sector_fudge);
  251. return m_State;
  252. }
  253. ///////////////////////////////////////////////////////////////////////////
  254. //
  255. // Begin_Distributed_Solve
  256. //
  257. ///////////////////////////////////////////////////////////////////////////
  258. /*void
  259. PathSolveClass::Begin_Distributed_Solve (void)
  260. {
  261. for (int index = 0; index < m_NodeList.Count (); index ++) {
  262. PathNodeClass *node = m_NodeList[index];
  263. if (node != NULL) {
  264. node->Reconnect_To_Portal ();
  265. }
  266. }
  267. return ;
  268. }*/
  269. ///////////////////////////////////////////////////////////////////////////
  270. //
  271. // Unlink_Pathfind_Hooks
  272. //
  273. ///////////////////////////////////////////////////////////////////////////
  274. void
  275. PathSolveClass::Unlink_Pathfind_Hooks (void)
  276. {
  277. for (int index = 0; index < m_NodeList.Count (); index ++) {
  278. m_NodeList[index]->Disconnect_From_Portal ();
  279. }
  280. return ;
  281. }
  282. ///////////////////////////////////////////////////////////////////////////
  283. //
  284. // Resolve_Path
  285. //
  286. ///////////////////////////////////////////////////////////////////////////
  287. void
  288. PathSolveClass::Resolve_Path (unsigned int milliseconds)
  289. {
  290. WWMEMLOG(MEM_PATHFIND);
  291. __int64 start_time = Get_Time ();
  292. __int64 end_time = start_time + (((__int64)milliseconds) * _TicksPerMilliSec);
  293. int iterations = 0;
  294. //Begin_Distributed_Solve ();
  295. do
  296. {
  297. //
  298. // Pop the least cost path 'node' from the open list and process
  299. // all of its adjacent sectors.
  300. //
  301. PathNodeClass *node = (PathNodeClass *)m_BinaryHeap.Remove_Min ();
  302. //
  303. // Have we found our path?
  304. //
  305. if (node == NULL) {
  306. m_State = ERROR_NO_PATH;
  307. } else if (node->Peek_Sector () == m_DestSector) {
  308. m_State = SOLVED_PATH;
  309. //
  310. // Record this path as 'final'
  311. //
  312. m_CompletedNode = node;
  313. m_CompletedNode->Add_Ref ();
  314. //
  315. // Mark all the nodes that are on the final path
  316. //
  317. for ( PathNodeClass *path_node = m_CompletedNode;
  318. path_node != NULL;
  319. path_node = path_node->Peek_Parent_Node ())
  320. {
  321. path_node->On_Final_Path (true);
  322. }
  323. //
  324. // 'Straighten' the path as much as possible
  325. //
  326. Post_Process_Path ();
  327. } else {
  328. Process_Portals (node);
  329. }
  330. iterations ++;
  331. //
  332. //
  333. // Keep going until we've either found the path, or we ran out of time.
  334. //
  335. } while ((m_State == THINKING) && (Get_Time () < end_time));
  336. //End_Distributed_Solve ();
  337. //WWDebug_Printf ("Time spent in pathfind: %d 1/100 millis, loops = %d, finished = %d.\r\n", (unsigned int)((Get_Time () - start_time) / (_TicksPerMilliSec/100)), iterations, (int)(m_State == TRAVERSING_PATH));
  338. return ;
  339. }
  340. ///////////////////////////////////////////////////////////////////////////
  341. //
  342. // Timestep
  343. //
  344. ///////////////////////////////////////////////////////////////////////////
  345. PathSolveClass::STATE_DESC
  346. PathSolveClass::Timestep (unsigned int milliseconds)
  347. {
  348. //
  349. // Spend some time trying to resolve the path
  350. //
  351. if (m_State == THINKING) {
  352. Resolve_Path (milliseconds);
  353. }
  354. return m_State;
  355. }
  356. ///////////////////////////////////////////////////////////////////////////
  357. //
  358. // Initialize
  359. //
  360. ///////////////////////////////////////////////////////////////////////////
  361. void
  362. PathSolveClass::Initialize (float sector_fudge)
  363. {
  364. m_State = THINKING;
  365. m_CompletedNode = NULL;
  366. m_BirthTime = TIMEGETTIME ();
  367. //
  368. // Lookup the start and destination sectors
  369. //
  370. m_StartSector = PathfindClass::Get_Instance ()->Find_Sector (m_StartPos, sector_fudge);
  371. m_DestSector = PathfindClass::Get_Instance ()->Find_Sector (m_DestPos, sector_fudge);
  372. //
  373. // Sanity check, are the starting and ending points
  374. // inside of pathfind sectors?
  375. //
  376. if (m_StartSector == NULL) {
  377. m_State = ERROR_INVALID_START_POS;
  378. } else if (m_DestSector == NULL) {
  379. m_State = ERROR_INVALID_DEST_POS;
  380. } else {
  381. //
  382. // Clip the start and destination points inside the sectors if necessary.
  383. //
  384. //if (sector_fudge > 0) {
  385. //::Clip_Point (&m_StartPos, m_StartSector->Get_Bounding_Box (), m_PathObject.Get_Width ());
  386. ::Clip_Point (&m_StartPos, m_StartSector->Get_Bounding_Box ());
  387. ::Clip_Point (&m_DestPos, m_DestSector->Get_Bounding_Box ());
  388. //}
  389. }
  390. return ;
  391. }
  392. ///////////////////////////////////////////////////////////////////////////
  393. //
  394. // Process_Initial_Sector
  395. //
  396. ///////////////////////////////////////////////////////////////////////////
  397. void
  398. PathSolveClass::Process_Initial_Sector (void)
  399. {
  400. if (m_StartSector == NULL) {
  401. return ;
  402. }
  403. //
  404. // Create a set of path 'nodes' that represent all the portals of the
  405. // starting sector.
  406. //
  407. int index = m_StartSector->Get_Portal_Count ();
  408. while (index --) {
  409. PathfindPortalClass *portal = m_StartSector->Peek_Portal (index);
  410. if (portal != NULL) {
  411. AABoxClass portal_box;
  412. portal->Get_Bounding_Box (portal_box);
  413. //
  414. // Find where we should enter the portal...
  415. //
  416. Vector3 dest_point = m_StartPos;
  417. ::Clip_Point (&dest_point, portal_box, m_PathObject.Get_Width ());
  418. dest_point.Z = portal_box.Center.Z - (portal_box.Extent.Z - 0.1F);
  419. //
  420. // Determine how we should be facing when we enter the portal.
  421. //
  422. Vector3 x_vector = dest_point - m_StartPos;
  423. Vector3 y_vector;
  424. Vector3 z_vector (0, 0, 1);
  425. x_vector.Normalize ();
  426. y_vector = Vector3::Cross_Product (x_vector, z_vector);
  427. Matrix3D ending_tm (x_vector, y_vector, z_vector, dest_point);
  428. //
  429. // Sumbit a node for this portal
  430. //
  431. if (Does_Object_Have_Access_To_Portal (portal)) {
  432. Submit_Node ( 0,
  433. NULL,
  434. portal,
  435. portal->Peek_Dest_Sector (m_StartSector),
  436. Matrix3D (m_StartPos),
  437. ending_tm);
  438. }
  439. }
  440. }
  441. if (m_StartSector == m_DestSector) {
  442. //
  443. // If the start and destination are the same
  444. // sector, then we can just beeline.
  445. //
  446. m_State = SOLVED_PATH;
  447. m_Path.Delete_All ();
  448. m_Path.Add (PathDataStruct (NULL, m_StartPos));
  449. m_Path.Add (PathDataStruct (NULL, m_DestPos));
  450. } else {
  451. m_State = THINKING;
  452. }
  453. return ;
  454. }
  455. //
  456. //
  457. // Steps:
  458. //
  459. //
  460. // - For each portal in the current sector:
  461. // - Can the object drive to that portal?
  462. // - Yes: Can the object fit in the portal?
  463. // - Yes: Can the object fit in the dest sector?
  464. // - Yes: Record the node, stick it on the open list.
  465. // - No: Punt
  466. // - No: Punt
  467. // - No: Punt
  468. //
  469. //
  470. //
  471. //
  472. ///////////////////////////////////////////////////////////////////////////
  473. //
  474. // Find_Tangent_Angle
  475. //
  476. ///////////////////////////////////////////////////////////////////////////
  477. float
  478. Find_Tangent_Angle
  479. (
  480. const Vector2 & center,
  481. float radius,
  482. const Vector2 & point,
  483. bool clockwise
  484. )
  485. {
  486. float angle = 0;
  487. //
  488. // Calculate the distance from the point to the center of the circle
  489. //
  490. float delta_x = point.X - center.X;
  491. float delta_y = point.Y - center.Y;
  492. float dist = ::sqrt (delta_x * delta_x + delta_y * delta_y);
  493. //
  494. // Determine the offset angle (from the line between the point and center)
  495. // where the 2 tangent points lie.
  496. //
  497. float angle_offset = WWMath::Acos (radius / dist);
  498. float base_angle = WWMath::Atan2 (delta_x, -delta_y);
  499. base_angle = WWMath::Wrap (base_angle, 0, WWMATH_PI * 2);
  500. //
  501. // Return which ever tangent point we would come across first, depending
  502. // on our orientation
  503. //
  504. if (clockwise) {
  505. angle = (WWMATH_PI * 3) - (base_angle + angle_offset);
  506. } else {
  507. angle = base_angle - angle_offset;
  508. }
  509. angle = WWMath::Wrap (angle, 0, WWMATH_PI * 2);
  510. return angle;
  511. }
  512. ///////////////////////////////////////////////////////////////////////////
  513. //
  514. // Get_Turn_Arc_Center
  515. //
  516. ///////////////////////////////////////////////////////////////////////////
  517. void
  518. Get_Turn_Arc_Center
  519. (
  520. float wheel_offset,
  521. float turn_radius,
  522. bool left_turn,
  523. Vector2 * arc_center
  524. )
  525. {
  526. arc_center->X = wheel_offset;
  527. if (left_turn) {
  528. arc_center->Y = turn_radius;
  529. } else {
  530. arc_center->Y = -turn_radius;
  531. }
  532. return ;
  533. }
  534. ///////////////////////////////////////////////////////////////////////////
  535. //
  536. // Find_Adjacent_Portals
  537. //
  538. ///////////////////////////////////////////////////////////////////////////
  539. void
  540. Find_Adjacent_Portals
  541. (
  542. PathfindSectorClass *sector,
  543. PathfindPortalClass *portal,
  544. PathfindSectorClass *dest_sector,
  545. PathfindPortalClass **portal1,
  546. PathfindPortalClass **portal2
  547. )
  548. {
  549. AABoxClass portal_box;
  550. portal->Get_Bounding_Box (portal_box);
  551. float x_value1 = 0;
  552. float x_value2 = 0;
  553. float y_value1 = 0;
  554. float y_value2 = 0;
  555. bool compare_x = false;
  556. if (portal_box.Extent.X > portal_box.Extent.Y) {
  557. x_value1 = portal_box.Center.X - portal_box.Extent.X;
  558. x_value2 = portal_box.Center.X + portal_box.Extent.X;
  559. y_value1 = portal_box.Center.Y;
  560. y_value2 = portal_box.Center.Y;
  561. compare_x = true;
  562. } else {
  563. x_value1 = portal_box.Center.X;
  564. x_value2 = portal_box.Center.X;
  565. y_value1 = portal_box.Center.Y - portal_box.Extent.Y;
  566. y_value2 = portal_box.Center.Y + portal_box.Extent.Y;
  567. compare_x = false;
  568. }
  569. int index = sector->Get_Portal_Count ();
  570. while (index --) {
  571. PathfindPortalClass *curr_portal = sector->Peek_Portal (index);
  572. if (curr_portal && curr_portal != portal) {
  573. AABoxClass curr_box;
  574. curr_portal->Get_Bounding_Box (curr_box);
  575. if (compare_x) {
  576. float curr_x1 = curr_box.Center.X + curr_box.Extent.X;
  577. float curr_x2 = curr_box.Center.X - curr_box.Extent.X;
  578. float curr_y = curr_box.Center.Y;
  579. //
  580. // Is this the y-value we are looking for?
  581. //
  582. if (WWMath::Fabs (curr_y - y_value1) <= WWMATH_EPSILON) {
  583. //
  584. // Is this either the left or right adjacent portal?
  585. //
  586. if (WWMath::Fabs (curr_x1 - x_value1) <= WWMATH_EPSILON) {
  587. (*portal1) = curr_portal;
  588. } else if (WWMath::Fabs (curr_x2 - x_value2) <= WWMATH_EPSILON) {
  589. (*portal2) = curr_portal;
  590. }
  591. }
  592. } else {
  593. float curr_y1 = curr_box.Center.Y + curr_box.Extent.Y;
  594. float curr_y2 = curr_box.Center.Y - curr_box.Extent.Y;
  595. float curr_x = curr_box.Center.X;
  596. //
  597. // Is this the x-value we are looking for?
  598. //
  599. if (WWMath::Fabs (curr_x - x_value1) <= WWMATH_EPSILON) {
  600. //
  601. // Is this either the up or down adjacent portal?
  602. //
  603. if (WWMath::Fabs (curr_y1 - y_value1) <= WWMATH_EPSILON) {
  604. (*portal1) = curr_portal;
  605. } else if (WWMath::Fabs (curr_y2 - y_value2) <= WWMATH_EPSILON) {
  606. (*portal2) = curr_portal;
  607. }
  608. }
  609. }
  610. }
  611. }
  612. return ;
  613. }
  614. ///////////////////////////////////////////////////////////////////////////
  615. //
  616. // Does_Object_Fit_Through_Portal
  617. //
  618. ///////////////////////////////////////////////////////////////////////////
  619. bool
  620. Does_Object_Fit_Through_Portal
  621. (
  622. const PathObjectClass & object,
  623. PathfindSectorClass * sector,
  624. PathfindPortalClass * portal
  625. )
  626. {
  627. bool retval = false;
  628. AABoxClass portal_box;
  629. AABoxClass obj_box;
  630. portal->Get_Bounding_Box (portal_box);
  631. object.Get_Collision_Box (obj_box);
  632. float min_pos = 0;
  633. float max_pos = 0;
  634. if (portal_box.Extent.X > portal_box.Extent.Y) {
  635. min_pos = portal_box.Center.X - portal_box.Extent.X;
  636. max_pos = portal_box.Center.X + portal_box.Extent.X;
  637. } else {
  638. min_pos = portal_box.Center.Y - portal_box.Extent.Y;
  639. max_pos = portal_box.Center.Y + portal_box.Extent.Y;
  640. }
  641. //
  642. // Can we fit through this portal?
  643. //
  644. if (object.Get_Width () <= (max_pos - min_pos)) {
  645. retval = true;
  646. } else {
  647. //
  648. // This object could not fit through the given portal, so try to
  649. // find any adjacent portals that might pass through a different
  650. // sector but would end up in the correct sector. We will combine
  651. // the total length of these portals and see if the object fits...
  652. //
  653. PathfindSectorClass *dest_sector = portal->Peek_Dest_Sector (sector);
  654. //
  655. // Try to find two adjacent portals which can make it to the destination
  656. // sector...
  657. //
  658. PathfindPortalClass *portal1 = NULL;
  659. PathfindPortalClass *portal2 = NULL;
  660. ::Find_Adjacent_Portals (sector, portal, dest_sector, &portal1, &portal2);
  661. //
  662. // If the vehicle can fit in through the combined portals, then we will
  663. // allow the vehicle to pass...
  664. //
  665. if (portal1 != NULL || portal2 != NULL) {
  666. //
  667. // We assume portal1 is 'less-then' the test portal, so use its
  668. // position and size to determine the lower bounds of our extended portal.
  669. //
  670. if (portal1 != NULL) {
  671. AABoxClass temp_box;
  672. portal1->Get_Bounding_Box (temp_box);
  673. if (portal_box.Extent.X > portal_box.Extent.Y) {
  674. min_pos = temp_box.Center.X - temp_box.Extent.X;
  675. } else {
  676. min_pos = temp_box.Center.Y - temp_box.Extent.Y;
  677. }
  678. }
  679. //
  680. // We assume portal2 is 'greater-then' the test portal, so use its
  681. // position and size to determine the upper bounds of our extended portal.
  682. //
  683. if (portal2 != NULL) {
  684. AABoxClass temp_box;
  685. portal2->Get_Bounding_Box (temp_box);
  686. if (portal_box.Extent.X > portal_box.Extent.Y) {
  687. max_pos = temp_box.Center.X + temp_box.Extent.X;
  688. } else {
  689. max_pos = temp_box.Center.Y + temp_box.Extent.Y;
  690. }
  691. }
  692. //
  693. // Can we fit through this extended portal?
  694. //
  695. if (object.Get_Width () <= (max_pos - min_pos)) {
  696. retval = true;
  697. }
  698. }
  699. }
  700. return retval;
  701. }
  702. ///////////////////////////////////////////////////////////////////////////
  703. //
  704. // Does_Object_Have_Access_To_Portal
  705. //
  706. ///////////////////////////////////////////////////////////////////////////
  707. bool
  708. PathSolveClass::Does_Object_Have_Access_To_Portal (PathfindPortalClass *portal)
  709. {
  710. bool retval = true;
  711. //
  712. // Does this portal interact with some sort of mechanism
  713. //
  714. PathfindActionPortalClass *action_portal = portal->As_PathfindActionPortalClass ();
  715. if ( action_portal != NULL &&
  716. action_portal->Get_Action_Type () == PathClass::ACTION_MECHANISM)
  717. {
  718. //
  719. // Lookup the mechanism this portal uses
  720. //
  721. uint32 mechanism_id = action_portal->Get_Mechanism_ID ();
  722. StaticPhysClass *mechanism = PhysicsSceneClass::Get_Instance ()->Find_Static_Object (mechanism_id);
  723. if (mechanism != NULL) {
  724. AccessiblePhysClass *accessible_obj = mechanism->As_AccessiblePhysClass ();
  725. //
  726. // See if we can unlock this mechanism
  727. //
  728. if (accessible_obj != NULL && accessible_obj->Get_Lock_Code () != 0) {
  729. retval = accessible_obj->Can_Unlock (m_PathObject.Get_Key_Ring ());
  730. }
  731. }
  732. }
  733. return retval;
  734. }
  735. ///////////////////////////////////////////////////////////////////////////
  736. //
  737. // Can_Object_Go_Through_Portal
  738. //
  739. ///////////////////////////////////////////////////////////////////////////
  740. bool
  741. PathSolveClass::Can_Object_Go_Through_Portal
  742. (
  743. const Matrix3D & current_tm,
  744. PathfindSectorClass * sector,
  745. PathfindPortalClass * portal,
  746. Matrix3D * ending_tm
  747. )
  748. {
  749. bool retval = false;
  750. //
  751. // Calculate the size of the portal we are trying
  752. // to pass through
  753. //
  754. AABoxClass portal_box;
  755. portal->Get_Bounding_Box (portal_box);
  756. //
  757. // Can we fit through this portal?
  758. //
  759. if ( portal->Does_Size_Matter () == false ||
  760. ::Does_Object_Fit_Through_Portal (m_PathObject, sector, portal))
  761. {
  762. //
  763. // Find where we should enter the portal...
  764. //
  765. Vector3 curr_pos = current_tm.Get_Translation ();
  766. Vector3 dest_point = curr_pos;
  767. if (portal->As_PathfindActionPortalClass () != NULL) {
  768. dest_point = portal_box.Center;
  769. //::Clip_Point (&dest_point, portal_box, m_PathObject.Get_Width ());
  770. } else {
  771. ::Clip_Point (&dest_point, portal_box, m_PathObject.Get_Width () * 2);
  772. }
  773. dest_point.Z = portal_box.Center.Z - (portal_box.Extent.Z - 0.1F);
  774. //
  775. // Calculate a complete transform for the portal enter position
  776. //
  777. Vector3 x_vector = dest_point - curr_pos;
  778. Vector3 y_vector;
  779. Vector3 z_vector (0, 0, 1);
  780. x_vector.Normalize ();
  781. y_vector = Vector3::Cross_Product (x_vector, z_vector);
  782. ending_tm->Set (x_vector, y_vector, z_vector, dest_point);
  783. retval = true;
  784. }
  785. return retval;
  786. }
  787. ///////////////////////////////////////////////////////////////////////////
  788. //
  789. // Can_Object_Enter_Portal
  790. //
  791. ///////////////////////////////////////////////////////////////////////////
  792. bool
  793. Can_Object_Enter_Portal
  794. (
  795. const AABoxClass & portal_box,
  796. const PathObjectClass & object,
  797. const Vector3 & direction_vector
  798. )
  799. {
  800. bool retval = false;
  801. AABoxClass obj_box;
  802. object.Get_Collision_Box (obj_box);
  803. //
  804. // Can this object be rotated about Z?
  805. //
  806. if (object.Is_Flag_Set (PathObjectClass::CAN_BOX_ROTATE)) {
  807. //
  808. // Build a rotation matrix from the direction vector
  809. //
  810. Vector3 x_vector = ::Normalize (direction_vector);
  811. Vector3 z_vector (0, 0, 1);
  812. Vector3 y_vector = Vector3::Cross_Product (x_vector, z_vector);
  813. Matrix3 tm (x_vector, y_vector, z_vector);
  814. //
  815. // Rotate the edges of the box (toss the z component, we don't care)
  816. //
  817. Vector3 v1 (obj_box.Extent.X, -obj_box.Extent.Y, 0);
  818. Vector3 v2 (obj_box.Extent.X, obj_box.Extent.Y, 0);
  819. Vector3 v3 (-obj_box.Extent.X, obj_box.Extent.Y, 0);
  820. Vector3 v4 (-obj_box.Extent.X, -obj_box.Extent.Y, 0);
  821. v1 = tm * v1;
  822. v2 = tm * v2;
  823. v3 = tm * v3;
  824. v4 = tm * v4;
  825. //
  826. // Determine the new axis-aligned extents from this rotated box
  827. //
  828. float max_x = max (WWMath::Fabs (v1.X), WWMath::Fabs (v2.X));
  829. max_x = max (max_x, WWMath::Fabs (v3.X));
  830. max_x = max (max_x, WWMath::Fabs (v4.X));
  831. float max_y = max (WWMath::Fabs (v1.Y), WWMath::Fabs (v2.Y));
  832. max_y = max (max_y, WWMath::Fabs (v3.Y));
  833. max_y = max (max_y, WWMath::Fabs (v4.Y));
  834. obj_box.Extent.X = max_x / 2;
  835. obj_box.Extent.Y = max_y / 2;
  836. }
  837. //
  838. // Portal boxes are generally long and very skinny.
  839. // So skinny in fact they are conceptually planes.
  840. // We find out which direction they are skinny in
  841. // and simulate 'shoving' the object through in
  842. // that direction.
  843. //
  844. if (portal_box.Extent.X > portal_box.Extent.Y) {
  845. retval = (obj_box.Extent.X < portal_box.Extent.X);
  846. } else {
  847. retval = (obj_box.Extent.Y < portal_box.Extent.Y);
  848. }
  849. return retval;
  850. }
  851. ///////////////////////////////////////////////////////////////////////////
  852. //
  853. // Process_Portals
  854. //
  855. ///////////////////////////////////////////////////////////////////////////
  856. void
  857. PathSolveClass::Process_Portals (PathNodeClass *node)
  858. {
  859. PathfindSectorClass *sector = node->Peek_Sector ();
  860. PathfindPortalClass *last_portal = node->Peek_Portal ();
  861. const Matrix3D &current_tm = node->Get_Transform ();
  862. //
  863. // Loop over all the portals that this sector has access to
  864. //
  865. for (int index = 0; index < sector->Get_Portal_Count (); index ++) {
  866. PathfindPortalClass *portal = sector->Peek_Portal (index);
  867. //
  868. // Don't process any portals we can't get to from the current portal
  869. //
  870. if (sector->Can_Access_Portal (last_portal, portal)) {
  871. //
  872. // Get this portal's destination
  873. //
  874. PathfindSectorClass *dest_sector = portal->Peek_Dest_Sector (sector);
  875. if (dest_sector != NULL) {
  876. //
  877. // Determine if we can pass through this portal, and if so where
  878. // we should pass through...
  879. //
  880. Matrix3D ending_tm (1);
  881. if ( Does_Object_Have_Access_To_Portal (portal) &&
  882. Can_Object_Go_Through_Portal (current_tm, sector, portal, &ending_tm))
  883. {
  884. //
  885. // Submit a new node for the destination sector
  886. //
  887. Submit_Node (node->Get_Traversal_Cost (), node, portal,
  888. dest_sector, current_tm, ending_tm);
  889. }
  890. }
  891. }
  892. }
  893. if (last_portal != NULL) {
  894. last_portal->m_ClosedListPtr = node;
  895. }
  896. return ;
  897. }
  898. ///////////////////////////////////////////////////////////////////////////
  899. //
  900. // Calculate_Heuristic_Cost
  901. //
  902. ///////////////////////////////////////////////////////////////////////////
  903. float
  904. Calculate_Heuristic_Cost
  905. (
  906. const PathObjectClass & object,
  907. const PathfindPortalClass &portal,
  908. const PathfindSectorClass &sector,
  909. const Vector3 & curr_pos,
  910. const Vector3 & dest_pos
  911. )
  912. {
  913. float cost = (curr_pos - dest_pos).Length () * 0.9F;
  914. if (sector.As_PathfindWaypathSectorClass () != NULL) {
  915. //
  916. // Make waypath sectors slightly more appealing
  917. //
  918. cost = cost * 0.5F;
  919. } else if (object.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
  920. //
  921. // Do some special case heuristics for vehicles
  922. //
  923. //
  924. // Make one-way portals very expensive for vehicles
  925. //
  926. if (portal.Does_Size_Matter () && portal.Is_Two_Way_Portal () == false) {
  927. cost = cost * 2.0F;
  928. }
  929. AABoxClass sector_box = sector.Get_Bounding_Box ();
  930. float sector_area = (sector_box.Extent.X * sector_box.Extent.Y);
  931. //AABoxClass object_box;
  932. //object.Get_Collision_Box (object_box);
  933. //float object_area = (object_box.Extent.X * object_box.Extent.Y);
  934. float object_area = 50.0F;
  935. float factor = (object_area / sector_area);
  936. factor = max (factor, 0.01F);
  937. factor = min (factor, 100.0F);
  938. cost = cost * factor;
  939. }
  940. return cost;
  941. }
  942. ///////////////////////////////////////////////////////////////////////////
  943. //
  944. // Submit_Node
  945. //
  946. ///////////////////////////////////////////////////////////////////////////
  947. void
  948. PathSolveClass::Submit_Node
  949. (
  950. float traversal_cost,
  951. PathNodeClass * current_node,
  952. PathfindPortalClass * portal,
  953. PathfindSectorClass * dest_sector,
  954. const Matrix3D & current_tm,
  955. const Matrix3D & ending_tm
  956. )
  957. {
  958. const Vector3 &current_pos = current_tm.Get_Translation ();
  959. const Vector3 &dest_pos = ending_tm.Get_Translation ();
  960. //
  961. // Calculate the total traversal cost for this path.
  962. //
  963. Vector3 dest_vector = current_pos - dest_pos;
  964. dest_vector.Z = 0;
  965. float dest_dist = dest_vector.Length ();
  966. float current_traversal_cost = traversal_cost + dest_dist;
  967. //
  968. // Calculate the heuristic for this portal
  969. //
  970. float heuristic_cost = ::Calculate_Heuristic_Cost (m_PathObject, *portal, *dest_sector,
  971. dest_pos, m_DestPos);
  972. //
  973. // Try to keep vehicles from taking one-way portals
  974. //
  975. if ( m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE) &&
  976. portal->Is_Two_Way_Portal () == false)
  977. {
  978. current_traversal_cost += dest_dist * 2.0F;
  979. }
  980. //
  981. // Is this sector already in the open list?
  982. //
  983. int open_index = portal->Get_Heap_Location ();
  984. if (open_index > 0) {
  985. PathNodeClass *open_version = (PathNodeClass *)m_BinaryHeap.Peek_Node (open_index);
  986. //
  987. // If the traversal cost is lower from our current 'path', then
  988. // remove the old path and re-insert the new path.
  989. //
  990. if (current_traversal_cost < open_version->Get_Traversal_Cost ()) {
  991. open_version->Set_Sector (dest_sector);
  992. open_version->Set_Parent_Node (current_node);
  993. open_version->Set_Traversal_Cost (current_traversal_cost);
  994. open_version->Set_Transform (ending_tm);
  995. open_version->Set_Heuristic_Cost (heuristic_cost);
  996. m_BinaryHeap.Percolate_Up (open_index);
  997. }
  998. } else {
  999. //
  1000. // Is this sector already in the closed list?
  1001. //
  1002. if (portal->m_ClosedListPtr != NULL) {
  1003. PathNodeClass *closed_version = portal->m_ClosedListPtr;
  1004. //
  1005. // If the traversal cost is lower from our current 'path', then
  1006. // remove the old path and re-insert the new path.
  1007. //
  1008. if (current_traversal_cost < closed_version->Get_Traversal_Cost ()) {
  1009. portal->m_ClosedListPtr = NULL;
  1010. closed_version->Set_Sector (dest_sector);
  1011. closed_version->Set_Parent_Node (current_node);
  1012. closed_version->Set_Traversal_Cost (current_traversal_cost);
  1013. closed_version->Set_Transform (ending_tm);
  1014. closed_version->Set_Heuristic_Cost (heuristic_cost);
  1015. m_BinaryHeap.Insert (closed_version);
  1016. }
  1017. } else {
  1018. //
  1019. // Create a new path 'node' that represents that path from start
  1020. // up until this sector.
  1021. //
  1022. PathNodeClass *new_node = new PathNodeClass;
  1023. new_node->Set_Sector (dest_sector);
  1024. new_node->Set_Parent_Node (current_node);
  1025. new_node->Set_Portal (portal);
  1026. //
  1027. // Calculate the path's total traversal cost
  1028. //
  1029. new_node->Set_Traversal_Cost (current_traversal_cost);
  1030. new_node->Set_Transform (ending_tm);
  1031. new_node->Set_Heuristic_Cost (heuristic_cost);
  1032. //
  1033. // Insert this node into the open list
  1034. //
  1035. m_BinaryHeap.Insert (new_node);
  1036. //
  1037. // Keep track of this node's pointer (for cleanup)
  1038. //
  1039. m_NodeList.Add (new_node);
  1040. }
  1041. }
  1042. return ;
  1043. }
  1044. ///////////////////////////////////////////////////////////////////////////
  1045. //
  1046. // Reset_Lists
  1047. //
  1048. ///////////////////////////////////////////////////////////////////////////
  1049. void
  1050. PathSolveClass::Reset_Lists (void)
  1051. {
  1052. //
  1053. // Ensure we unlink our data before we destroy it...
  1054. //
  1055. if (PathMgrClass::Peek_Active_Path () == this) {
  1056. Unlink_Pathfind_Hooks ();
  1057. }
  1058. REF_PTR_RELEASE (m_CompletedNode);
  1059. //
  1060. // Free all our nodes
  1061. //
  1062. for (int index = 0; index < m_NodeList.Count (); index ++) {
  1063. PathNodeClass *node = m_NodeList[index];
  1064. REF_PTR_RELEASE (node);
  1065. }
  1066. m_BinaryHeap.Flush_Array ();
  1067. m_NodeList.Reset_Active ();
  1068. return ;
  1069. }
  1070. /////////////////////////////////////////////////////////////////////////////////
  1071. //
  1072. // Does_Line_Go_Through_Portal
  1073. //
  1074. /////////////////////////////////////////////////////////////////////////////////
  1075. bool
  1076. PathSolveClass::Does_Line_Go_Through_Portal
  1077. (
  1078. const Vector3 & start,
  1079. const Vector3 & end,
  1080. const AABoxClass & box
  1081. )
  1082. {
  1083. float delta_x = end.X - start.X;
  1084. float delta_y = end.Y - start.Y;
  1085. float percent = 1000;
  1086. if ((box.Extent.X <= box.Extent.Y) && (delta_x != 0)) {
  1087. //
  1088. // Calculate where the line intersects the X plane
  1089. //
  1090. percent = (box.Center.X - start.X) / delta_x;
  1091. } else if ((box.Extent.Y <= box.Extent.X) && (delta_y != 0)) {
  1092. //
  1093. // Calculate where the line intersects the Y plane
  1094. //
  1095. percent = (box.Center.Y - start.Y) / delta_y;
  1096. }
  1097. //
  1098. // Calculate the point where the line intersects one of the
  1099. // planes of the box.
  1100. //
  1101. Vector3 dest_point = start + ((end - start) * percent);
  1102. //
  1103. // Check to make sure this point isn't outside the bounds of the
  1104. // box, if it is outside the bounds, then the line DOES NOT pass
  1105. // through the portal.
  1106. //
  1107. bool retval = true;
  1108. if ((box.Center.X + box.Extent.X) < dest_point.X) retval = false;
  1109. if ((box.Center.Y + box.Extent.Y) < dest_point.Y) retval = false;
  1110. if ((box.Center.X - box.Extent.X) > dest_point.X) retval = false;
  1111. if ((box.Center.Y - box.Extent.Y) > dest_point.Y) retval = false;
  1112. return retval;
  1113. }
  1114. /////////////////////////////////////////////////////////////////////////////////
  1115. //
  1116. // Relax_Line
  1117. //
  1118. /////////////////////////////////////////////////////////////////////////////////
  1119. Vector3
  1120. PathSolveClass::Relax_Line (const Vector3 &start, const Vector3 &end, const AABoxClass &box)
  1121. {
  1122. Vector3 result = box.Center;
  1123. float delta_x = end.X - start.X;
  1124. float delta_y = end.Y - start.Y;
  1125. float percent = 1000;
  1126. if ((box.Extent.X < box.Extent.Y) && (delta_x != 0)) {
  1127. //
  1128. // Calculate where the line intersects the X plane
  1129. //
  1130. percent = (box.Center.X - start.X) / delta_x;
  1131. } else if ((box.Extent.Y < box.Extent.X) && (delta_y != 0)) {
  1132. //
  1133. // Calculate where the line intersects the Y plane
  1134. //
  1135. percent = (box.Center.Y - start.Y) / delta_y;
  1136. }
  1137. //
  1138. // Calculate the point where the line intersects one of the
  1139. // planes of the box.
  1140. //
  1141. //if (percent >= 0 && percent < 1.0F) {
  1142. result = start + ((end - start) * percent);
  1143. Clip_Point (&result, box, m_PathObject.Get_Width () * 3.0F);
  1144. //result = box.Center;
  1145. //result.Z = box.Center.Z - (box.Extent.Z - 0.1F);
  1146. /*} else {
  1147. int test = 0;
  1148. }*/
  1149. return result;
  1150. }
  1151. /////////////////////////////////////////////////////////////////////////////////
  1152. //
  1153. // Check_Line
  1154. //
  1155. // Note: The purpose of this function is to determine if the unit's collision
  1156. // box will be jutting outside the sector at the point where the path begins
  1157. // to enter the portal.
  1158. //
  1159. //
  1160. /////////////////////////////////////////////////////////////////////////////////
  1161. bool
  1162. Check_Line
  1163. (
  1164. const Vector3 & p0,
  1165. const Vector3 & p1,
  1166. const AABoxClass & sector,
  1167. const AABoxClass & portal,
  1168. float width,
  1169. Vector3 * result
  1170. )
  1171. {
  1172. bool retval = false;
  1173. if ( WWMath::Fabs (portal.Center.X - sector.Center.X + sector.Extent.X) < WWMATH_EPSILON ||
  1174. WWMath::Fabs (portal.Center.X - sector.Center.X - sector.Extent.X) < WWMATH_EPSILON)
  1175. {
  1176. float y_val0 = portal.Center.Y - portal.Extent.Y;
  1177. float y_val1 = portal.Center.Y + portal.Extent.Y;
  1178. bool do_it = false;
  1179. float y_val = 0;
  1180. if ( (p0.Y < y_val0 && p1.Y > y_val0) ||
  1181. (p0.Y > y_val0 && p1.Y < y_val0))
  1182. {
  1183. do_it = true;
  1184. y_val = y_val0;
  1185. } else if ( (p0.Y < y_val1 && p1.Y > y_val1) ||
  1186. (p0.Y > y_val1 && p1.Y < y_val1))
  1187. {
  1188. do_it = true;
  1189. y_val = y_val1;
  1190. }
  1191. if (do_it) {
  1192. float percent = (y_val - p0.Y) / (p1.Y - p0.Y);
  1193. Vector3 pos = p0 + (p1 - p0) * percent;
  1194. if (pos.X + width > (sector.Center.X + sector.Extent.X)) {
  1195. (*result) = pos;
  1196. result->X = (sector.Center.X + sector.Extent.X) - width;
  1197. result->X = max (result->X, sector.Center.X);
  1198. retval = true;
  1199. } else if (pos.X - width < (sector.Center.X - sector.Extent.X)) {
  1200. (*result) = pos;
  1201. result->X = (sector.Center.X - sector.Extent.X) + width;
  1202. result->X = min (result->X, sector.Center.X);
  1203. retval = true;
  1204. }
  1205. }
  1206. } else if ( WWMath::Fabs (portal.Center.Y - sector.Center.Y + sector.Extent.Y) < WWMATH_EPSILON ||
  1207. WWMath::Fabs (portal.Center.Y - sector.Center.Y - sector.Extent.Y) < WWMATH_EPSILON)
  1208. {
  1209. float x_val0 = portal.Center.X - portal.Extent.X;
  1210. float x_val1 = portal.Center.X + portal.Extent.X;
  1211. bool do_it = false;
  1212. float x_val = 0;
  1213. if ( (p0.X < x_val0 && p1.X > x_val0) ||
  1214. (p0.X > x_val0 && p1.X < x_val0))
  1215. {
  1216. do_it = true;
  1217. x_val = x_val0;
  1218. } else if ( (p0.X < x_val1 && p1.X > x_val1) ||
  1219. (p0.X > x_val1 && p1.X < x_val1))
  1220. {
  1221. do_it = true;
  1222. x_val = x_val1;
  1223. }
  1224. if (do_it) {
  1225. float percent = (x_val - p0.X) / (p1.X - p0.X);
  1226. Vector3 pos = p0 + (p1 - p0) * percent;
  1227. if (pos.Y + width > (sector.Center.Y + sector.Extent.Y)) {
  1228. (*result) = pos;
  1229. result->Y = (sector.Center.Y + sector.Extent.Y) - width;
  1230. result->Y = max (result->Y, sector.Center.Y);
  1231. retval = true;
  1232. } else if (pos.Y - width < (sector.Center.Y - sector.Extent.Y)) {
  1233. (*result) = pos;
  1234. result->Y = (sector.Center.Y - sector.Extent.Y) + width;
  1235. result->Y = min (result->Y, sector.Center.Y);
  1236. retval = true;
  1237. }
  1238. }
  1239. }
  1240. return retval;
  1241. }
  1242. ///////////////////////////////////////////////////////////////////////////
  1243. //
  1244. // Post_Process_Path
  1245. //
  1246. ///////////////////////////////////////////////////////////////////////////
  1247. PathSolveClass::PATHNODE_LIST PathSolveClass::temp_node_list;
  1248. void
  1249. PathSolveClass::Post_Process_Path (void)
  1250. {
  1251. m_Path.Delete_All ();
  1252. //
  1253. // Build a list of the nodes (in order) the path passes through.
  1254. //
  1255. temp_node_list.Reset_Active();
  1256. for (PathNodeClass *node = m_CompletedNode; node != NULL; node = node->Peek_Parent_Node ()) {
  1257. temp_node_list.Add_Head (node);
  1258. }
  1259. //
  1260. // Just do it
  1261. //
  1262. m_Path.Add (PathDataStruct (NULL, m_StartPos));
  1263. m_Path[m_Path.Count () - 1].m_SectorCenter = m_StartSector->Get_Bounding_Box ().Center;
  1264. m_Path[m_Path.Count () - 1].m_SectorExtent = m_StartSector->Get_Bounding_Box ().Extent;
  1265. for (int index = 0; index < temp_node_list.Count (); index ++) {
  1266. PathNodeClass *node = temp_node_list[index];
  1267. PathfindPortalClass *portal = node->Peek_Portal ();
  1268. m_Path.Add (PathDataStruct (portal, node->Get_Position ()));
  1269. m_Path[m_Path.Count () - 1].m_SectorCenter.Set (0, 0, 0);
  1270. m_Path[m_Path.Count () - 1].m_SectorExtent.Set (0, 0, 0);
  1271. PathfindSectorClass *sector = node->Peek_Sector ();
  1272. if (sector != NULL) {
  1273. m_Path[m_Path.Count () - 1].m_SectorCenter = sector->Get_Bounding_Box ().Center;
  1274. m_Path[m_Path.Count () - 1].m_SectorExtent = sector->Get_Bounding_Box ().Extent;
  1275. }
  1276. }
  1277. m_Path.Add (PathDataStruct (NULL, m_DestPos));
  1278. m_Path[m_Path.Count () - 1].m_SectorCenter = m_DestSector->Get_Bounding_Box ().Center;
  1279. m_Path[m_Path.Count () - 1].m_SectorExtent = m_DestSector->Get_Bounding_Box ().Extent;
  1280. //
  1281. // Try to adjust the path so it doesn't make any sharp turns. To do this
  1282. // we will average the closest portal points starting from the beginning
  1283. // and then starting from the end.
  1284. //
  1285. typedef struct
  1286. {
  1287. Vector3 backward;
  1288. Vector3 forward;
  1289. } PATH_POINT;
  1290. int count = m_Path.Count ();
  1291. PATH_POINT *path_points = new PATH_POINT[count];
  1292. Vector3 next_point = m_DestPos;
  1293. for (index = m_Path.Count () - 2; index > 0; index --) {
  1294. //
  1295. // Do we have a portal we can clip the point to?
  1296. //
  1297. PathfindPortalClass *portal = m_Path[index].m_Portal;
  1298. if (portal != NULL && portal->As_PathfindActionPortalClass () == NULL) {
  1299. //
  1300. // Clip the point to the portal's box
  1301. //
  1302. AABoxClass portal_box;
  1303. portal->Get_Bounding_Box (portal_box);
  1304. Vector3 curr_point = next_point;
  1305. ::Clip_Point (&curr_point, portal_box, m_PathObject.Get_Width () * 3.0F);
  1306. //
  1307. // Move the point to the floor
  1308. //
  1309. curr_point.Z = portal_box.Center.Z - portal_box.Extent.Z;
  1310. //
  1311. // Add this point to the array
  1312. //
  1313. path_points[index].backward = curr_point;
  1314. next_point = curr_point;
  1315. } else {
  1316. //
  1317. // No portal, so simply add the point to the array
  1318. //
  1319. path_points[index].backward = m_Path[index].m_Point;
  1320. next_point = m_Path[index].m_Point;
  1321. }
  1322. }
  1323. next_point = m_StartPos;
  1324. for (index = 1; index < m_Path.Count () - 1; index ++) {
  1325. //
  1326. // Do we have a portal we can clip the point to?
  1327. //
  1328. PathfindPortalClass *portal = m_Path[index].m_Portal;
  1329. if (portal != NULL && portal->As_PathfindActionPortalClass () == NULL) {
  1330. //
  1331. // Clip the point to the portal's box
  1332. //
  1333. AABoxClass portal_box;
  1334. portal->Get_Bounding_Box (portal_box);
  1335. Vector3 curr_point = next_point;
  1336. ::Clip_Point (&curr_point, portal_box, m_PathObject.Get_Width () * 3.0F);
  1337. //
  1338. // Move the point to the floor
  1339. //
  1340. curr_point.Z = portal_box.Center.Z - portal_box.Extent.Z;
  1341. //
  1342. // Add this point to the array
  1343. //
  1344. path_points[index].forward = curr_point;
  1345. next_point = curr_point;
  1346. } else {
  1347. //
  1348. // No portal, so simply add the point to the array
  1349. //
  1350. path_points[index].forward = m_Path[index].m_Point;
  1351. next_point = m_Path[index].m_Point;
  1352. }
  1353. }
  1354. //
  1355. // Now average the points
  1356. //
  1357. for (index = 1; index < m_Path.Count () - 1; index ++) {
  1358. Vector3 avg_point = (path_points[index].forward + path_points[index].backward) * 0.5F;
  1359. m_Path[index].m_Point = avg_point;
  1360. }
  1361. delete [] path_points;
  1362. //
  1363. // Relax the points
  1364. //
  1365. for (index = m_Path.Count () - 2; index > 1; index --) {
  1366. Vector3 &prev_point = m_Path[index + 1].m_Point;
  1367. Vector3 &next_point = m_Path[index - 1].m_Point;
  1368. PathfindPortalClass *portal = m_Path[index].m_Portal;
  1369. if (portal != NULL && portal->As_PathfindActionPortalClass () == NULL) {
  1370. //
  1371. // Relax the between the last point and the next point, making
  1372. // sure the point lies in the poral box
  1373. //
  1374. AABoxClass portal_box;
  1375. portal->Get_Bounding_Box (portal_box);
  1376. m_Path[index].m_Point = Relax_Line (prev_point, next_point, portal_box);
  1377. }
  1378. }
  1379. //
  1380. // Ensure that no object will poke its bounding box outside of
  1381. // the sector it's in
  1382. //
  1383. Keep_Unit_Inside_Sectors ();
  1384. return ;
  1385. }
  1386. /////////////////////////////////////////////////////////////////////////////////
  1387. //
  1388. // Keep_Unit_Inside_Sectors
  1389. //
  1390. /////////////////////////////////////////////////////////////////////////////////
  1391. void
  1392. PathSolveClass::Keep_Unit_Inside_Sectors (void)
  1393. {
  1394. if (m_Path.Count () == 0) {
  1395. return ;
  1396. }
  1397. //
  1398. // Clip the starting point so that the unit will stay on the inside of its sector
  1399. //
  1400. Vector3 temp_point = m_Path[0].m_Point;
  1401. AABoxClass sector_box (m_Path[0].m_SectorCenter, m_Path[0].m_SectorExtent);
  1402. ::Clip_Point (&temp_point, sector_box, m_PathObject.Get_Width () * 2);
  1403. m_Path[0].m_Point = temp_point;
  1404. //
  1405. // Now check each "line" along the path to see if the
  1406. // unit would jut out at any point
  1407. //
  1408. for (int index = 0; index < m_Path.Count () - 1; index ++) {
  1409. Vector3 curr_point = m_Path[index].m_Point;
  1410. Vector3 next_point = m_Path[index + 1].m_Point;
  1411. //
  1412. // Is the current point contained in a sector, and will it
  1413. // be passing through a portal
  1414. //
  1415. if ( (m_Path[index].m_SectorExtent.X > 0) ||
  1416. (m_Path[index].m_SectorExtent.Y > 0))
  1417. {
  1418. //
  1419. // Get the sector's box
  1420. //
  1421. AABoxClass sector_box (m_Path[index].m_SectorCenter, m_Path[index].m_SectorExtent);
  1422. float width = m_PathObject.Get_Width () * 3;
  1423. Vector3 start_point = curr_point;
  1424. //
  1425. // Lookup the current and next portals
  1426. //
  1427. PathfindPortalClass *prev_portal = m_Path[index].m_Portal;
  1428. PathfindPortalClass *next_portal = m_Path[index + 1].m_Portal;
  1429. //
  1430. // Test the object at the point it will be entering the sector
  1431. //
  1432. if (prev_portal != NULL && prev_portal->As_PathfindActionPortalClass () == NULL) {
  1433. //
  1434. // Get the portal's box
  1435. //
  1436. AABoxClass portal_box;
  1437. prev_portal->Get_Bounding_Box (portal_box);
  1438. //
  1439. // Check to see if the object will jut outside the sector as it moves
  1440. // along this line
  1441. //
  1442. Vector3 new_point (0, 0, 0);
  1443. if (::Check_Line (start_point, next_point, sector_box, portal_box, width, &new_point)) {
  1444. //
  1445. // Yup, we would jut outside the sector, so add a new point
  1446. // to avoid this
  1447. //
  1448. m_Path.Insert (index + 1, PathDataStruct (NULL, new_point));
  1449. index ++;
  1450. start_point = new_point;
  1451. }
  1452. }
  1453. //
  1454. // Test the object at the point it will be exiting the sector
  1455. //
  1456. if (next_portal != NULL && next_portal->As_PathfindActionPortalClass () == NULL) {
  1457. //
  1458. // Get the portal's box
  1459. //
  1460. AABoxClass portal_box;
  1461. next_portal->Get_Bounding_Box (portal_box);
  1462. //
  1463. // Check to see if the object will jut outside the sector as it moves
  1464. // along this line
  1465. //
  1466. Vector3 new_point (0, 0, 0);
  1467. if (::Check_Line (start_point, next_point, sector_box, portal_box, width, &new_point)) {
  1468. //
  1469. // Yup, we would jut outside the sector, so add a new point
  1470. // to avoid this
  1471. //
  1472. m_Path.Insert (index + 1, PathDataStruct (NULL, new_point));
  1473. index ++;
  1474. }
  1475. }
  1476. }
  1477. }
  1478. return ;
  1479. }
  1480. /////////////////////////////////////////////////////////////////////////////////
  1481. //
  1482. // Set_Path_Object
  1483. //
  1484. /////////////////////////////////////////////////////////////////////////////////
  1485. void
  1486. PathSolveClass::Set_Path_Object (PathObjectClass &path_object)
  1487. {
  1488. m_PathObject = path_object;
  1489. return ;
  1490. }
  1491. /////////////////////////////////////////////////////////////////////////////////
  1492. //
  1493. // Get_Path_Object
  1494. //
  1495. /////////////////////////////////////////////////////////////////////////////////
  1496. void
  1497. PathSolveClass::Get_Path_Object (PathObjectClass &path_object) const
  1498. {
  1499. path_object = m_PathObject;
  1500. return ;
  1501. }
  1502. /////////////////////////////////////////////////////////////////////////////////
  1503. //
  1504. // Get_Volumes
  1505. //
  1506. /////////////////////////////////////////////////////////////////////////////////
  1507. void
  1508. PathSolveClass::Get_Volumes
  1509. (
  1510. BOX_LIST &sector_list,
  1511. BOX_LIST &portal_list
  1512. )
  1513. {
  1514. //
  1515. // Build a list of the nodes (in order) the path passes through.
  1516. //
  1517. for ( PathNodeClass *node = m_CompletedNode;
  1518. node != NULL;
  1519. node = node->Peek_Parent_Node ())
  1520. {
  1521. //
  1522. // Add the box from the sector to the list
  1523. //
  1524. AABoxClass *box = new AABoxClass (node->Peek_Sector ()->Get_Bounding_Box ());
  1525. //box->Extent.X = max (0.0F, (box->Extent.X - m_PathObject.Get_Width () * 3));
  1526. //box->Extent.Y = max (0.0F, (box->Extent.Y - m_PathObject.Get_Width () * 3));
  1527. sector_list.Add (box);
  1528. //
  1529. // If this node has a portal, then add the portal's box to the list
  1530. //
  1531. if (node->Peek_Portal () != NULL) {
  1532. AABoxClass *portal_box = new AABoxClass;
  1533. node->Peek_Portal ()->Get_Bounding_Box (*portal_box);
  1534. portal_list.Add (portal_box);
  1535. }
  1536. }
  1537. return ;
  1538. }
  1539. ////////////////////////////////////////////////////////////////////////////////////////////
  1540. //
  1541. // Save
  1542. //
  1543. ////////////////////////////////////////////////////////////////////////////////////////////
  1544. void
  1545. PathSolveClass::Save (ChunkSaveClass &csave)
  1546. {
  1547. csave.Begin_Chunk (CHUNKID_VARIABLES);
  1548. //
  1549. // Save each variable to its own microchunk
  1550. //
  1551. WRITE_MICRO_CHUNK (csave, VARID_STARTPOS, m_StartPos);
  1552. WRITE_MICRO_CHUNK (csave, VARID_DESTPOS, m_DestPos);
  1553. WRITE_MICRO_CHUNK (csave, VARID_START_SECTOR, m_StartSector);
  1554. WRITE_MICRO_CHUNK (csave, VARID_DEST_SECTOR, m_DestSector);
  1555. WRITE_MICRO_CHUNK (csave, VARID_PATH_OBJECT, m_PathObject);
  1556. WRITE_MICRO_CHUNK (csave, VARID_PRIORITY, m_Priority);
  1557. PathSolveClass *this_ptr = this;
  1558. WRITE_MICRO_CHUNK (csave, VARID_OLD_PTR, this_ptr);
  1559. csave.End_Chunk ();
  1560. return ;
  1561. }
  1562. ////////////////////////////////////////////////////////////////////////////////////////////
  1563. //
  1564. // Load
  1565. //
  1566. ////////////////////////////////////////////////////////////////////////////////////////////
  1567. void
  1568. PathSolveClass::Load (ChunkLoadClass &cload)
  1569. {
  1570. while (cload.Open_Chunk ()) {
  1571. switch (cload.Cur_Chunk_ID ()) {
  1572. case CHUNKID_VARIABLES:
  1573. Load_Variables (cload);
  1574. break;
  1575. }
  1576. cload.Close_Chunk ();
  1577. }
  1578. SaveLoadSystemClass::Register_Post_Load_Callback (this);
  1579. return ;
  1580. }
  1581. ///////////////////////////////////////////////////////////////////////
  1582. //
  1583. // On_Post_Load
  1584. //
  1585. ///////////////////////////////////////////////////////////////////////
  1586. void
  1587. PathSolveClass::On_Post_Load (void)
  1588. {
  1589. //
  1590. // This basically kicks off the pathfind
  1591. //
  1592. Initialize (1.0F);
  1593. return ;
  1594. }
  1595. ///////////////////////////////////////////////////////////////////////
  1596. //
  1597. // Load_Variables
  1598. //
  1599. ///////////////////////////////////////////////////////////////////////
  1600. void
  1601. PathSolveClass::Load_Variables (ChunkLoadClass &cload)
  1602. {
  1603. PathSolveClass *old_ptr = NULL;
  1604. m_StartSector = NULL;
  1605. m_DestSector = NULL;
  1606. //
  1607. // Loop through all the microchunks that define the variables
  1608. //
  1609. while (cload.Open_Micro_Chunk ()) {
  1610. switch (cload.Cur_Micro_Chunk_ID ()) {
  1611. READ_MICRO_CHUNK (cload, VARID_STARTPOS, m_StartPos);
  1612. READ_MICRO_CHUNK (cload, VARID_DESTPOS, m_DestPos);
  1613. READ_MICRO_CHUNK (cload, VARID_PATH_OBJECT, m_PathObject);
  1614. READ_MICRO_CHUNK (cload, VARID_OLD_PTR, old_ptr);
  1615. READ_MICRO_CHUNK (cload, VARID_PRIORITY, m_Priority);
  1616. }
  1617. cload.Close_Micro_Chunk ();
  1618. }
  1619. //
  1620. // Register our old ptr so other objects can remap to us
  1621. //
  1622. if (old_ptr != NULL) {
  1623. SaveLoadSystemClass::Register_Pointer (old_ptr, this);
  1624. }
  1625. return ;
  1626. }