| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /***********************************************************************************************
- *** 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 ***
- ***********************************************************************************************
- * *
- * Project Name : wwphys *
- * *
- * $Archive:: /Commando/Code/wwphys/pathsolve.cpp $*
- * *
- * Author:: Patrick Smith *
- * *
- * $Modtime:: 12/10/01 12:40p $*
- * *
- * $Revision:: 20 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "pathsolve.h"
- #include <windows.h>
- #include "pathfind.h"
- #include "pathfindportal.h"
- #include "pathnode.h"
- #include "pathdebugplotter.h"
- #include "pathobject.h"
- #include "accessiblephys.h"
- #include "chunkio.h"
- #include "pathmgr.h"
- #include "wwmemlog.h"
- #include "systimer.h"
- ////////////////////////////////////////////////////////////////
- // Save/Load constants
- ////////////////////////////////////////////////////////////////
- enum
- {
- CHUNKID_VARIABLES = 0x11040604,
- };
- enum
- {
- VARID_STARTPOS = 1,
- VARID_DESTPOS,
- VARID_START_SECTOR,
- VARID_DEST_SECTOR,
- VARID_PATH_OBJECT,
- VARID_OLD_PTR,
- VARID_PRIORITY
- };
- ///////////////////////////////////////////////////////////////////////////
- // Static member initialization
- ///////////////////////////////////////////////////////////////////////////
- __int64 PathSolveClass::_TicksPerMilliSec = 0;
- ///////////////////////////////////////////////////////////////////////////
- // Local inlines
- ///////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////
- //
- // Get_Time
- //
- ///////////////////////////////////////////////////////////////////////////
- static inline __int64
- Get_Time (void)
- {
- __int64 curr_time = 0;
- ::QueryPerformanceCounter ((LARGE_INTEGER *)&curr_time);
- return curr_time;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Shrink_Box
- //
- ///////////////////////////////////////////////////////////////////////////
- static inline void
- Shrink_Box (AABoxClass &box, float width)
- {
- if (box.Extent.X > width) {
- box.Extent.X -= width;
- } else {
- box.Extent.X = 0.25F;
- }
- if (box.Extent.Y > width) {
- box.Extent.Y -= width;
- } else {
- box.Extent.Y = 0.25F;
- }
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Clip_Point
- //
- ///////////////////////////////////////////////////////////////////////////
- static inline bool
- Clip_Point (Vector3 *point, const AABoxClass &box)
- {
- Vector3 temp_point = *point;
- //
- // Clip the temporary point
- //
- temp_point.X = max (temp_point.X, box.Center.X - box.Extent.X);
- temp_point.Y = max (temp_point.Y, box.Center.Y - box.Extent.Y);
- temp_point.Z = max (temp_point.Z, box.Center.Z - box.Extent.Z);
- temp_point.X = min (temp_point.X, box.Center.X + box.Extent.X);
- temp_point.Y = min (temp_point.Y, box.Center.Y + box.Extent.Y);
- temp_point.Z = min (temp_point.Z, box.Center.Z + box.Extent.Z);
- //
- // Did the clip change the point?
- //
- bool retval = (point->X != temp_point.X);
- retval |= (point->Y != temp_point.Y);
- retval |= (point->Z != temp_point.Z);
- //
- // Pass the new point back to the caller
- //
- (*point) = temp_point;
- return retval;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Clip_Point
- //
- ///////////////////////////////////////////////////////////////////////////
- static inline void
- Clip_Point (Vector3 *point, const AABoxClass &box, float edge_dist)
- {
- Vector3 temp_point = *point;
- AABoxClass clip_box = box;
- if (clip_box.Extent.X > edge_dist) {
- clip_box.Extent.X -= edge_dist;
- } else {
- clip_box.Extent.X = 0;
- }
- if (clip_box.Extent.Y > edge_dist) {
- clip_box.Extent.Y -= edge_dist;
- } else {
- clip_box.Extent.Y = 0;
- }
- //
- // Clip the temporary point
- //
- temp_point.X = max (temp_point.X, clip_box.Center.X - clip_box.Extent.X);
- temp_point.Y = max (temp_point.Y, clip_box.Center.Y - clip_box.Extent.Y);
- temp_point.Z = max (temp_point.Z, clip_box.Center.Z - clip_box.Extent.Z);
- temp_point.X = min (temp_point.X, clip_box.Center.X + clip_box.Extent.X);
- temp_point.Y = min (temp_point.Y, clip_box.Center.Y + clip_box.Extent.Y);
- temp_point.Z = min (temp_point.Z, clip_box.Center.Z + clip_box.Extent.Z);
- //
- // Pass the new point back to the caller
- //
- (*point) = temp_point;
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // PathSolveClass
- //
- ///////////////////////////////////////////////////////////////////////////
- PathSolveClass::PathSolveClass (void)
- : m_StartPos (0, 0, 0),
- m_DestPos (0, 0, 0),
- m_StartSector (NULL),
- m_DestSector (NULL),
- m_CompletedNode (NULL),
- m_State (ERROR_INVALID_START_POS),
- m_BinaryHeap (10000),
- m_Priority (0.5F),
- m_BirthTime (0)
- {
- //
- // Determine how many performance-counter ticks
- // per millisecond we will get.
- //
- if (_TicksPerMilliSec == 0) {
- ::QueryPerformanceFrequency ((LARGE_INTEGER *)&_TicksPerMilliSec);
- _TicksPerMilliSec /= 1000;
- }
- m_NodeList.Set_Growth_Step (500);
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // PathSolveClass
- //
- ///////////////////////////////////////////////////////////////////////////
- PathSolveClass::PathSolveClass (const Vector3 &start, const Vector3 &dest)
- : m_StartPos (0, 0, 0),
- m_DestPos (0, 0, 0),
- m_StartSector (NULL),
- m_DestSector (NULL),
- m_CompletedNode (NULL),
- m_State (ERROR_INVALID_START_POS),
- m_BinaryHeap (10000),
- m_Priority (0.5F),
- m_BirthTime (0)
- {
- //
- // Determine how many performance-counter ticks
- // per millisecond we will get.
- //
- if (_TicksPerMilliSec == 0) {
- ::QueryPerformanceFrequency ((LARGE_INTEGER *)&_TicksPerMilliSec);
- _TicksPerMilliSec /= 1000;
- }
- Reset (start, dest);
- m_NodeList.Set_Growth_Step (500);
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // ~PathSolveClass
- //
- ///////////////////////////////////////////////////////////////////////////
- PathSolveClass::~PathSolveClass (void)
- {
- Reset_Lists ();
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Reset
- //
- ///////////////////////////////////////////////////////////////////////////
- PathSolveClass::STATE_DESC
- PathSolveClass::Reset
- (
- const Vector3 &start,
- const Vector3 &dest,
- float sector_fudge
- )
- {
- WWMEMLOG(MEM_PATHFIND);
- Reset_Lists ();
- m_StartPos = start;
- m_DestPos = dest;
- Initialize (sector_fudge);
- return m_State;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Begin_Distributed_Solve
- //
- ///////////////////////////////////////////////////////////////////////////
- /*void
- PathSolveClass::Begin_Distributed_Solve (void)
- {
- for (int index = 0; index < m_NodeList.Count (); index ++) {
- PathNodeClass *node = m_NodeList[index];
- if (node != NULL) {
- node->Reconnect_To_Portal ();
- }
- }
- return ;
- }*/
- ///////////////////////////////////////////////////////////////////////////
- //
- // Unlink_Pathfind_Hooks
- //
- ///////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Unlink_Pathfind_Hooks (void)
- {
- for (int index = 0; index < m_NodeList.Count (); index ++) {
- m_NodeList[index]->Disconnect_From_Portal ();
- }
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Resolve_Path
- //
- ///////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Resolve_Path (unsigned int milliseconds)
- {
- WWMEMLOG(MEM_PATHFIND);
- __int64 start_time = Get_Time ();
- __int64 end_time = start_time + (((__int64)milliseconds) * _TicksPerMilliSec);
- int iterations = 0;
- //Begin_Distributed_Solve ();
- do
- {
- //
- // Pop the least cost path 'node' from the open list and process
- // all of its adjacent sectors.
- //
- PathNodeClass *node = (PathNodeClass *)m_BinaryHeap.Remove_Min ();
- //
- // Have we found our path?
- //
- if (node == NULL) {
- m_State = ERROR_NO_PATH;
- } else if (node->Peek_Sector () == m_DestSector) {
- m_State = SOLVED_PATH;
- //
- // Record this path as 'final'
- //
- m_CompletedNode = node;
- m_CompletedNode->Add_Ref ();
- //
- // Mark all the nodes that are on the final path
- //
- for ( PathNodeClass *path_node = m_CompletedNode;
- path_node != NULL;
- path_node = path_node->Peek_Parent_Node ())
- {
- path_node->On_Final_Path (true);
- }
- //
- // 'Straighten' the path as much as possible
- //
- Post_Process_Path ();
- } else {
- Process_Portals (node);
- }
- iterations ++;
- //
- //
- // Keep going until we've either found the path, or we ran out of time.
- //
- } while ((m_State == THINKING) && (Get_Time () < end_time));
- //End_Distributed_Solve ();
- //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));
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Timestep
- //
- ///////////////////////////////////////////////////////////////////////////
- PathSolveClass::STATE_DESC
- PathSolveClass::Timestep (unsigned int milliseconds)
- {
- //
- // Spend some time trying to resolve the path
- //
- if (m_State == THINKING) {
- Resolve_Path (milliseconds);
- }
- return m_State;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Initialize
- //
- ///////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Initialize (float sector_fudge)
- {
- m_State = THINKING;
- m_CompletedNode = NULL;
- m_BirthTime = TIMEGETTIME ();
- //
- // Lookup the start and destination sectors
- //
- m_StartSector = PathfindClass::Get_Instance ()->Find_Sector (m_StartPos, sector_fudge);
- m_DestSector = PathfindClass::Get_Instance ()->Find_Sector (m_DestPos, sector_fudge);
- //
- // Sanity check, are the starting and ending points
- // inside of pathfind sectors?
- //
- if (m_StartSector == NULL) {
- m_State = ERROR_INVALID_START_POS;
- } else if (m_DestSector == NULL) {
- m_State = ERROR_INVALID_DEST_POS;
- } else {
- //
- // Clip the start and destination points inside the sectors if necessary.
- //
- //if (sector_fudge > 0) {
- //::Clip_Point (&m_StartPos, m_StartSector->Get_Bounding_Box (), m_PathObject.Get_Width ());
- ::Clip_Point (&m_StartPos, m_StartSector->Get_Bounding_Box ());
- ::Clip_Point (&m_DestPos, m_DestSector->Get_Bounding_Box ());
- //}
- }
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Process_Initial_Sector
- //
- ///////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Process_Initial_Sector (void)
- {
- if (m_StartSector == NULL) {
- return ;
- }
- //
- // Create a set of path 'nodes' that represent all the portals of the
- // starting sector.
- //
- int index = m_StartSector->Get_Portal_Count ();
- while (index --) {
- PathfindPortalClass *portal = m_StartSector->Peek_Portal (index);
- if (portal != NULL) {
- AABoxClass portal_box;
- portal->Get_Bounding_Box (portal_box);
- //
- // Find where we should enter the portal...
- //
- Vector3 dest_point = m_StartPos;
- ::Clip_Point (&dest_point, portal_box, m_PathObject.Get_Width ());
- dest_point.Z = portal_box.Center.Z - (portal_box.Extent.Z - 0.1F);
- //
- // Determine how we should be facing when we enter the portal.
- //
- Vector3 x_vector = dest_point - m_StartPos;
- Vector3 y_vector;
- Vector3 z_vector (0, 0, 1);
- x_vector.Normalize ();
- y_vector = Vector3::Cross_Product (x_vector, z_vector);
- Matrix3D ending_tm (x_vector, y_vector, z_vector, dest_point);
- //
- // Sumbit a node for this portal
- //
- if (Does_Object_Have_Access_To_Portal (portal)) {
- Submit_Node ( 0,
- NULL,
- portal,
- portal->Peek_Dest_Sector (m_StartSector),
- Matrix3D (m_StartPos),
- ending_tm);
- }
- }
- }
- if (m_StartSector == m_DestSector) {
- //
- // If the start and destination are the same
- // sector, then we can just beeline.
- //
- m_State = SOLVED_PATH;
- m_Path.Delete_All ();
- m_Path.Add (PathDataStruct (NULL, m_StartPos));
- m_Path.Add (PathDataStruct (NULL, m_DestPos));
- } else {
- m_State = THINKING;
- }
- return ;
- }
- //
- //
- // Steps:
- //
- //
- // - For each portal in the current sector:
- // - Can the object drive to that portal?
- // - Yes: Can the object fit in the portal?
- // - Yes: Can the object fit in the dest sector?
- // - Yes: Record the node, stick it on the open list.
- // - No: Punt
- // - No: Punt
- // - No: Punt
- //
- //
- //
- //
- ///////////////////////////////////////////////////////////////////////////
- //
- // Find_Tangent_Angle
- //
- ///////////////////////////////////////////////////////////////////////////
- float
- Find_Tangent_Angle
- (
- const Vector2 & center,
- float radius,
- const Vector2 & point,
- bool clockwise
- )
- {
- float angle = 0;
- //
- // Calculate the distance from the point to the center of the circle
- //
- float delta_x = point.X - center.X;
- float delta_y = point.Y - center.Y;
- float dist = ::sqrt (delta_x * delta_x + delta_y * delta_y);
- //
- // Determine the offset angle (from the line between the point and center)
- // where the 2 tangent points lie.
- //
- float angle_offset = WWMath::Acos (radius / dist);
- float base_angle = WWMath::Atan2 (delta_x, -delta_y);
- base_angle = WWMath::Wrap (base_angle, 0, WWMATH_PI * 2);
- //
- // Return which ever tangent point we would come across first, depending
- // on our orientation
- //
- if (clockwise) {
- angle = (WWMATH_PI * 3) - (base_angle + angle_offset);
- } else {
- angle = base_angle - angle_offset;
- }
- angle = WWMath::Wrap (angle, 0, WWMATH_PI * 2);
- return angle;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Get_Turn_Arc_Center
- //
- ///////////////////////////////////////////////////////////////////////////
- void
- Get_Turn_Arc_Center
- (
- float wheel_offset,
- float turn_radius,
- bool left_turn,
- Vector2 * arc_center
- )
- {
- arc_center->X = wheel_offset;
- if (left_turn) {
- arc_center->Y = turn_radius;
- } else {
- arc_center->Y = -turn_radius;
- }
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Find_Adjacent_Portals
- //
- ///////////////////////////////////////////////////////////////////////////
- void
- Find_Adjacent_Portals
- (
- PathfindSectorClass *sector,
- PathfindPortalClass *portal,
- PathfindSectorClass *dest_sector,
- PathfindPortalClass **portal1,
- PathfindPortalClass **portal2
- )
- {
- AABoxClass portal_box;
- portal->Get_Bounding_Box (portal_box);
- float x_value1 = 0;
- float x_value2 = 0;
- float y_value1 = 0;
- float y_value2 = 0;
- bool compare_x = false;
- if (portal_box.Extent.X > portal_box.Extent.Y) {
- x_value1 = portal_box.Center.X - portal_box.Extent.X;
- x_value2 = portal_box.Center.X + portal_box.Extent.X;
- y_value1 = portal_box.Center.Y;
- y_value2 = portal_box.Center.Y;
- compare_x = true;
- } else {
- x_value1 = portal_box.Center.X;
- x_value2 = portal_box.Center.X;
- y_value1 = portal_box.Center.Y - portal_box.Extent.Y;
- y_value2 = portal_box.Center.Y + portal_box.Extent.Y;
- compare_x = false;
- }
- int index = sector->Get_Portal_Count ();
- while (index --) {
- PathfindPortalClass *curr_portal = sector->Peek_Portal (index);
- if (curr_portal && curr_portal != portal) {
- AABoxClass curr_box;
- curr_portal->Get_Bounding_Box (curr_box);
- if (compare_x) {
- float curr_x1 = curr_box.Center.X + curr_box.Extent.X;
- float curr_x2 = curr_box.Center.X - curr_box.Extent.X;
- float curr_y = curr_box.Center.Y;
- //
- // Is this the y-value we are looking for?
- //
- if (WWMath::Fabs (curr_y - y_value1) <= WWMATH_EPSILON) {
- //
- // Is this either the left or right adjacent portal?
- //
- if (WWMath::Fabs (curr_x1 - x_value1) <= WWMATH_EPSILON) {
- (*portal1) = curr_portal;
- } else if (WWMath::Fabs (curr_x2 - x_value2) <= WWMATH_EPSILON) {
- (*portal2) = curr_portal;
- }
- }
- } else {
- float curr_y1 = curr_box.Center.Y + curr_box.Extent.Y;
- float curr_y2 = curr_box.Center.Y - curr_box.Extent.Y;
- float curr_x = curr_box.Center.X;
- //
- // Is this the x-value we are looking for?
- //
- if (WWMath::Fabs (curr_x - x_value1) <= WWMATH_EPSILON) {
- //
- // Is this either the up or down adjacent portal?
- //
- if (WWMath::Fabs (curr_y1 - y_value1) <= WWMATH_EPSILON) {
- (*portal1) = curr_portal;
- } else if (WWMath::Fabs (curr_y2 - y_value2) <= WWMATH_EPSILON) {
- (*portal2) = curr_portal;
- }
- }
- }
- }
- }
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Does_Object_Fit_Through_Portal
- //
- ///////////////////////////////////////////////////////////////////////////
- bool
- Does_Object_Fit_Through_Portal
- (
- const PathObjectClass & object,
- PathfindSectorClass * sector,
- PathfindPortalClass * portal
- )
- {
- bool retval = false;
- AABoxClass portal_box;
- AABoxClass obj_box;
- portal->Get_Bounding_Box (portal_box);
- object.Get_Collision_Box (obj_box);
- float min_pos = 0;
- float max_pos = 0;
- if (portal_box.Extent.X > portal_box.Extent.Y) {
- min_pos = portal_box.Center.X - portal_box.Extent.X;
- max_pos = portal_box.Center.X + portal_box.Extent.X;
- } else {
- min_pos = portal_box.Center.Y - portal_box.Extent.Y;
- max_pos = portal_box.Center.Y + portal_box.Extent.Y;
- }
- //
- // Can we fit through this portal?
- //
- if (object.Get_Width () <= (max_pos - min_pos)) {
- retval = true;
- } else {
- //
- // This object could not fit through the given portal, so try to
- // find any adjacent portals that might pass through a different
- // sector but would end up in the correct sector. We will combine
- // the total length of these portals and see if the object fits...
- //
- PathfindSectorClass *dest_sector = portal->Peek_Dest_Sector (sector);
- //
- // Try to find two adjacent portals which can make it to the destination
- // sector...
- //
- PathfindPortalClass *portal1 = NULL;
- PathfindPortalClass *portal2 = NULL;
- ::Find_Adjacent_Portals (sector, portal, dest_sector, &portal1, &portal2);
- //
- // If the vehicle can fit in through the combined portals, then we will
- // allow the vehicle to pass...
- //
- if (portal1 != NULL || portal2 != NULL) {
- //
- // We assume portal1 is 'less-then' the test portal, so use its
- // position and size to determine the lower bounds of our extended portal.
- //
- if (portal1 != NULL) {
- AABoxClass temp_box;
- portal1->Get_Bounding_Box (temp_box);
- if (portal_box.Extent.X > portal_box.Extent.Y) {
- min_pos = temp_box.Center.X - temp_box.Extent.X;
- } else {
- min_pos = temp_box.Center.Y - temp_box.Extent.Y;
- }
- }
- //
- // We assume portal2 is 'greater-then' the test portal, so use its
- // position and size to determine the upper bounds of our extended portal.
- //
- if (portal2 != NULL) {
- AABoxClass temp_box;
- portal2->Get_Bounding_Box (temp_box);
- if (portal_box.Extent.X > portal_box.Extent.Y) {
- max_pos = temp_box.Center.X + temp_box.Extent.X;
- } else {
- max_pos = temp_box.Center.Y + temp_box.Extent.Y;
- }
- }
- //
- // Can we fit through this extended portal?
- //
- if (object.Get_Width () <= (max_pos - min_pos)) {
- retval = true;
- }
- }
- }
- return retval;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Does_Object_Have_Access_To_Portal
- //
- ///////////////////////////////////////////////////////////////////////////
- bool
- PathSolveClass::Does_Object_Have_Access_To_Portal (PathfindPortalClass *portal)
- {
- bool retval = true;
- //
- // Does this portal interact with some sort of mechanism
- //
- PathfindActionPortalClass *action_portal = portal->As_PathfindActionPortalClass ();
- if ( action_portal != NULL &&
- action_portal->Get_Action_Type () == PathClass::ACTION_MECHANISM)
- {
- //
- // Lookup the mechanism this portal uses
- //
- uint32 mechanism_id = action_portal->Get_Mechanism_ID ();
- StaticPhysClass *mechanism = PhysicsSceneClass::Get_Instance ()->Find_Static_Object (mechanism_id);
- if (mechanism != NULL) {
- AccessiblePhysClass *accessible_obj = mechanism->As_AccessiblePhysClass ();
- //
- // See if we can unlock this mechanism
- //
- if (accessible_obj != NULL && accessible_obj->Get_Lock_Code () != 0) {
- retval = accessible_obj->Can_Unlock (m_PathObject.Get_Key_Ring ());
- }
- }
- }
- return retval;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Can_Object_Go_Through_Portal
- //
- ///////////////////////////////////////////////////////////////////////////
- bool
- PathSolveClass::Can_Object_Go_Through_Portal
- (
- const Matrix3D & current_tm,
- PathfindSectorClass * sector,
- PathfindPortalClass * portal,
- Matrix3D * ending_tm
- )
- {
- bool retval = false;
- //
- // Calculate the size of the portal we are trying
- // to pass through
- //
- AABoxClass portal_box;
- portal->Get_Bounding_Box (portal_box);
- //
- // Can we fit through this portal?
- //
- if ( portal->Does_Size_Matter () == false ||
- ::Does_Object_Fit_Through_Portal (m_PathObject, sector, portal))
- {
- //
- // Find where we should enter the portal...
- //
- Vector3 curr_pos = current_tm.Get_Translation ();
- Vector3 dest_point = curr_pos;
- if (portal->As_PathfindActionPortalClass () != NULL) {
- dest_point = portal_box.Center;
- //::Clip_Point (&dest_point, portal_box, m_PathObject.Get_Width ());
- } else {
- ::Clip_Point (&dest_point, portal_box, m_PathObject.Get_Width () * 2);
- }
- dest_point.Z = portal_box.Center.Z - (portal_box.Extent.Z - 0.1F);
- //
- // Calculate a complete transform for the portal enter position
- //
- Vector3 x_vector = dest_point - curr_pos;
- Vector3 y_vector;
- Vector3 z_vector (0, 0, 1);
- x_vector.Normalize ();
- y_vector = Vector3::Cross_Product (x_vector, z_vector);
- ending_tm->Set (x_vector, y_vector, z_vector, dest_point);
- retval = true;
- }
- return retval;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Can_Object_Enter_Portal
- //
- ///////////////////////////////////////////////////////////////////////////
- bool
- Can_Object_Enter_Portal
- (
- const AABoxClass & portal_box,
- const PathObjectClass & object,
- const Vector3 & direction_vector
- )
- {
- bool retval = false;
- AABoxClass obj_box;
- object.Get_Collision_Box (obj_box);
- //
- // Can this object be rotated about Z?
- //
- if (object.Is_Flag_Set (PathObjectClass::CAN_BOX_ROTATE)) {
- //
- // Build a rotation matrix from the direction vector
- //
- Vector3 x_vector = ::Normalize (direction_vector);
- Vector3 z_vector (0, 0, 1);
- Vector3 y_vector = Vector3::Cross_Product (x_vector, z_vector);
- Matrix3 tm (x_vector, y_vector, z_vector);
- //
- // Rotate the edges of the box (toss the z component, we don't care)
- //
- Vector3 v1 (obj_box.Extent.X, -obj_box.Extent.Y, 0);
- Vector3 v2 (obj_box.Extent.X, obj_box.Extent.Y, 0);
- Vector3 v3 (-obj_box.Extent.X, obj_box.Extent.Y, 0);
- Vector3 v4 (-obj_box.Extent.X, -obj_box.Extent.Y, 0);
- v1 = tm * v1;
- v2 = tm * v2;
- v3 = tm * v3;
- v4 = tm * v4;
- //
- // Determine the new axis-aligned extents from this rotated box
- //
- float max_x = max (WWMath::Fabs (v1.X), WWMath::Fabs (v2.X));
- max_x = max (max_x, WWMath::Fabs (v3.X));
- max_x = max (max_x, WWMath::Fabs (v4.X));
- float max_y = max (WWMath::Fabs (v1.Y), WWMath::Fabs (v2.Y));
- max_y = max (max_y, WWMath::Fabs (v3.Y));
- max_y = max (max_y, WWMath::Fabs (v4.Y));
- obj_box.Extent.X = max_x / 2;
- obj_box.Extent.Y = max_y / 2;
- }
- //
- // Portal boxes are generally long and very skinny.
- // So skinny in fact they are conceptually planes.
- // We find out which direction they are skinny in
- // and simulate 'shoving' the object through in
- // that direction.
- //
- if (portal_box.Extent.X > portal_box.Extent.Y) {
- retval = (obj_box.Extent.X < portal_box.Extent.X);
- } else {
- retval = (obj_box.Extent.Y < portal_box.Extent.Y);
- }
- return retval;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Process_Portals
- //
- ///////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Process_Portals (PathNodeClass *node)
- {
- PathfindSectorClass *sector = node->Peek_Sector ();
- PathfindPortalClass *last_portal = node->Peek_Portal ();
- const Matrix3D ¤t_tm = node->Get_Transform ();
- //
- // Loop over all the portals that this sector has access to
- //
- for (int index = 0; index < sector->Get_Portal_Count (); index ++) {
- PathfindPortalClass *portal = sector->Peek_Portal (index);
- //
- // Don't process any portals we can't get to from the current portal
- //
- if (sector->Can_Access_Portal (last_portal, portal)) {
- //
- // Get this portal's destination
- //
- PathfindSectorClass *dest_sector = portal->Peek_Dest_Sector (sector);
- if (dest_sector != NULL) {
- //
- // Determine if we can pass through this portal, and if so where
- // we should pass through...
- //
- Matrix3D ending_tm (1);
- if ( Does_Object_Have_Access_To_Portal (portal) &&
- Can_Object_Go_Through_Portal (current_tm, sector, portal, &ending_tm))
- {
- //
- // Submit a new node for the destination sector
- //
- Submit_Node (node->Get_Traversal_Cost (), node, portal,
- dest_sector, current_tm, ending_tm);
- }
- }
- }
- }
- if (last_portal != NULL) {
- last_portal->m_ClosedListPtr = node;
- }
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Calculate_Heuristic_Cost
- //
- ///////////////////////////////////////////////////////////////////////////
- float
- Calculate_Heuristic_Cost
- (
- const PathObjectClass & object,
- const PathfindPortalClass &portal,
- const PathfindSectorClass §or,
- const Vector3 & curr_pos,
- const Vector3 & dest_pos
- )
- {
- float cost = (curr_pos - dest_pos).Length () * 0.9F;
- if (sector.As_PathfindWaypathSectorClass () != NULL) {
- //
- // Make waypath sectors slightly more appealing
- //
- cost = cost * 0.5F;
- } else if (object.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
- //
- // Do some special case heuristics for vehicles
- //
- //
- // Make one-way portals very expensive for vehicles
- //
- if (portal.Does_Size_Matter () && portal.Is_Two_Way_Portal () == false) {
- cost = cost * 2.0F;
- }
- AABoxClass sector_box = sector.Get_Bounding_Box ();
- float sector_area = (sector_box.Extent.X * sector_box.Extent.Y);
- //AABoxClass object_box;
- //object.Get_Collision_Box (object_box);
- //float object_area = (object_box.Extent.X * object_box.Extent.Y);
- float object_area = 50.0F;
- float factor = (object_area / sector_area);
- factor = max (factor, 0.01F);
- factor = min (factor, 100.0F);
- cost = cost * factor;
- }
- return cost;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Submit_Node
- //
- ///////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Submit_Node
- (
- float traversal_cost,
- PathNodeClass * current_node,
- PathfindPortalClass * portal,
- PathfindSectorClass * dest_sector,
- const Matrix3D & current_tm,
- const Matrix3D & ending_tm
- )
- {
- const Vector3 ¤t_pos = current_tm.Get_Translation ();
- const Vector3 &dest_pos = ending_tm.Get_Translation ();
- //
- // Calculate the total traversal cost for this path.
- //
- Vector3 dest_vector = current_pos - dest_pos;
- dest_vector.Z = 0;
- float dest_dist = dest_vector.Length ();
- float current_traversal_cost = traversal_cost + dest_dist;
- //
- // Calculate the heuristic for this portal
- //
- float heuristic_cost = ::Calculate_Heuristic_Cost (m_PathObject, *portal, *dest_sector,
- dest_pos, m_DestPos);
- //
- // Try to keep vehicles from taking one-way portals
- //
- if ( m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE) &&
- portal->Is_Two_Way_Portal () == false)
- {
- current_traversal_cost += dest_dist * 2.0F;
- }
- //
- // Is this sector already in the open list?
- //
- int open_index = portal->Get_Heap_Location ();
- if (open_index > 0) {
- PathNodeClass *open_version = (PathNodeClass *)m_BinaryHeap.Peek_Node (open_index);
- //
- // If the traversal cost is lower from our current 'path', then
- // remove the old path and re-insert the new path.
- //
- if (current_traversal_cost < open_version->Get_Traversal_Cost ()) {
- open_version->Set_Sector (dest_sector);
- open_version->Set_Parent_Node (current_node);
- open_version->Set_Traversal_Cost (current_traversal_cost);
- open_version->Set_Transform (ending_tm);
- open_version->Set_Heuristic_Cost (heuristic_cost);
- m_BinaryHeap.Percolate_Up (open_index);
- }
- } else {
- //
- // Is this sector already in the closed list?
- //
- if (portal->m_ClosedListPtr != NULL) {
- PathNodeClass *closed_version = portal->m_ClosedListPtr;
- //
- // If the traversal cost is lower from our current 'path', then
- // remove the old path and re-insert the new path.
- //
- if (current_traversal_cost < closed_version->Get_Traversal_Cost ()) {
- portal->m_ClosedListPtr = NULL;
- closed_version->Set_Sector (dest_sector);
- closed_version->Set_Parent_Node (current_node);
- closed_version->Set_Traversal_Cost (current_traversal_cost);
- closed_version->Set_Transform (ending_tm);
- closed_version->Set_Heuristic_Cost (heuristic_cost);
- m_BinaryHeap.Insert (closed_version);
- }
- } else {
- //
- // Create a new path 'node' that represents that path from start
- // up until this sector.
- //
- PathNodeClass *new_node = new PathNodeClass;
- new_node->Set_Sector (dest_sector);
- new_node->Set_Parent_Node (current_node);
- new_node->Set_Portal (portal);
- //
- // Calculate the path's total traversal cost
- //
- new_node->Set_Traversal_Cost (current_traversal_cost);
- new_node->Set_Transform (ending_tm);
- new_node->Set_Heuristic_Cost (heuristic_cost);
- //
- // Insert this node into the open list
- //
- m_BinaryHeap.Insert (new_node);
- //
- // Keep track of this node's pointer (for cleanup)
- //
- m_NodeList.Add (new_node);
- }
- }
- return ;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Reset_Lists
- //
- ///////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Reset_Lists (void)
- {
- //
- // Ensure we unlink our data before we destroy it...
- //
- if (PathMgrClass::Peek_Active_Path () == this) {
- Unlink_Pathfind_Hooks ();
- }
- REF_PTR_RELEASE (m_CompletedNode);
- //
- // Free all our nodes
- //
- for (int index = 0; index < m_NodeList.Count (); index ++) {
- PathNodeClass *node = m_NodeList[index];
- REF_PTR_RELEASE (node);
- }
- m_BinaryHeap.Flush_Array ();
- m_NodeList.Reset_Active ();
- return ;
- }
- /////////////////////////////////////////////////////////////////////////////////
- //
- // Does_Line_Go_Through_Portal
- //
- /////////////////////////////////////////////////////////////////////////////////
- bool
- PathSolveClass::Does_Line_Go_Through_Portal
- (
- const Vector3 & start,
- const Vector3 & end,
- const AABoxClass & box
- )
- {
- float delta_x = end.X - start.X;
- float delta_y = end.Y - start.Y;
- float percent = 1000;
- if ((box.Extent.X <= box.Extent.Y) && (delta_x != 0)) {
- //
- // Calculate where the line intersects the X plane
- //
- percent = (box.Center.X - start.X) / delta_x;
- } else if ((box.Extent.Y <= box.Extent.X) && (delta_y != 0)) {
- //
- // Calculate where the line intersects the Y plane
- //
- percent = (box.Center.Y - start.Y) / delta_y;
- }
- //
- // Calculate the point where the line intersects one of the
- // planes of the box.
- //
- Vector3 dest_point = start + ((end - start) * percent);
- //
- // Check to make sure this point isn't outside the bounds of the
- // box, if it is outside the bounds, then the line DOES NOT pass
- // through the portal.
- //
- bool retval = true;
- if ((box.Center.X + box.Extent.X) < dest_point.X) retval = false;
- if ((box.Center.Y + box.Extent.Y) < dest_point.Y) retval = false;
- if ((box.Center.X - box.Extent.X) > dest_point.X) retval = false;
- if ((box.Center.Y - box.Extent.Y) > dest_point.Y) retval = false;
- return retval;
- }
- /////////////////////////////////////////////////////////////////////////////////
- //
- // Relax_Line
- //
- /////////////////////////////////////////////////////////////////////////////////
- Vector3
- PathSolveClass::Relax_Line (const Vector3 &start, const Vector3 &end, const AABoxClass &box)
- {
- Vector3 result = box.Center;
- float delta_x = end.X - start.X;
- float delta_y = end.Y - start.Y;
- float percent = 1000;
- if ((box.Extent.X < box.Extent.Y) && (delta_x != 0)) {
- //
- // Calculate where the line intersects the X plane
- //
- percent = (box.Center.X - start.X) / delta_x;
- } else if ((box.Extent.Y < box.Extent.X) && (delta_y != 0)) {
- //
- // Calculate where the line intersects the Y plane
- //
- percent = (box.Center.Y - start.Y) / delta_y;
- }
- //
- // Calculate the point where the line intersects one of the
- // planes of the box.
- //
- //if (percent >= 0 && percent < 1.0F) {
- result = start + ((end - start) * percent);
- Clip_Point (&result, box, m_PathObject.Get_Width () * 3.0F);
- //result = box.Center;
- //result.Z = box.Center.Z - (box.Extent.Z - 0.1F);
- /*} else {
- int test = 0;
- }*/
- return result;
- }
- /////////////////////////////////////////////////////////////////////////////////
- //
- // Check_Line
- //
- // Note: The purpose of this function is to determine if the unit's collision
- // box will be jutting outside the sector at the point where the path begins
- // to enter the portal.
- //
- //
- /////////////////////////////////////////////////////////////////////////////////
- bool
- Check_Line
- (
- const Vector3 & p0,
- const Vector3 & p1,
- const AABoxClass & sector,
- const AABoxClass & portal,
- float width,
- Vector3 * result
- )
- {
- bool retval = false;
- if ( WWMath::Fabs (portal.Center.X - sector.Center.X + sector.Extent.X) < WWMATH_EPSILON ||
- WWMath::Fabs (portal.Center.X - sector.Center.X - sector.Extent.X) < WWMATH_EPSILON)
- {
- float y_val0 = portal.Center.Y - portal.Extent.Y;
- float y_val1 = portal.Center.Y + portal.Extent.Y;
- bool do_it = false;
- float y_val = 0;
- if ( (p0.Y < y_val0 && p1.Y > y_val0) ||
- (p0.Y > y_val0 && p1.Y < y_val0))
- {
- do_it = true;
- y_val = y_val0;
- } else if ( (p0.Y < y_val1 && p1.Y > y_val1) ||
- (p0.Y > y_val1 && p1.Y < y_val1))
- {
- do_it = true;
- y_val = y_val1;
- }
- if (do_it) {
- float percent = (y_val - p0.Y) / (p1.Y - p0.Y);
- Vector3 pos = p0 + (p1 - p0) * percent;
- if (pos.X + width > (sector.Center.X + sector.Extent.X)) {
- (*result) = pos;
- result->X = (sector.Center.X + sector.Extent.X) - width;
- result->X = max (result->X, sector.Center.X);
- retval = true;
- } else if (pos.X - width < (sector.Center.X - sector.Extent.X)) {
- (*result) = pos;
- result->X = (sector.Center.X - sector.Extent.X) + width;
- result->X = min (result->X, sector.Center.X);
- retval = true;
- }
- }
- } else if ( WWMath::Fabs (portal.Center.Y - sector.Center.Y + sector.Extent.Y) < WWMATH_EPSILON ||
- WWMath::Fabs (portal.Center.Y - sector.Center.Y - sector.Extent.Y) < WWMATH_EPSILON)
- {
- float x_val0 = portal.Center.X - portal.Extent.X;
- float x_val1 = portal.Center.X + portal.Extent.X;
- bool do_it = false;
- float x_val = 0;
- if ( (p0.X < x_val0 && p1.X > x_val0) ||
- (p0.X > x_val0 && p1.X < x_val0))
- {
- do_it = true;
- x_val = x_val0;
- } else if ( (p0.X < x_val1 && p1.X > x_val1) ||
- (p0.X > x_val1 && p1.X < x_val1))
- {
- do_it = true;
- x_val = x_val1;
- }
- if (do_it) {
- float percent = (x_val - p0.X) / (p1.X - p0.X);
- Vector3 pos = p0 + (p1 - p0) * percent;
- if (pos.Y + width > (sector.Center.Y + sector.Extent.Y)) {
- (*result) = pos;
- result->Y = (sector.Center.Y + sector.Extent.Y) - width;
- result->Y = max (result->Y, sector.Center.Y);
- retval = true;
- } else if (pos.Y - width < (sector.Center.Y - sector.Extent.Y)) {
- (*result) = pos;
- result->Y = (sector.Center.Y - sector.Extent.Y) + width;
- result->Y = min (result->Y, sector.Center.Y);
- retval = true;
- }
- }
- }
- return retval;
- }
- ///////////////////////////////////////////////////////////////////////////
- //
- // Post_Process_Path
- //
- ///////////////////////////////////////////////////////////////////////////
- PathSolveClass::PATHNODE_LIST PathSolveClass::temp_node_list;
- void
- PathSolveClass::Post_Process_Path (void)
- {
- m_Path.Delete_All ();
- //
- // Build a list of the nodes (in order) the path passes through.
- //
- temp_node_list.Reset_Active();
- for (PathNodeClass *node = m_CompletedNode; node != NULL; node = node->Peek_Parent_Node ()) {
- temp_node_list.Add_Head (node);
- }
- //
- // Just do it
- //
- m_Path.Add (PathDataStruct (NULL, m_StartPos));
- m_Path[m_Path.Count () - 1].m_SectorCenter = m_StartSector->Get_Bounding_Box ().Center;
- m_Path[m_Path.Count () - 1].m_SectorExtent = m_StartSector->Get_Bounding_Box ().Extent;
- for (int index = 0; index < temp_node_list.Count (); index ++) {
- PathNodeClass *node = temp_node_list[index];
- PathfindPortalClass *portal = node->Peek_Portal ();
- m_Path.Add (PathDataStruct (portal, node->Get_Position ()));
- m_Path[m_Path.Count () - 1].m_SectorCenter.Set (0, 0, 0);
- m_Path[m_Path.Count () - 1].m_SectorExtent.Set (0, 0, 0);
- PathfindSectorClass *sector = node->Peek_Sector ();
- if (sector != NULL) {
- m_Path[m_Path.Count () - 1].m_SectorCenter = sector->Get_Bounding_Box ().Center;
- m_Path[m_Path.Count () - 1].m_SectorExtent = sector->Get_Bounding_Box ().Extent;
- }
- }
- m_Path.Add (PathDataStruct (NULL, m_DestPos));
- m_Path[m_Path.Count () - 1].m_SectorCenter = m_DestSector->Get_Bounding_Box ().Center;
- m_Path[m_Path.Count () - 1].m_SectorExtent = m_DestSector->Get_Bounding_Box ().Extent;
- //
- // Try to adjust the path so it doesn't make any sharp turns. To do this
- // we will average the closest portal points starting from the beginning
- // and then starting from the end.
- //
- typedef struct
- {
- Vector3 backward;
- Vector3 forward;
- } PATH_POINT;
- int count = m_Path.Count ();
- PATH_POINT *path_points = new PATH_POINT[count];
- Vector3 next_point = m_DestPos;
- for (index = m_Path.Count () - 2; index > 0; index --) {
- //
- // Do we have a portal we can clip the point to?
- //
- PathfindPortalClass *portal = m_Path[index].m_Portal;
- if (portal != NULL && portal->As_PathfindActionPortalClass () == NULL) {
- //
- // Clip the point to the portal's box
- //
- AABoxClass portal_box;
- portal->Get_Bounding_Box (portal_box);
- Vector3 curr_point = next_point;
- ::Clip_Point (&curr_point, portal_box, m_PathObject.Get_Width () * 3.0F);
- //
- // Move the point to the floor
- //
- curr_point.Z = portal_box.Center.Z - portal_box.Extent.Z;
- //
- // Add this point to the array
- //
- path_points[index].backward = curr_point;
- next_point = curr_point;
- } else {
- //
- // No portal, so simply add the point to the array
- //
- path_points[index].backward = m_Path[index].m_Point;
- next_point = m_Path[index].m_Point;
- }
- }
- next_point = m_StartPos;
- for (index = 1; index < m_Path.Count () - 1; index ++) {
- //
- // Do we have a portal we can clip the point to?
- //
- PathfindPortalClass *portal = m_Path[index].m_Portal;
- if (portal != NULL && portal->As_PathfindActionPortalClass () == NULL) {
- //
- // Clip the point to the portal's box
- //
- AABoxClass portal_box;
- portal->Get_Bounding_Box (portal_box);
- Vector3 curr_point = next_point;
- ::Clip_Point (&curr_point, portal_box, m_PathObject.Get_Width () * 3.0F);
- //
- // Move the point to the floor
- //
- curr_point.Z = portal_box.Center.Z - portal_box.Extent.Z;
- //
- // Add this point to the array
- //
- path_points[index].forward = curr_point;
- next_point = curr_point;
- } else {
- //
- // No portal, so simply add the point to the array
- //
- path_points[index].forward = m_Path[index].m_Point;
- next_point = m_Path[index].m_Point;
- }
- }
- //
- // Now average the points
- //
- for (index = 1; index < m_Path.Count () - 1; index ++) {
- Vector3 avg_point = (path_points[index].forward + path_points[index].backward) * 0.5F;
- m_Path[index].m_Point = avg_point;
- }
- delete [] path_points;
- //
- // Relax the points
- //
- for (index = m_Path.Count () - 2; index > 1; index --) {
- Vector3 &prev_point = m_Path[index + 1].m_Point;
- Vector3 &next_point = m_Path[index - 1].m_Point;
- PathfindPortalClass *portal = m_Path[index].m_Portal;
- if (portal != NULL && portal->As_PathfindActionPortalClass () == NULL) {
- //
- // Relax the between the last point and the next point, making
- // sure the point lies in the poral box
- //
- AABoxClass portal_box;
- portal->Get_Bounding_Box (portal_box);
- m_Path[index].m_Point = Relax_Line (prev_point, next_point, portal_box);
- }
- }
- //
- // Ensure that no object will poke its bounding box outside of
- // the sector it's in
- //
- Keep_Unit_Inside_Sectors ();
- return ;
- }
- /////////////////////////////////////////////////////////////////////////////////
- //
- // Keep_Unit_Inside_Sectors
- //
- /////////////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Keep_Unit_Inside_Sectors (void)
- {
- if (m_Path.Count () == 0) {
- return ;
- }
- //
- // Clip the starting point so that the unit will stay on the inside of its sector
- //
- Vector3 temp_point = m_Path[0].m_Point;
- AABoxClass sector_box (m_Path[0].m_SectorCenter, m_Path[0].m_SectorExtent);
- ::Clip_Point (&temp_point, sector_box, m_PathObject.Get_Width () * 2);
- m_Path[0].m_Point = temp_point;
- //
- // Now check each "line" along the path to see if the
- // unit would jut out at any point
- //
- for (int index = 0; index < m_Path.Count () - 1; index ++) {
- Vector3 curr_point = m_Path[index].m_Point;
- Vector3 next_point = m_Path[index + 1].m_Point;
- //
- // Is the current point contained in a sector, and will it
- // be passing through a portal
- //
- if ( (m_Path[index].m_SectorExtent.X > 0) ||
- (m_Path[index].m_SectorExtent.Y > 0))
- {
- //
- // Get the sector's box
- //
- AABoxClass sector_box (m_Path[index].m_SectorCenter, m_Path[index].m_SectorExtent);
- float width = m_PathObject.Get_Width () * 3;
- Vector3 start_point = curr_point;
- //
- // Lookup the current and next portals
- //
- PathfindPortalClass *prev_portal = m_Path[index].m_Portal;
- PathfindPortalClass *next_portal = m_Path[index + 1].m_Portal;
- //
- // Test the object at the point it will be entering the sector
- //
- if (prev_portal != NULL && prev_portal->As_PathfindActionPortalClass () == NULL) {
- //
- // Get the portal's box
- //
- AABoxClass portal_box;
- prev_portal->Get_Bounding_Box (portal_box);
- //
- // Check to see if the object will jut outside the sector as it moves
- // along this line
- //
- Vector3 new_point (0, 0, 0);
- if (::Check_Line (start_point, next_point, sector_box, portal_box, width, &new_point)) {
- //
- // Yup, we would jut outside the sector, so add a new point
- // to avoid this
- //
- m_Path.Insert (index + 1, PathDataStruct (NULL, new_point));
- index ++;
- start_point = new_point;
- }
- }
- //
- // Test the object at the point it will be exiting the sector
- //
- if (next_portal != NULL && next_portal->As_PathfindActionPortalClass () == NULL) {
- //
- // Get the portal's box
- //
- AABoxClass portal_box;
- next_portal->Get_Bounding_Box (portal_box);
- //
- // Check to see if the object will jut outside the sector as it moves
- // along this line
- //
- Vector3 new_point (0, 0, 0);
- if (::Check_Line (start_point, next_point, sector_box, portal_box, width, &new_point)) {
- //
- // Yup, we would jut outside the sector, so add a new point
- // to avoid this
- //
- m_Path.Insert (index + 1, PathDataStruct (NULL, new_point));
- index ++;
- }
- }
- }
- }
- return ;
- }
- /////////////////////////////////////////////////////////////////////////////////
- //
- // Set_Path_Object
- //
- /////////////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Set_Path_Object (PathObjectClass &path_object)
- {
- m_PathObject = path_object;
- return ;
- }
- /////////////////////////////////////////////////////////////////////////////////
- //
- // Get_Path_Object
- //
- /////////////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Get_Path_Object (PathObjectClass &path_object) const
- {
- path_object = m_PathObject;
- return ;
- }
- /////////////////////////////////////////////////////////////////////////////////
- //
- // Get_Volumes
- //
- /////////////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Get_Volumes
- (
- BOX_LIST §or_list,
- BOX_LIST &portal_list
- )
- {
- //
- // Build a list of the nodes (in order) the path passes through.
- //
- for ( PathNodeClass *node = m_CompletedNode;
- node != NULL;
- node = node->Peek_Parent_Node ())
- {
- //
- // Add the box from the sector to the list
- //
- AABoxClass *box = new AABoxClass (node->Peek_Sector ()->Get_Bounding_Box ());
- //box->Extent.X = max (0.0F, (box->Extent.X - m_PathObject.Get_Width () * 3));
- //box->Extent.Y = max (0.0F, (box->Extent.Y - m_PathObject.Get_Width () * 3));
- sector_list.Add (box);
- //
- // If this node has a portal, then add the portal's box to the list
- //
- if (node->Peek_Portal () != NULL) {
- AABoxClass *portal_box = new AABoxClass;
- node->Peek_Portal ()->Get_Bounding_Box (*portal_box);
- portal_list.Add (portal_box);
- }
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Save
- //
- ////////////////////////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Save (ChunkSaveClass &csave)
- {
- csave.Begin_Chunk (CHUNKID_VARIABLES);
- //
- // Save each variable to its own microchunk
- //
- WRITE_MICRO_CHUNK (csave, VARID_STARTPOS, m_StartPos);
- WRITE_MICRO_CHUNK (csave, VARID_DESTPOS, m_DestPos);
- WRITE_MICRO_CHUNK (csave, VARID_START_SECTOR, m_StartSector);
- WRITE_MICRO_CHUNK (csave, VARID_DEST_SECTOR, m_DestSector);
- WRITE_MICRO_CHUNK (csave, VARID_PATH_OBJECT, m_PathObject);
- WRITE_MICRO_CHUNK (csave, VARID_PRIORITY, m_Priority);
- PathSolveClass *this_ptr = this;
- WRITE_MICRO_CHUNK (csave, VARID_OLD_PTR, this_ptr);
- csave.End_Chunk ();
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Load
- //
- ////////////////////////////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Load (ChunkLoadClass &cload)
- {
- while (cload.Open_Chunk ()) {
- switch (cload.Cur_Chunk_ID ()) {
- case CHUNKID_VARIABLES:
- Load_Variables (cload);
- break;
- }
- cload.Close_Chunk ();
- }
- SaveLoadSystemClass::Register_Post_Load_Callback (this);
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // On_Post_Load
- //
- ///////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::On_Post_Load (void)
- {
- //
- // This basically kicks off the pathfind
- //
- Initialize (1.0F);
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Load_Variables
- //
- ///////////////////////////////////////////////////////////////////////
- void
- PathSolveClass::Load_Variables (ChunkLoadClass &cload)
- {
- PathSolveClass *old_ptr = NULL;
- m_StartSector = NULL;
- m_DestSector = NULL;
- //
- // Loop through all the microchunks that define the variables
- //
- while (cload.Open_Micro_Chunk ()) {
- switch (cload.Cur_Micro_Chunk_ID ()) {
- READ_MICRO_CHUNK (cload, VARID_STARTPOS, m_StartPos);
- READ_MICRO_CHUNK (cload, VARID_DESTPOS, m_DestPos);
- READ_MICRO_CHUNK (cload, VARID_PATH_OBJECT, m_PathObject);
- READ_MICRO_CHUNK (cload, VARID_OLD_PTR, old_ptr);
- READ_MICRO_CHUNK (cload, VARID_PRIORITY, m_Priority);
- }
- cload.Close_Micro_Chunk ();
- }
- //
- // Register our old ptr so other objects can remap to us
- //
- if (old_ptr != NULL) {
- SaveLoadSystemClass::Register_Pointer (old_ptr, this);
- }
- return ;
- }
|