doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
AI_pathing.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31 
32 #include "../Game_local.h"
33 
34 /*
35 ===============================================================================
36 
37  Dynamic Obstacle Avoidance
38 
39  - assumes the AI lives inside a bounding box aligned with the gravity direction
40  - obstacles in proximity of the AI are gathered
41  - if obstacles are found the AAS walls are also considered as obstacles
42  - every obstacle is represented by an oriented bounding box (OBB)
43  - an OBB is projected onto a 2D plane orthogonal to AI's gravity direction
44  - the 2D windings of the projections are expanded for the AI bbox
45  - a path tree is build using clockwise and counter clockwise edge walks along the winding edges
46  - the path tree is pruned and optimized
47  - the shortest path is chosen for navigation
48 
49 ===============================================================================
50 */
51 
52 const float MAX_OBSTACLE_RADIUS = 256.0f;
53 const float PUSH_OUTSIDE_OBSTACLES = 0.5f;
54 const float CLIP_BOUNDS_EPSILON = 10.0f;
55 const int MAX_AAS_WALL_EDGES = 256;
56 const int MAX_OBSTACLES = 256;
57 const int MAX_PATH_NODES = 256;
58 const int MAX_OBSTACLE_PATH = 64;
59 
60 typedef struct obstacle_s {
64 } obstacle_t;
65 
66 typedef struct pathNode_s {
67  int dir;
70  float dist;
71  int obstacle;
72  int edgeNum;
73  int numNodes;
74  struct pathNode_s * parent;
75  struct pathNode_s * children[2];
76  struct pathNode_s * next;
77  void Init();
78 } pathNode_t;
79 
81  dir = 0;
82  pos.Zero();
83  delta.Zero();
84  obstacle = -1;
85  edgeNum = -1;
86  numNodes = 0;
87  parent = children[0] = children[1] = next = NULL;
88 }
89 
91 
92 
93 /*
94 ============
95 LineIntersectsPath
96 ============
97 */
98 bool LineIntersectsPath( const idVec2 &start, const idVec2 &end, const pathNode_t *node ) {
99  float d0, d1, d2, d3;
100  idVec3 plane1, plane2;
101 
102  plane1 = idWinding2D::Plane2DFromPoints( start, end );
103  d0 = plane1.x * node->pos.x + plane1.y * node->pos.y + plane1.z;
104  while( node->parent ) {
105  d1 = plane1.x * node->parent->pos.x + plane1.y * node->parent->pos.y + plane1.z;
106  if ( FLOATSIGNBITSET( d0 ) ^ FLOATSIGNBITSET( d1 ) ) {
107  plane2 = idWinding2D::Plane2DFromPoints( node->pos, node->parent->pos );
108  d2 = plane2.x * start.x + plane2.y * start.y + plane2.z;
109  d3 = plane2.x * end.x + plane2.y * end.y + plane2.z;
110  if ( FLOATSIGNBITSET( d2 ) ^ FLOATSIGNBITSET( d3 ) ) {
111  return true;
112  }
113  }
114  d0 = d1;
115  node = node->parent;
116  }
117  return false;
118 }
119 
120 /*
121 ============
122 PointInsideObstacle
123 ============
124 */
125 int PointInsideObstacle( const obstacle_t *obstacles, const int numObstacles, const idVec2 &point ) {
126  int i;
127 
128  for ( i = 0; i < numObstacles; i++ ) {
129 
130  const idVec2 *bounds = obstacles[i].bounds;
131  if ( point.x < bounds[0].x || point.y < bounds[0].y || point.x > bounds[1].x || point.y > bounds[1].y ) {
132  continue;
133  }
134 
135  if ( !obstacles[i].winding.PointInside( point, 0.1f ) ) {
136  continue;
137  }
138 
139  return i;
140  }
141 
142  return -1;
143 }
144 
145 /*
146 ============
147 GetPointOutsideObstacles
148 ============
149 */
150 void GetPointOutsideObstacles( const obstacle_t *obstacles, const int numObstacles, idVec2 &point, int *obstacle, int *edgeNum ) {
151  int i, j, k, n, bestObstacle, bestEdgeNum, queueStart, queueEnd, edgeNums[2];
152  float d, bestd, scale[2];
153  idVec3 plane, bestPlane;
154  idVec2 newPoint, dir, bestPoint;
155  int *queue;
156  bool *obstacleVisited;
157  idWinding2D w1, w2;
158 
159  if ( obstacle ) {
160  *obstacle = -1;
161  }
162  if ( edgeNum ) {
163  *edgeNum = -1;
164  }
165 
166  bestObstacle = PointInsideObstacle( obstacles, numObstacles, point );
167  if ( bestObstacle == -1 ) {
168  return;
169  }
170 
171  const idWinding2D &w = obstacles[bestObstacle].winding;
172  bestd = idMath::INFINITY;
173  bestEdgeNum = 0;
174  for ( i = 0; i < w.GetNumPoints(); i++ ) {
175  plane = idWinding2D::Plane2DFromPoints( w[(i+1)%w.GetNumPoints()], w[i], true );
176  d = plane.x * point.x + plane.y * point.y + plane.z;
177  if ( d < bestd ) {
178  bestd = d;
179  bestPlane = plane;
180  bestEdgeNum = i;
181  }
182  // if this is a wall always try to pop out at the first edge
183  if ( obstacles[bestObstacle].entity == NULL ) {
184  break;
185  }
186  }
187 
188  newPoint = point - ( bestd + PUSH_OUTSIDE_OBSTACLES ) * bestPlane.ToVec2();
189  if ( PointInsideObstacle( obstacles, numObstacles, newPoint ) == -1 ) {
190  point = newPoint;
191  if ( obstacle ) {
192  *obstacle = bestObstacle;
193  }
194  if ( edgeNum ) {
195  *edgeNum = bestEdgeNum;
196  }
197  return;
198  }
199 
200  queue = (int *) _alloca( numObstacles * sizeof( queue[0] ) );
201  obstacleVisited = (bool *) _alloca( numObstacles * sizeof( obstacleVisited[0] ) );
202 
203  queueStart = 0;
204  queueEnd = 1;
205  queue[0] = bestObstacle;
206 
207  memset( obstacleVisited, 0, numObstacles * sizeof( obstacleVisited[0] ) );
208  obstacleVisited[bestObstacle] = true;
209 
210  bestd = idMath::INFINITY;
211  for ( i = queue[0]; queueStart < queueEnd; i = queue[++queueStart] ) {
212  w1 = obstacles[i].winding;
214 
215  for ( j = 0; j < numObstacles; j++ ) {
216  // if the obstacle has been visited already
217  if ( obstacleVisited[j] ) {
218  continue;
219  }
220  // if the bounds do not intersect
221  if ( obstacles[j].bounds[0].x > obstacles[i].bounds[1].x || obstacles[j].bounds[0].y > obstacles[i].bounds[1].y ||
222  obstacles[j].bounds[1].x < obstacles[i].bounds[0].x || obstacles[j].bounds[1].y < obstacles[i].bounds[0].y ) {
223  continue;
224  }
225 
226  queue[queueEnd++] = j;
227  obstacleVisited[j] = true;
228 
229  w2 = obstacles[j].winding;
230  w2.Expand( 0.2f );
231 
232  for ( k = 0; k < w1.GetNumPoints(); k++ ) {
233  dir = w1[(k+1)%w1.GetNumPoints()] - w1[k];
234  if ( !w2.RayIntersection( w1[k], dir, scale[0], scale[1], edgeNums ) ) {
235  continue;
236  }
237  for ( n = 0; n < 2; n++ ) {
238  newPoint = w1[k] + scale[n] * dir;
239  if ( PointInsideObstacle( obstacles, numObstacles, newPoint ) == -1 ) {
240  d = ( newPoint - point ).LengthSqr();
241  if ( d < bestd ) {
242  bestd = d;
243  bestPoint = newPoint;
244  bestEdgeNum = edgeNums[n];
245  bestObstacle = j;
246  }
247  }
248  }
249  }
250  }
251 
252  if ( bestd < idMath::INFINITY ) {
253  point = bestPoint;
254  if ( obstacle ) {
255  *obstacle = bestObstacle;
256  }
257  if ( edgeNum ) {
258  *edgeNum = bestEdgeNum;
259  }
260  return;
261  }
262  }
263  gameLocal.Warning( "GetPointOutsideObstacles: no valid point found" );
264 }
265 
266 /*
267 ============
268 GetFirstBlockingObstacle
269 ============
270 */
271 bool GetFirstBlockingObstacle( const obstacle_t *obstacles, int numObstacles, int skipObstacle, const idVec2 &startPos, const idVec2 &delta, float &blockingScale, int &blockingObstacle, int &blockingEdgeNum ) {
272  int i, edgeNums[2];
273  float dist, scale1, scale2;
274  idVec2 bounds[2];
275 
276  // get bounds for the current movement delta
277  bounds[0] = startPos - idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );
278  bounds[1] = startPos + idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );
279  bounds[FLOATSIGNBITNOTSET(delta.x)].x += delta.x;
280  bounds[FLOATSIGNBITNOTSET(delta.y)].y += delta.y;
281 
282  // test for obstacles blocking the path
283  blockingScale = idMath::INFINITY;
284  dist = delta.Length();
285  for ( i = 0; i < numObstacles; i++ ) {
286  if ( i == skipObstacle ) {
287  continue;
288  }
289  if ( bounds[0].x > obstacles[i].bounds[1].x || bounds[0].y > obstacles[i].bounds[1].y ||
290  bounds[1].x < obstacles[i].bounds[0].x || bounds[1].y < obstacles[i].bounds[0].y ) {
291  continue;
292  }
293  if ( obstacles[i].winding.RayIntersection( startPos, delta, scale1, scale2, edgeNums ) ) {
294  if ( scale1 < blockingScale && scale1 * dist > -0.01f && scale2 * dist > 0.01f ) {
295  blockingScale = scale1;
296  blockingObstacle = i;
297  blockingEdgeNum = edgeNums[0];
298  }
299  }
300  }
301  return ( blockingScale < 1.0f );
302 }
303 
304 /*
305 ============
306 GetObstacles
307 ============
308 */
309 int GetObstacles( const idPhysics *physics, const idAAS *aas, const idEntity *ignore, int areaNum, const idVec3 &startPos, const idVec3 &seekPos, obstacle_t *obstacles, int maxObstacles, idBounds &clipBounds ) {
310  int i, j, numListedClipModels, numObstacles, numVerts, clipMask, blockingObstacle, blockingEdgeNum;
311  int wallEdges[MAX_AAS_WALL_EDGES], numWallEdges, verts[2], lastVerts[2], nextVerts[2];
312  float stepHeight, headHeight, blockingScale, min, max;
313  idVec3 seekDelta, silVerts[32], start, end, nextStart, nextEnd;
314  idVec2 expBounds[2], edgeDir, edgeNormal, nextEdgeDir, nextEdgeNormal, lastEdgeNormal;
315  idVec2 obDelta;
316  idPhysics *obPhys;
317  idBox box;
318  idEntity *obEnt;
319  idClipModel *clipModel;
320  idClipModel *clipModelList[ MAX_GENTITIES ];
321 
322  numObstacles = 0;
323 
324  seekDelta = seekPos - startPos;
325  expBounds[0] = physics->GetBounds()[0].ToVec2() - idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );
326  expBounds[1] = physics->GetBounds()[1].ToVec2() + idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );
327 
328  physics->GetAbsBounds().AxisProjection( -physics->GetGravityNormal(), stepHeight, headHeight );
329  stepHeight += aas->GetSettings()->maxStepHeight;
330 
331  // clip bounds for the obstacle search space
332  clipBounds[0] = clipBounds[1] = startPos;
333  clipBounds.AddPoint( seekPos );
334  clipBounds.ExpandSelf( MAX_OBSTACLE_RADIUS );
335  clipMask = physics->GetClipMask();
336 
337  // find all obstacles touching the clip bounds
338  numListedClipModels = gameLocal.clip.ClipModelsTouchingBounds( clipBounds, clipMask, clipModelList, MAX_GENTITIES );
339 
340  for ( i = 0; i < numListedClipModels && numObstacles < MAX_OBSTACLES; i++ ) {
341  clipModel = clipModelList[i];
342  obEnt = clipModel->GetEntity();
343 
344  if ( !clipModel->IsTraceModel() ) {
345  continue;
346  }
347 
348  if ( obEnt->IsType( idActor::Type ) ) {
349  obPhys = obEnt->GetPhysics();
350  // ignore myself, my enemy, and dead bodies
351  if ( ( obPhys == physics ) || ( obEnt == ignore ) || ( obEnt->health <= 0 ) ) {
352  continue;
353  }
354  // if the actor is moving
355  idVec3 v1 = obPhys->GetLinearVelocity();
356  if ( v1.LengthSqr() > Square( 10.0f ) ) {
357  idVec3 v2 = physics->GetLinearVelocity();
358  if ( v2.LengthSqr() > Square( 10.0f ) ) {
359  // if moving in about the same direction
360  if ( v1 * v2 > 0.0f ) {
361  continue;
362  }
363  }
364  }
365  } else if ( obEnt->IsType( idMoveable::Type ) ) {
366  // moveables are considered obstacles
367  } else {
368  // ignore everything else
369  continue;
370  }
371 
372  // check if we can step over the object
373  clipModel->GetAbsBounds().AxisProjection( -physics->GetGravityNormal(), min, max );
374  if ( max < stepHeight || min > headHeight ) {
375  // can step over this one
376  continue;
377  }
378 
379  // project a box containing the obstacle onto the floor plane
380  box = idBox( clipModel->GetBounds(), clipModel->GetOrigin(), clipModel->GetAxis() );
381  numVerts = box.GetParallelProjectionSilhouetteVerts( physics->GetGravityNormal(), silVerts );
382 
383  // create a 2D winding for the obstacle;
384  obstacle_t &obstacle = obstacles[numObstacles++];
385  obstacle.winding.Clear();
386  for ( j = 0; j < numVerts; j++ ) {
387  obstacle.winding.AddPoint( silVerts[j].ToVec2() );
388  }
389 
391  for ( j = 0; j < numVerts; j++ ) {
392  silVerts[j].z = startPos.z;
393  }
394  for ( j = 0; j < numVerts; j++ ) {
395  gameRenderWorld->DebugArrow( colorWhite, silVerts[j], silVerts[(j+1)%numVerts], 4 );
396  }
397  }
398 
399  // expand the 2D winding for collision with a 2D box
400  obstacle.winding.ExpandForAxialBox( expBounds );
401  obstacle.winding.GetBounds( obstacle.bounds );
402  obstacle.entity = obEnt;
403  }
404 
405  // if there are no dynamic obstacles the path should be through valid AAS space
406  if ( numObstacles == 0 ) {
407  return 0;
408  }
409 
410  // if the current path doesn't intersect any dynamic obstacles the path should be through valid AAS space
411  if ( PointInsideObstacle( obstacles, numObstacles, startPos.ToVec2() ) == -1 ) {
412  if ( !GetFirstBlockingObstacle( obstacles, numObstacles, -1, startPos.ToVec2(), seekDelta.ToVec2(), blockingScale, blockingObstacle, blockingEdgeNum ) ) {
413  return 0;
414  }
415  }
416 
417  // create obstacles for AAS walls
418  if ( aas ) {
419  float halfBoundsSize = ( expBounds[ 1 ].x - expBounds[ 0 ].x ) * 0.5f;
420 
421  numWallEdges = aas->GetWallEdges( areaNum, clipBounds, TFL_WALK, wallEdges, MAX_AAS_WALL_EDGES );
422  aas->SortWallEdges( wallEdges, numWallEdges );
423 
424  lastVerts[0] = lastVerts[1] = 0;
425  lastEdgeNormal.Zero();
426  nextVerts[0] = nextVerts[1] = 0;
427  for ( i = 0; i < numWallEdges && numObstacles < MAX_OBSTACLES; i++ ) {
428  aas->GetEdge( wallEdges[i], start, end );
429  aas->GetEdgeVertexNumbers( wallEdges[i], verts );
430  edgeDir = end.ToVec2() - start.ToVec2();
431  edgeDir.Normalize();
432  edgeNormal.x = edgeDir.y;
433  edgeNormal.y = -edgeDir.x;
434  if ( i < numWallEdges-1 ) {
435  aas->GetEdge( wallEdges[i+1], nextStart, nextEnd );
436  aas->GetEdgeVertexNumbers( wallEdges[i+1], nextVerts );
437  nextEdgeDir = nextEnd.ToVec2() - nextStart.ToVec2();
438  nextEdgeDir.Normalize();
439  nextEdgeNormal.x = nextEdgeDir.y;
440  nextEdgeNormal.y = -nextEdgeDir.x;
441  }
442 
443  obstacle_t &obstacle = obstacles[numObstacles++];
444  obstacle.winding.Clear();
445  obstacle.winding.AddPoint( end.ToVec2() );
446  obstacle.winding.AddPoint( start.ToVec2() );
447  obstacle.winding.AddPoint( start.ToVec2() - edgeDir - edgeNormal * halfBoundsSize );
448  obstacle.winding.AddPoint( end.ToVec2() + edgeDir - edgeNormal * halfBoundsSize );
449  if ( lastVerts[1] == verts[0] ) {
450  obstacle.winding[2] -= lastEdgeNormal * halfBoundsSize;
451  } else {
452  obstacle.winding[1] -= edgeDir;
453  }
454  if ( verts[1] == nextVerts[0] ) {
455  obstacle.winding[3] -= nextEdgeNormal * halfBoundsSize;
456  } else {
457  obstacle.winding[0] += edgeDir;
458  }
459  obstacle.winding.GetBounds( obstacle.bounds );
460  obstacle.entity = NULL;
461 
462  memcpy( lastVerts, verts, sizeof( lastVerts ) );
463  lastEdgeNormal = edgeNormal;
464  }
465  }
466 
467  // show obstacles
469  for ( i = 0; i < numObstacles; i++ ) {
470  obstacle_t &obstacle = obstacles[i];
471  for ( j = 0; j < obstacle.winding.GetNumPoints(); j++ ) {
472  silVerts[j].ToVec2() = obstacle.winding[j];
473  silVerts[j].z = startPos.z;
474  }
475  for ( j = 0; j < obstacle.winding.GetNumPoints(); j++ ) {
476  gameRenderWorld->DebugArrow( colorGreen, silVerts[j], silVerts[(j+1)%obstacle.winding.GetNumPoints()], 4 );
477  }
478  }
479  }
480 
481  return numObstacles;
482 }
483 
484 /*
485 ============
486 FreePathTree_r
487 ============
488 */
489 void FreePathTree_r( pathNode_t *node ) {
490  if ( node->children[0] ) {
491  FreePathTree_r( node->children[0] );
492  }
493  if ( node->children[1] ) {
494  FreePathTree_r( node->children[1] );
495  }
496  pathNodeAllocator.Free( node );
497 }
498 
499 /*
500 ============
501 DrawPathTree
502 ============
503 */
504 void DrawPathTree( const pathNode_t *root, const float height ) {
505  int i;
506  idVec3 start, end;
507  const pathNode_t *node;
508 
509  for ( node = root; node; node = node->next ) {
510  for ( i = 0; i < 2; i++ ) {
511  if ( node->children[i] ) {
512  start.ToVec2() = node->pos;
513  start.z = height;
514  end.ToVec2() = node->children[i]->pos;
515  end.z = height;
516  gameRenderWorld->DebugArrow( node->edgeNum == -1 ? colorYellow : i ? colorBlue : colorRed, start, end, 1 );
517  break;
518  }
519  }
520  }
521 }
522 
523 /*
524 ============
525 GetPathNodeDelta
526 ============
527 */
528 bool GetPathNodeDelta( pathNode_t *node, const obstacle_t *obstacles, const idVec2 &seekPos, bool blocked ) {
529  int numPoints, edgeNum;
530  bool facing;
531  idVec2 seekDelta, dir;
532  pathNode_t *n;
533 
534  numPoints = obstacles[node->obstacle].winding.GetNumPoints();
535 
536  // get delta along the current edge
537  while( 1 ) {
538  edgeNum = ( node->edgeNum + node->dir ) % numPoints;
539  node->delta = obstacles[node->obstacle].winding[edgeNum] - node->pos;
540  if ( node->delta.LengthSqr() > 0.01f ) {
541  break;
542  }
543  node->edgeNum = ( node->edgeNum + numPoints + ( 2 * node->dir - 1 ) ) % numPoints;
544  }
545 
546  // if not blocked
547  if ( !blocked ) {
548 
549  // test if the current edge faces the goal
550  seekDelta = seekPos - node->pos;
551  facing = ( ( 2 * node->dir - 1 ) * ( node->delta.x * seekDelta.y - node->delta.y * seekDelta.x ) ) >= 0.0f;
552 
553  // if the current edge faces goal and the line from the current
554  // position to the goal does not intersect the current path
555  if ( facing && !LineIntersectsPath( node->pos, seekPos, node->parent ) ) {
556  node->delta = seekPos - node->pos;
557  node->edgeNum = -1;
558  }
559  }
560 
561  // if the delta is along the obstacle edge
562  if ( node->edgeNum != -1 ) {
563  // if the edge is found going from this node to the root node
564  for ( n = node->parent; n; n = n->parent ) {
565 
566  if ( node->obstacle != n->obstacle || node->edgeNum != n->edgeNum ) {
567  continue;
568  }
569 
570  // test whether or not the edge segments actually overlap
571  if ( n->pos * node->delta > ( node->pos + node->delta ) * node->delta ) {
572  continue;
573  }
574  if ( node->pos * node->delta > ( n->pos + n->delta ) * node->delta ) {
575  continue;
576  }
577 
578  break;
579  }
580  if ( n ) {
581  return false;
582  }
583  }
584  return true;
585 }
586 
587 /*
588 ============
589 BuildPathTree
590 ============
591 */
592 pathNode_t *BuildPathTree( const obstacle_t *obstacles, int numObstacles, const idBounds &clipBounds, const idVec2 &startPos, const idVec2 &seekPos, obstaclePath_t &path ) {
593  int blockingEdgeNum, blockingObstacle, obstaclePoints, bestNumNodes = MAX_OBSTACLE_PATH;
594  float blockingScale;
595  pathNode_t *root, *node, *child;
596  // gcc 4.0
598 
599  root = pathNodeAllocator.Alloc();
600  root->Init();
601  root->pos = startPos;
602 
603  root->delta = seekPos - root->pos;
604  root->numNodes = 0;
605  pathNodeQueue.Add( root );
606 
607  for ( node = pathNodeQueue.Get(); node && pathNodeAllocator.GetAllocCount() < MAX_PATH_NODES; node = pathNodeQueue.Get() ) {
608 
609  treeQueue.Add( node );
610 
611  // if this path has more than twice the number of nodes than the best path so far
612  if ( node->numNodes > bestNumNodes * 2 ) {
613  continue;
614  }
615 
616  // don't move outside of the clip bounds
617  idVec2 endPos = node->pos + node->delta;
618  if ( endPos.x - CLIP_BOUNDS_EPSILON < clipBounds[0].x || endPos.x + CLIP_BOUNDS_EPSILON > clipBounds[1].x ||
619  endPos.y - CLIP_BOUNDS_EPSILON < clipBounds[0].y || endPos.y + CLIP_BOUNDS_EPSILON > clipBounds[1].y ) {
620  continue;
621  }
622 
623  // if an obstacle is blocking the path
624  if ( GetFirstBlockingObstacle( obstacles, numObstacles, node->obstacle, node->pos, node->delta, blockingScale, blockingObstacle, blockingEdgeNum ) ) {
625 
626  if ( path.firstObstacle == NULL ) {
627  path.firstObstacle = obstacles[blockingObstacle].entity;
628  }
629 
630  node->delta *= blockingScale;
631 
632  if ( node->edgeNum == -1 ) {
633  node->children[0] = pathNodeAllocator.Alloc();
634  node->children[0]->Init();
635  node->children[1] = pathNodeAllocator.Alloc();
636  node->children[1]->Init();
637  node->children[0]->dir = 0;
638  node->children[1]->dir = 1;
639  node->children[0]->parent = node->children[1]->parent = node;
640  node->children[0]->pos = node->children[1]->pos = node->pos + node->delta;
641  node->children[0]->obstacle = node->children[1]->obstacle = blockingObstacle;
642  node->children[0]->edgeNum = node->children[1]->edgeNum = blockingEdgeNum;
643  node->children[0]->numNodes = node->children[1]->numNodes = node->numNodes + 1;
644  if ( GetPathNodeDelta( node->children[0], obstacles, seekPos, true ) ) {
645  pathNodeQueue.Add( node->children[0] );
646  }
647  if ( GetPathNodeDelta( node->children[1], obstacles, seekPos, true ) ) {
648  pathNodeQueue.Add( node->children[1] );
649  }
650  } else {
651  node->children[node->dir] = child = pathNodeAllocator.Alloc();
652  child->Init();
653  child->dir = node->dir;
654  child->parent = node;
655  child->pos = node->pos + node->delta;
656  child->obstacle = blockingObstacle;
657  child->edgeNum = blockingEdgeNum;
658  child->numNodes = node->numNodes + 1;
659  if ( GetPathNodeDelta( child, obstacles, seekPos, true ) ) {
660  pathNodeQueue.Add( child );
661  }
662  }
663  } else {
664  node->children[node->dir] = child = pathNodeAllocator.Alloc();
665  child->Init();
666  child->dir = node->dir;
667  child->parent = node;
668  child->pos = node->pos + node->delta;
669  child->numNodes = node->numNodes + 1;
670 
671  // there is a free path towards goal
672  if ( node->edgeNum == -1 ) {
673  if ( node->numNodes < bestNumNodes ) {
674  bestNumNodes = node->numNodes;
675  }
676  continue;
677  }
678 
679  child->obstacle = node->obstacle;
680  obstaclePoints = obstacles[node->obstacle].winding.GetNumPoints();
681  child->edgeNum = ( node->edgeNum + obstaclePoints + ( 2 * node->dir - 1 ) ) % obstaclePoints;
682 
683  if ( GetPathNodeDelta( child, obstacles, seekPos, false ) ) {
684  pathNodeQueue.Add( child );
685  }
686  }
687  }
688 
689  return root;
690 }
691 
692 /*
693 ============
694 PrunePathTree
695 ============
696 */
697 void PrunePathTree( pathNode_t *root, const idVec2 &seekPos ) {
698  int i;
699  float bestDist;
700  pathNode_t *node, *lastNode, *n, *bestNode;
701 
702  node = root;
703  while( node ) {
704 
705  node->dist = ( seekPos - node->pos ).LengthSqr();
706 
707  if ( node->children[0] ) {
708  node = node->children[0];
709  } else if ( node->children[1] ) {
710  node = node->children[1];
711  } else {
712 
713  // find the node closest to the goal along this path
714  bestDist = idMath::INFINITY;
715  bestNode = node;
716  for ( n = node; n; n = n->parent ) {
717  if ( n->children[0] && n->children[1] ) {
718  break;
719  }
720  if ( n->dist < bestDist ) {
721  bestDist = n->dist;
722  bestNode = n;
723  }
724  }
725 
726  // free tree down from the best node
727  for ( i = 0; i < 2; i++ ) {
728  if ( bestNode->children[i] ) {
729  FreePathTree_r( bestNode->children[i] );
730  bestNode->children[i] = NULL;
731  }
732  }
733 
734  for ( lastNode = bestNode, node = bestNode->parent; node; lastNode = node, node = node->parent ) {
735  if ( node->children[1] && ( node->children[1] != lastNode ) ) {
736  node = node->children[1];
737  break;
738  }
739  }
740  }
741  }
742 }
743 
744 /*
745 ============
746 OptimizePath
747 ============
748 */
749 int OptimizePath( const pathNode_t *root, const pathNode_t *leafNode, const obstacle_t *obstacles, int numObstacles, idVec2 optimizedPath[MAX_OBSTACLE_PATH] ) {
750  int i, numPathPoints, edgeNums[2];
751  const pathNode_t *curNode, *nextNode;
752  idVec2 curPos, curDelta, bounds[2];
753  float scale1, scale2, curLength;
754 
755  optimizedPath[0] = root->pos;
756  numPathPoints = 1;
757 
758  for ( nextNode = curNode = root; curNode != leafNode; curNode = nextNode ) {
759 
760  for ( nextNode = leafNode; nextNode->parent != curNode; nextNode = nextNode->parent ) {
761 
762  // can only take shortcuts when going from one object to another
763  if ( nextNode->obstacle == curNode->obstacle ) {
764  continue;
765  }
766 
767  curPos = curNode->pos;
768  curDelta = nextNode->pos - curPos;
769  curLength = curDelta.Length();
770 
771  // get bounds for the current movement delta
772  bounds[0] = curPos - idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );
773  bounds[1] = curPos + idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON );
774  bounds[FLOATSIGNBITNOTSET(curDelta.x)].x += curDelta.x;
775  bounds[FLOATSIGNBITNOTSET(curDelta.y)].y += curDelta.y;
776 
777  // test if the shortcut intersects with any obstacles
778  for ( i = 0; i < numObstacles; i++ ) {
779  if ( bounds[0].x > obstacles[i].bounds[1].x || bounds[0].y > obstacles[i].bounds[1].y ||
780  bounds[1].x < obstacles[i].bounds[0].x || bounds[1].y < obstacles[i].bounds[0].y ) {
781  continue;
782  }
783  if ( obstacles[i].winding.RayIntersection( curPos, curDelta, scale1, scale2, edgeNums ) ) {
784  if ( scale1 >= 0.0f && scale1 <= 1.0f && ( i != nextNode->obstacle || scale1 * curLength < curLength - 0.5f ) ) {
785  break;
786  }
787  if ( scale2 >= 0.0f && scale2 <= 1.0f && ( i != nextNode->obstacle || scale2 * curLength < curLength - 0.5f ) ) {
788  break;
789  }
790  }
791  }
792  if ( i >= numObstacles ) {
793  break;
794  }
795  }
796 
797  // store the next position along the optimized path
798  optimizedPath[numPathPoints++] = nextNode->pos;
799  }
800 
801  return numPathPoints;
802 }
803 
804 /*
805 ============
806 PathLength
807 ============
808 */
809 float PathLength( idVec2 optimizedPath[MAX_OBSTACLE_PATH], int numPathPoints, const idVec2 &curDir ) {
810  int i;
811  float pathLength;
812 
813  // calculate the path length
814  pathLength = 0.0f;
815  for ( i = 0; i < numPathPoints-1; i++ ) {
816  pathLength += ( optimizedPath[i+1] - optimizedPath[i] ).LengthFast();
817  }
818 
819  // add penalty if this path does not go in the current direction
820  if ( curDir * ( optimizedPath[1] - optimizedPath[0] ) < 0.0f ) {
821  pathLength += 100.0f;
822  }
823  return pathLength;
824 }
825 
826 /*
827 ============
828 FindOptimalPath
829 
830  Returns true if there is a path all the way to the goal.
831 ============
832 */
833 bool FindOptimalPath( const pathNode_t *root, const obstacle_t *obstacles, int numObstacles, const float height, const idVec3 &curDir, idVec3 &seekPos ) {
834  int i, numPathPoints, bestNumPathPoints;
835  const pathNode_t *node, *lastNode, *bestNode;
836  idVec2 optimizedPath[MAX_OBSTACLE_PATH];
837  float pathLength, bestPathLength;
838  bool pathToGoalExists, optimizedPathCalculated;
839 
840  seekPos.Zero();
841  seekPos.z = height;
842 
843  pathToGoalExists = false;
844  optimizedPathCalculated = false;
845 
846  bestNode = root;
847  bestNumPathPoints = 0;
848  bestPathLength = idMath::INFINITY;
849 
850  node = root;
851  while( node ) {
852 
853  pathToGoalExists |= ( node->dist < 0.1f );
854 
855  if ( node->dist <= bestNode->dist ) {
856 
857  if ( idMath::Fabs( node->dist - bestNode->dist ) < 0.1f ) {
858 
859  if ( !optimizedPathCalculated ) {
860  bestNumPathPoints = OptimizePath( root, bestNode, obstacles, numObstacles, optimizedPath );
861  bestPathLength = PathLength( optimizedPath, bestNumPathPoints, curDir.ToVec2() );
862  seekPos.ToVec2() = optimizedPath[1];
863  }
864 
865  numPathPoints = OptimizePath( root, node, obstacles, numObstacles, optimizedPath );
866  pathLength = PathLength( optimizedPath, numPathPoints, curDir.ToVec2() );
867 
868  if ( pathLength < bestPathLength ) {
869  bestNode = node;
870  bestNumPathPoints = numPathPoints;
871  bestPathLength = pathLength;
872  seekPos.ToVec2() = optimizedPath[1];
873  }
874  optimizedPathCalculated = true;
875 
876  } else {
877 
878  bestNode = node;
879  optimizedPathCalculated = false;
880  }
881  }
882 
883  if ( node->children[0] ) {
884  node = node->children[0];
885  } else if ( node->children[1] ) {
886  node = node->children[1];
887  } else {
888  for ( lastNode = node, node = node->parent; node; lastNode = node, node = node->parent ) {
889  if ( node->children[1] && node->children[1] != lastNode ) {
890  node = node->children[1];
891  break;
892  }
893  }
894  }
895  }
896 
897  if ( !pathToGoalExists ) {
898  seekPos.ToVec2() = root->children[0]->pos;
899  } else if ( !optimizedPathCalculated ) {
900  OptimizePath( root, bestNode, obstacles, numObstacles, optimizedPath );
901  seekPos.ToVec2() = optimizedPath[1];
902  }
903 
905  idVec3 start, end;
906  start.z = end.z = height + 4.0f;
907  numPathPoints = OptimizePath( root, bestNode, obstacles, numObstacles, optimizedPath );
908  for ( i = 0; i < numPathPoints-1; i++ ) {
909  start.ToVec2() = optimizedPath[i];
910  end.ToVec2() = optimizedPath[i+1];
911  gameRenderWorld->DebugArrow( colorCyan, start, end, 1 );
912  }
913  }
914 
915  return pathToGoalExists;
916 }
917 
918 /*
919 ============
920 idAI::FindPathAroundObstacles
921 
922  Finds a path around dynamic obstacles using a path tree with clockwise and counter clockwise edge walks.
923 ============
924 */
925 bool idAI::FindPathAroundObstacles( const idPhysics *physics, const idAAS *aas, const idEntity *ignore, const idVec3 &startPos, const idVec3 &seekPos, obstaclePath_t &path ) {
926  int numObstacles, areaNum, insideObstacle;
927  obstacle_t obstacles[MAX_OBSTACLES];
928  idBounds clipBounds;
929  idBounds bounds;
930  pathNode_t *root;
931  bool pathToGoalExists;
932 
933  path.seekPos = seekPos;
934  path.firstObstacle = NULL;
935  path.startPosOutsideObstacles = startPos;
936  path.startPosObstacle = NULL;
937  path.seekPosOutsideObstacles = seekPos;
938  path.seekPosObstacle = NULL;
939 
940  if ( !aas ) {
941  return true;
942  }
943 
944  bounds[1] = aas->GetSettings()->boundingBoxes[0][1];
945  bounds[0] = -bounds[1];
946  bounds[1].z = 32.0f;
947 
948  // get the AAS area number and a valid point inside that area
950  aas->PushPointIntoAreaNum( areaNum, path.startPosOutsideObstacles );
951 
952  // get all the nearby obstacles
953  numObstacles = GetObstacles( physics, aas, ignore, areaNum, path.startPosOutsideObstacles, path.seekPosOutsideObstacles, obstacles, MAX_OBSTACLES, clipBounds );
954 
955  // get a source position outside the obstacles
956  GetPointOutsideObstacles( obstacles, numObstacles, path.startPosOutsideObstacles.ToVec2(), &insideObstacle, NULL );
957  if ( insideObstacle != -1 ) {
958  path.startPosObstacle = obstacles[insideObstacle].entity;
959  }
960 
961  // get a goal position outside the obstacles
962  GetPointOutsideObstacles( obstacles, numObstacles, path.seekPosOutsideObstacles.ToVec2(), &insideObstacle, NULL );
963  if ( insideObstacle != -1 ) {
964  path.seekPosObstacle = obstacles[insideObstacle].entity;
965  }
966 
967  // if start and destination are pushed to the same point, we don't have a path around the obstacle
968  if ( ( path.seekPosOutsideObstacles.ToVec2() - path.startPosOutsideObstacles.ToVec2() ).LengthSqr() < Square( 1.0f ) ) {
969  if ( ( seekPos.ToVec2() - startPos.ToVec2() ).LengthSqr() > Square( 2.0f ) ) {
970  return false;
971  }
972  }
973 
974  // build a path tree
975  root = BuildPathTree( obstacles, numObstacles, clipBounds, path.startPosOutsideObstacles.ToVec2(), path.seekPosOutsideObstacles.ToVec2(), path );
976 
977  // draw the path tree
979  DrawPathTree( root, physics->GetOrigin().z );
980  }
981 
982  // prune the tree
984 
985  // find the optimal path
986  pathToGoalExists = FindOptimalPath( root, obstacles, numObstacles, physics->GetOrigin().z, physics->GetLinearVelocity(), path.seekPos );
987 
988  // free the tree
989  FreePathTree_r( root );
990 
991  return pathToGoalExists;
992 }
993 
994 /*
995 ============
996 idAI::FreeObstacleAvoidanceNodes
997 ============
998 */
1000  pathNodeAllocator.Shutdown();
1001 }
1002 
1003 
1004 /*
1005 ===============================================================================
1006 
1007  Path Prediction
1008 
1009  Uses the AAS to quickly and accurately predict a path for a certain
1010  period of time based on an initial position and velocity.
1011 
1012 ===============================================================================
1013 */
1014 
1015 const float OVERCLIP = 1.001f;
1016 const int MAX_FRAME_SLIDE = 5;
1017 
1018 typedef struct pathTrace_s {
1019  float fraction;
1023 } pathTrace_t;
1024 
1025 /*
1026 ============
1027 PathTrace
1028 
1029  Returns true if a stop event was triggered.
1030 ============
1031 */
1032 bool PathTrace( const idEntity *ent, const idAAS *aas, const idVec3 &start, const idVec3 &end, int stopEvent, struct pathTrace_s &trace, predictedPath_t &path ) {
1033  trace_t clipTrace;
1034  aasTrace_t aasTrace;
1035 
1036  memset( &trace, 0, sizeof( trace ) );
1037 
1038  if ( !aas || !aas->GetSettings() ) {
1039 
1040  gameLocal.clip.Translation( clipTrace, start, end, ent->GetPhysics()->GetClipModel(),
1041  ent->GetPhysics()->GetClipModel()->GetAxis(), MASK_MONSTERSOLID, ent );
1042 
1043  // NOTE: could do (expensive) ledge detection here for when there is no AAS file
1044 
1045  trace.fraction = clipTrace.fraction;
1046  trace.endPos = clipTrace.endpos;
1047  trace.normal = clipTrace.c.normal;
1048  trace.blockingEntity = gameLocal.entities[ clipTrace.c.entityNum ];
1049  } else {
1050  aasTrace.getOutOfSolid = true;
1051  if ( stopEvent & SE_ENTER_LEDGE_AREA ) {
1052  aasTrace.flags |= AREA_LEDGE;
1053  }
1054  if ( stopEvent & SE_ENTER_OBSTACLE ) {
1055  aasTrace.travelFlags |= TFL_INVALID;
1056  }
1057 
1058  aas->Trace( aasTrace, start, end );
1059 
1060  gameLocal.clip.TranslationEntities( clipTrace, start, aasTrace.endpos, ent->GetPhysics()->GetClipModel(),
1061  ent->GetPhysics()->GetClipModel()->GetAxis(), MASK_MONSTERSOLID, ent );
1062 
1063  if ( clipTrace.fraction >= 1.0f ) {
1064 
1065  trace.fraction = aasTrace.fraction;
1066  trace.endPos = aasTrace.endpos;
1067  trace.normal = aas->GetPlane( aasTrace.planeNum ).Normal();
1068  trace.blockingEntity = gameLocal.world;
1069 
1070  if ( aasTrace.fraction < 1.0f ) {
1071  if ( stopEvent & SE_ENTER_LEDGE_AREA ) {
1072  if ( aas->AreaFlags( aasTrace.blockingAreaNum ) & AREA_LEDGE ) {
1073  path.endPos = trace.endPos;
1074  path.endNormal = trace.normal;
1076  path.blockingEntity = trace.blockingEntity;
1077 
1078  if ( ai_debugMove.GetBool() ) {
1079  gameRenderWorld->DebugLine( colorRed, start, aasTrace.endpos );
1080  }
1081  return true;
1082  }
1083  }
1084  if ( stopEvent & SE_ENTER_OBSTACLE ) {
1085  if ( aas->AreaTravelFlags( aasTrace.blockingAreaNum ) & TFL_INVALID ) {
1086  path.endPos = trace.endPos;
1087  path.endNormal = trace.normal;
1088  path.endEvent = SE_ENTER_OBSTACLE;
1089  path.blockingEntity = trace.blockingEntity;
1090 
1091  if ( ai_debugMove.GetBool() ) {
1092  gameRenderWorld->DebugLine( colorRed, start, aasTrace.endpos );
1093  }
1094  return true;
1095  }
1096  }
1097  }
1098  } else {
1099  trace.fraction = clipTrace.fraction;
1100  trace.endPos = clipTrace.endpos;
1101  trace.normal = clipTrace.c.normal;
1102  trace.blockingEntity = gameLocal.entities[ clipTrace.c.entityNum ];
1103  }
1104  }
1105 
1106  if ( trace.fraction >= 1.0f ) {
1107  trace.blockingEntity = NULL;
1108  }
1109 
1110  return false;
1111 }
1112 
1113 /*
1114 ============
1115 idAI::PredictPath
1116 
1117  Can also be used when there is no AAS file available however ledges are not detected.
1118 ============
1119 */
1120 bool idAI::PredictPath( const idEntity *ent, const idAAS *aas, const idVec3 &start, const idVec3 &velocity, int totalTime, int frameTime, int stopEvent, predictedPath_t &path ) {
1121  int i, j, step, numFrames, curFrameTime;
1122  idVec3 delta, curStart, curEnd, curVelocity, lastEnd, stepUp, tmpStart;
1123  idVec3 gravity, gravityDir, invGravityDir;
1124  float maxStepHeight, minFloorCos;
1125  pathTrace_t trace;
1126 
1127  if ( aas && aas->GetSettings() ) {
1128  gravity = aas->GetSettings()->gravity;
1129  gravityDir = aas->GetSettings()->gravityDir;
1130  invGravityDir = aas->GetSettings()->invGravityDir;
1131  maxStepHeight = aas->GetSettings()->maxStepHeight;
1132  minFloorCos = aas->GetSettings()->minFloorCos;
1133  } else {
1134  gravity = DEFAULT_GRAVITY_VEC3;
1135  gravityDir = idVec3( 0, 0, -1 );
1136  invGravityDir = idVec3( 0, 0, 1 );
1137  maxStepHeight = 14.0f;
1138  minFloorCos = 0.7f;
1139  }
1140 
1141  path.endPos = start;
1142  path.endVelocity = velocity;
1143  path.endNormal.Zero();
1144  path.endEvent = 0;
1145  path.endTime = 0;
1146  path.blockingEntity = NULL;
1147 
1148  curStart = start;
1149  curVelocity = velocity;
1150 
1151  numFrames = ( totalTime + frameTime - 1 ) / frameTime;
1152  curFrameTime = frameTime;
1153  for ( i = 0; i < numFrames; i++ ) {
1154 
1155  if ( i == numFrames-1 ) {
1156  curFrameTime = totalTime - i * curFrameTime;
1157  }
1158 
1159  delta = curVelocity * curFrameTime * 0.001f;
1160 
1161  path.endVelocity = curVelocity;
1162  path.endTime = i * frameTime;
1163 
1164  // allow sliding along a few surfaces per frame
1165  for ( j = 0; j < MAX_FRAME_SLIDE; j++ ) {
1166 
1167  idVec3 lineStart = curStart;
1168 
1169  // allow stepping up three times per frame
1170  for ( step = 0; step < 3; step++ ) {
1171 
1172  curEnd = curStart + delta;
1173  if ( PathTrace( ent, aas, curStart, curEnd, stopEvent, trace, path ) ) {
1174  return true;
1175  }
1176 
1177  if ( step ) {
1178 
1179  // step down at end point
1180  tmpStart = trace.endPos;
1181  curEnd = tmpStart - stepUp;
1182  if ( PathTrace( ent, aas, tmpStart, curEnd, stopEvent, trace, path ) ) {
1183  return true;
1184  }
1185 
1186  // if not moved any further than without stepping up, or if not on a floor surface
1187  if ( (lastEnd - start).LengthSqr() > (trace.endPos - start).LengthSqr() - 0.1f ||
1188  ( trace.normal * invGravityDir ) < minFloorCos ) {
1189  if ( stopEvent & SE_BLOCKED ) {
1190  path.endPos = lastEnd;
1191  path.endEvent = SE_BLOCKED;
1192 
1193  if ( ai_debugMove.GetBool() ) {
1194  gameRenderWorld->DebugLine( colorRed, lineStart, lastEnd );
1195  }
1196 
1197  return true;
1198  }
1199 
1200  curStart = lastEnd;
1201  break;
1202  }
1203  }
1204 
1205  path.endNormal = trace.normal;
1206  path.blockingEntity = trace.blockingEntity;
1207 
1208  // if the trace is not blocked or blocked by a floor surface
1209  if ( trace.fraction >= 1.0f || ( trace.normal * invGravityDir ) > minFloorCos ) {
1210  curStart = trace.endPos;
1211  break;
1212  }
1213 
1214  // save last result
1215  lastEnd = trace.endPos;
1216 
1217  // step up
1218  stepUp = invGravityDir * maxStepHeight;
1219  if ( PathTrace( ent, aas, curStart, curStart + stepUp, stopEvent, trace, path ) ) {
1220  return true;
1221  }
1222  stepUp *= trace.fraction;
1223  curStart = trace.endPos;
1224  }
1225 
1226  if ( ai_debugMove.GetBool() ) {
1227  gameRenderWorld->DebugLine( colorRed, lineStart, curStart );
1228  }
1229 
1230  if ( trace.fraction >= 1.0f ) {
1231  break;
1232  }
1233 
1234  delta.ProjectOntoPlane( trace.normal, OVERCLIP );
1235  curVelocity.ProjectOntoPlane( trace.normal, OVERCLIP );
1236 
1237  if ( stopEvent & SE_BLOCKED ) {
1238  // if going backwards
1239  if ( (curVelocity - gravityDir * curVelocity * gravityDir ) *
1240  (velocity - gravityDir * velocity * gravityDir) < 0.0f ) {
1241  path.endPos = curStart;
1242  path.endEvent = SE_BLOCKED;
1243 
1244  return true;
1245  }
1246  }
1247  }
1248 
1249  if ( j >= MAX_FRAME_SLIDE ) {
1250  if ( stopEvent & SE_BLOCKED ) {
1251  path.endPos = curStart;
1252  path.endEvent = SE_BLOCKED;
1253  return true;
1254  }
1255  }
1256 
1257  // add gravity
1258  curVelocity += gravity * frameTime * 0.001f;
1259  }
1260 
1261  path.endTime = totalTime;
1262  path.endVelocity = curVelocity;
1263  path.endPos = curStart;
1264  path.endEvent = 0;
1265 
1266  return false;
1267 }
1268 
1269 
1270 /*
1271 ===============================================================================
1272 
1273  Trajectory Prediction
1274 
1275  Finds the best collision free trajectory for a clip model based on an
1276  initial position, target position and speed.
1277 
1278 ===============================================================================
1279 */
1280 
1281 /*
1282 =====================
1283 Ballistics
1284 
1285  get the ideal aim pitch angle in order to hit the target
1286  also get the time it takes for the projectile to arrive at the target
1287 =====================
1288 */
1289 typedef struct ballistics_s {
1290  float angle; // angle in degrees in the range [-180, 180]
1291  float time; // time it takes before the projectile arrives
1292 } ballistics_t;
1293 
1294 static int Ballistics( const idVec3 &start, const idVec3 &end, float speed, float gravity, ballistics_t bal[2] ) {
1295  int n, i;
1296  float x, y, a, b, c, d, sqrtd, inva, p[2];
1297 
1298  x = ( end.ToVec2() - start.ToVec2() ).Length();
1299  y = end[2] - start[2];
1300 
1301  a = 4.0f * y * y + 4.0f * x * x;
1302  b = -4.0f * speed * speed - 4.0f * y * gravity;
1303  c = gravity * gravity;
1304 
1305  d = b * b - 4.0f * a * c;
1306  if ( d <= 0.0f || a == 0.0f ) {
1307  return 0;
1308  }
1309  sqrtd = idMath::Sqrt( d );
1310  inva = 0.5f / a;
1311  p[0] = ( - b + sqrtd ) * inva;
1312  p[1] = ( - b - sqrtd ) * inva;
1313  n = 0;
1314  for ( i = 0; i < 2; i++ ) {
1315  if ( p[i] <= 0.0f ) {
1316  continue;
1317  }
1318  d = idMath::Sqrt( p[i] );
1319  bal[n].angle = atan2( 0.5f * ( 2.0f * y * p[i] - gravity ) / d, d * x );
1320  bal[n].time = x / ( cos( bal[n].angle ) * speed );
1321  bal[n].angle = idMath::AngleNormalize180( RAD2DEG( bal[n].angle ) );
1322  n++;
1323  }
1324 
1325  return n;
1326 }
1327 
1328 /*
1329 =====================
1330 HeightForTrajectory
1331 
1332 Returns the maximum hieght of a given trajectory
1333 =====================
1334 */
1335 static float HeightForTrajectory( const idVec3 &start, float zVel, float gravity ) {
1336  float maxHeight, t;
1337 
1338  t = zVel / gravity;
1339  // maximum height of projectile
1340  maxHeight = start.z - 0.5f * gravity * ( t * t );
1341 
1342  return maxHeight;
1343 }
1344 
1345 /*
1346 =====================
1347 idAI::TestTrajectory
1348 =====================
1349 */
1350 bool idAI::TestTrajectory( const idVec3 &start, const idVec3 &end, float zVel, float gravity, float time, float max_height, const idClipModel *clip, int clipmask, const idEntity *ignore, const idEntity *targetEntity, int drawtime ) {
1351  int i, numSegments;
1352  float maxHeight, t, t2;
1353  idVec3 points[5];
1354  trace_t trace;
1355  bool result;
1356 
1357  t = zVel / gravity;
1358  // maximum height of projectile
1359  maxHeight = start.z - 0.5f * gravity * ( t * t );
1360  // time it takes to fall from the top to the end height
1361  t = idMath::Sqrt( ( maxHeight - end.z ) / ( 0.5f * -gravity ) );
1362 
1363  // start of parabolic
1364  points[0] = start;
1365 
1366  if ( t < time ) {
1367  numSegments = 4;
1368  // point in the middle between top and start
1369  t2 = ( time - t ) * 0.5f;
1370  points[1].ToVec2() = start.ToVec2() + (end.ToVec2() - start.ToVec2()) * ( t2 / time );
1371  points[1].z = start.z + t2 * zVel + 0.5f * gravity * t2 * t2;
1372  // top of parabolic
1373  t2 = time - t;
1374  points[2].ToVec2() = start.ToVec2() + (end.ToVec2() - start.ToVec2()) * ( t2 / time );
1375  points[2].z = start.z + t2 * zVel + 0.5f * gravity * t2 * t2;
1376  // point in the middel between top and end
1377  t2 = time - t * 0.5f;
1378  points[3].ToVec2() = start.ToVec2() + (end.ToVec2() - start.ToVec2()) * ( t2 / time );
1379  points[3].z = start.z + t2 * zVel + 0.5f * gravity * t2 * t2;
1380  } else {
1381  numSegments = 2;
1382  // point halfway through
1383  t2 = time * 0.5f;
1384  points[1].ToVec2() = start.ToVec2() + ( end.ToVec2() - start.ToVec2() ) * 0.5f;
1385  points[1].z = start.z + t2 * zVel + 0.5f * gravity * t2 * t2;
1386  }
1387 
1388  // end of parabolic
1389  points[numSegments] = end;
1390 
1391  if ( drawtime ) {
1392  for ( i = 0; i < numSegments; i++ ) {
1393  gameRenderWorld->DebugLine( colorRed, points[i], points[i+1], drawtime );
1394  }
1395  }
1396 
1397  // make sure projectile doesn't go higher than we want it to go
1398  for ( i = 0; i < numSegments; i++ ) {
1399  if ( points[i].z > max_height ) {
1400  // goes higher than we want to allow
1401  return false;
1402  }
1403  }
1404 
1405  result = true;
1406  for ( i = 0; i < numSegments; i++ ) {
1407  gameLocal.clip.Translation( trace, points[i], points[i+1], clip, mat3_identity, clipmask, ignore );
1408  if ( trace.fraction < 1.0f ) {
1409  if ( gameLocal.GetTraceEntity( trace ) == targetEntity ) {
1410  result = true;
1411  } else {
1412  result = false;
1413  }
1414  break;
1415  }
1416  }
1417 
1418  if ( drawtime ) {
1419  if ( clip ) {
1420  gameRenderWorld->DebugBounds( result ? colorGreen : colorYellow, clip->GetBounds().Expand( 1.0f ), trace.endpos, drawtime );
1421  } else {
1422  idBounds bnds( trace.endpos );
1423  bnds.ExpandSelf( 1.0f );
1424  gameRenderWorld->DebugBounds( result ? colorGreen : colorYellow, bnds, vec3_zero, drawtime );
1425  }
1426  }
1427 
1428  return result;
1429 }
1430 
1431 /*
1432 =====================
1433 idAI::PredictTrajectory
1434 
1435  returns true if there is a collision free trajectory for the clip model
1436  aimDir is set to the ideal aim direction in order to hit the target
1437 =====================
1438 */
1439 bool idAI::PredictTrajectory( const idVec3 &firePos, const idVec3 &target, float projectileSpeed, const idVec3 &projGravity, const idClipModel *clip, int clipmask, float max_height, const idEntity *ignore, const idEntity *targetEntity, int drawtime, idVec3 &aimDir ) {
1440  int n, i, j;
1441  float zVel, a, t, pitch, s, c;
1442  trace_t trace;
1443  ballistics_t ballistics[2];
1444  idVec3 dir[2];
1445  idVec3 velocity;
1446  idVec3 lastPos, pos;
1447 
1448  assert( targetEntity );
1449 
1450  // check if the projectile starts inside the target
1451  if ( targetEntity->GetPhysics()->GetAbsBounds().IntersectsBounds( clip->GetBounds().Translate( firePos ) ) ) {
1452  aimDir = target - firePos;
1453  aimDir.Normalize();
1454  return true;
1455  }
1456 
1457  // if no velocity or the projectile is not affected by gravity
1458  if ( projectileSpeed <= 0.0f || projGravity == vec3_origin ) {
1459 
1460  aimDir = target - firePos;
1461  aimDir.Normalize();
1462 
1463  gameLocal.clip.Translation( trace, firePos, target, clip, mat3_identity, clipmask, ignore );
1464 
1465  if ( drawtime ) {
1466  gameRenderWorld->DebugLine( colorRed, firePos, target, drawtime );
1467  idBounds bnds( trace.endpos );
1468  bnds.ExpandSelf( 1.0f );
1469  gameRenderWorld->DebugBounds( ( trace.fraction >= 1.0f || ( gameLocal.GetTraceEntity( trace ) == targetEntity ) ) ? colorGreen : colorYellow, bnds, vec3_zero, drawtime );
1470  }
1471 
1472  return ( trace.fraction >= 1.0f || ( gameLocal.GetTraceEntity( trace ) == targetEntity ) );
1473  }
1474 
1475  n = Ballistics( firePos, target, projectileSpeed, projGravity[2], ballistics );
1476  if ( n == 0 ) {
1477  // there is no valid trajectory
1478  aimDir = target - firePos;
1479  aimDir.Normalize();
1480  return false;
1481  }
1482 
1483  // make sure the first angle is the smallest
1484  if ( n == 2 ) {
1485  if ( ballistics[1].angle < ballistics[0].angle ) {
1486  a = ballistics[0].angle; ballistics[0].angle = ballistics[1].angle; ballistics[1].angle = a;
1487  t = ballistics[0].time; ballistics[0].time = ballistics[1].time; ballistics[1].time = t;
1488  }
1489  }
1490 
1491  // test if there is a collision free trajectory
1492  for ( i = 0; i < n; i++ ) {
1493  pitch = DEG2RAD( ballistics[i].angle );
1494  idMath::SinCos( pitch, s, c );
1495  dir[i] = target - firePos;
1496  dir[i].z = 0.0f;
1497  dir[i] *= c * idMath::InvSqrt( dir[i].LengthSqr() );
1498  dir[i].z = s;
1499 
1500  zVel = projectileSpeed * dir[i].z;
1501 
1502  if ( ai_debugTrajectory.GetBool() ) {
1503  t = ballistics[i].time / 100.0f;
1504  velocity = dir[i] * projectileSpeed;
1505  lastPos = firePos;
1506  pos = firePos;
1507  for ( j = 1; j < 100; j++ ) {
1508  pos += velocity * t;
1509  velocity += projGravity * t;
1510  gameRenderWorld->DebugLine( colorCyan, lastPos, pos );
1511  lastPos = pos;
1512  }
1513  }
1514 
1515  if ( TestTrajectory( firePos, target, zVel, projGravity[2], ballistics[i].time, firePos.z + max_height, clip, clipmask, ignore, targetEntity, drawtime ) ) {
1516  aimDir = dir[i];
1517  return true;
1518  }
1519  }
1520 
1521  aimDir = dir[0];
1522 
1523  // there is no collision free trajectory
1524  return false;
1525 }
virtual const idVec3 & GetOrigin(int id=0) const =0
int ClipModelsTouchingBounds(const idBounds &bounds, int contentMask, idClipModel **clipModelList, int maxCount) const
Definition: Clip.cpp:804
idEntity * GetEntity(void) const
Definition: Clip.h:178
int GetObstacles(const idPhysics *physics, const idAAS *aas, const idEntity *ignore, int areaNum, const idVec3 &startPos, const idVec3 &seekPos, obstacle_t *obstacles, int maxObstacles, idBounds &clipBounds)
Definition: AI_pathing.cpp:309
GLsizei const GLfloat * points
Definition: glext.h:3884
static const float INFINITY
Definition: Math.h:218
#define min(a, b)
float Normalize(void)
Definition: Vector.h:646
idVec4 colorGreen
Definition: Lib.cpp:118
assert(prefInfo.fullscreenBtn)
void DrawPathTree(const pathNode_t *root, const float height)
Definition: AI_pathing.cpp:504
virtual const idVec3 & GetGravityNormal(void) const =0
const idVec3 & Normal(void) const
Definition: Plane.h:239
idMat3 mat3_identity(idVec3(1, 0, 0), idVec3(0, 1, 0), idVec3(0, 0, 1))
virtual int GetClipMask(int id=-1) const =0
bool FindOptimalPath(const pathNode_t *root, const obstacle_t *obstacles, int numObstacles, const float height, const idVec3 &curDir, idVec3 &seekPos)
Definition: AI_pathing.cpp:833
virtual void DebugArrow(const idVec4 &color, const idVec3 &start, const idVec3 &end, int size, const int lifetime=0)=0
void Expand(const float d)
Definition: Winding2D.cpp:119
idClip clip
Definition: Game_local.h:296
idVec4 colorWhite
Definition: Lib.cpp:116
struct ballistics_s ballistics_t
const int MAX_FRAME_SLIDE
#define MAX_GENTITIES
Definition: Game_local.h:83
bool GetPathNodeDelta(pathNode_t *node, const obstacle_t *obstacles, const idVec2 &seekPos, bool blocked)
Definition: AI_pathing.cpp:528
float y
Definition: Vector.h:55
idVec3 endNormal
Definition: AI.h:132
float maxStepHeight
Definition: AASFile.h:228
idEntity * startPosObstacle
Definition: AI.h:115
idCVar ai_showObstacleAvoidance("ai_showObstacleAvoidance","0", CVAR_GAME|CVAR_INTEGER,"draws obstacle avoidance information for monsters. if 2, draws obstacles for player, as well", 0, 2, idCmdSystem::ArgCompletion_Integer< 0, 2 >)
GLenum GLenum GLenum GLenum GLenum scale
Definition: glext.h:4804
bool IsType(const idTypeInfo &c) const
Definition: Class.h:337
struct pathNode_s * children[2]
Definition: AI_pathing.cpp:75
float dist
Definition: AI_pathing.cpp:70
GLenum GLint GLint y
Definition: glext.h:2849
const float CLIP_BOUNDS_EPSILON
Definition: AI_pathing.cpp:54
const idMat3 & GetAxis(void) const
Definition: Clip.h:210
#define CM_BOX_EPSILON
GLenum GLsizei n
Definition: glext.h:3705
float z
Definition: Vector.h:320
bool PathTrace(const idEntity *ent, const idAAS *aas, const idVec3 &start, const idVec3 &end, int stopEvent, struct pathTrace_s &trace, predictedPath_t &path)
const int MAX_AAS_WALL_EDGES
Definition: AI_pathing.cpp:55
int endEvent
Definition: AI.h:134
idBlockAlloc< pathNode_t, 128 > pathNodeAllocator
Definition: AI_pathing.cpp:90
type * Get(void)
Definition: Queue.h:74
Definition: Vector.h:316
idEntity * seekPosObstacle
Definition: AI.h:117
virtual const idVec3 & GetLinearVelocity(int id=0) const =0
static float Sqrt(float x)
Definition: Math.h:302
ID_INLINE T Square(T x)
Definition: Math.h:104
int flags
Definition: AASFile.h:194
static bool TestTrajectory(const idVec3 &start, const idVec3 &end, float zVel, float gravity, float time, float max_height, const idClipModel *clip, int clipmask, const idEntity *ignore, const idEntity *targetEntity, int drawtime)
static bool FindPathAroundObstacles(const idPhysics *physics, const idAAS *aas, const idEntity *ignore, const idVec3 &startPos, const idVec3 &seekPos, obstaclePath_t &path)
Definition: AI_pathing.cpp:925
static bool PredictPath(const idEntity *ent, const idAAS *aas, const idVec3 &start, const idVec3 &velocity, int totalTime, int frameTime, int stopEvent, predictedPath_t &path)
const idVec3 & GetOrigin(void) const
Definition: Clip.h:206
struct pathNode_s * next
Definition: AI_pathing.cpp:76
virtual int AreaFlags(int areaNum) const =0
virtual int PointReachableAreaNum(const idVec3 &origin, const idBounds &bounds, const int areaFlags) const =0
#define AREA_REACHABLE_FLY
Definition: AASFile.h:75
idCVar ai_debugTrajectory("ai_debugTrajectory","0", CVAR_GAME|CVAR_BOOL,"draws trajectory tests for monsters")
GLdouble s
Definition: glext.h:2935
virtual int GetWallEdges(int areaNum, const idBounds &bounds, int travelFlags, int *edges, int maxEdges) const =0
float minFloorCos
Definition: AASFile.h:232
float x
Definition: Vector.h:318
idBounds boundingBoxes[MAX_AAS_BOUNDING_BOXES]
Definition: AASFile.h:215
GLenum GLint x
Definition: glext.h:2849
int i
Definition: process.py:33
float Length(void) const
Definition: Vector.h:155
#define MASK_MONSTERSOLID
Definition: Game_local.h:736
contactInfo_t c
Boolean result
Definition: AI.h:122
static void SinCos(float a, float &s, float &c)
Definition: Math.h:390
idVec3 gravityDir
Definition: AASFile.h:225
void Add(type *element)
Definition: Queue.h:63
int travelFlags
Definition: AASFile.h:195
const idVec2 & ToVec2(void) const
Definition: Vector.h:711
float fraction
idVec3 endpos
idVec4 colorRed
Definition: Lib.cpp:117
struct pathNode_s pathNode_t
void Zero(void)
Definition: Vector.h:119
idEntity * firstObstacle
Definition: AI.h:113
int endTime
Definition: AI.h:133
void void void Warning(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:735
virtual const idBounds & GetBounds(int id=-1) const =0
pathNode_t * BuildPathTree(const obstacle_t *obstacles, int numObstacles, const idBounds &clipBounds, const idVec2 &startPos, const idVec2 &seekPos, obstaclePath_t &path)
Definition: AI_pathing.cpp:592
virtual void GetEdge(int edgeNum, idVec3 &start, idVec3 &end) const =0
void Clear(void)
Definition: Winding2D.h:115
bool AddPoint(const idVec3 &v)
Definition: Bounds.h:226
idVec3 gravity
Definition: AASFile.h:224
GLfloat GLfloat GLfloat v2
Definition: glext.h:3608
idBounds Translate(const idVec3 &translation) const
Definition: Bounds.h:332
idVec3 seekPosOutsideObstacles
Definition: AI.h:116
idVec4 colorYellow
Definition: Lib.cpp:120
void FreePathTree_r(pathNode_t *node)
Definition: AI_pathing.cpp:489
Definition: Vector.h:52
#define FLOATSIGNBITSET(f)
Definition: Math.h:68
idVec2 pos
Definition: AI_pathing.cpp:68
idPhysics * GetPhysics(void) const
Definition: Entity.cpp:2607
virtual void DebugLine(const idVec4 &color, const idVec3 &start, const idVec3 &end, const int lifetime=0, const bool depthTest=false)=0
const GLubyte * c
Definition: glext.h:4677
GLubyte GLubyte GLubyte GLubyte w
Definition: glext.h:3454
idWorldspawn * world
Definition: Game_local.h:280
idVec3 vec3_origin(0.0f, 0.0f, 0.0f)
int GetParallelProjectionSilhouetteVerts(const idVec3 &projectionDir, idVec3 silVerts[6]) const
Definition: Box.cpp:822
#define vec3_zero
Definition: Vector.h:390
idVec3 endpos
Definition: AASFile.h:200
GLuint GLuint end
Definition: glext.h:2845
type * Alloc(void)
Definition: Heap.h:197
const idVec3 DEFAULT_GRAVITY_VEC3(0, 0,-DEFAULT_GRAVITY)
static float Fabs(float f)
Definition: Math.h:779
int PointInsideObstacle(const obstacle_t *obstacles, const int numObstacles, const idVec2 &point)
Definition: AI_pathing.cpp:125
virtual int AreaTravelFlags(int areaNum) const =0
#define NULL
Definition: Lib.h:88
const idBounds & GetBounds(void) const
Definition: Clip.h:198
float PathLength(idVec2 optimizedPath[MAX_OBSTACLE_PATH], int numPathPoints, const idVec2 &curDir)
Definition: AI_pathing.cpp:809
void Shutdown(void)
Definition: Heap.h:224
float y
Definition: Vector.h:319
const idEntity * blockingEntity
const int MAX_PATH_NODES
Definition: AI_pathing.cpp:57
#define TFL_WALK
Definition: AASFile.h:45
#define AREA_LEDGE
Definition: AASFile.h:70
bool IsTraceModel(void) const
Definition: Clip.h:218
int blockingAreaNum
Definition: AASFile.h:203
virtual bool Trace(aasTrace_t &trace, const idVec3 &start, const idVec3 &end) const =0
static idVec3 Plane2DFromPoints(const idVec2 &start, const idVec2 &end, const bool normalize=false)
Definition: Winding2D.h:127
const idEntity * blockingEntity
Definition: AI.h:135
void ExpandForAxialBox(const idVec2 bounds[2])
Definition: Winding2D.cpp:75
idVec3 seekPos
Definition: AI.h:112
int GetNumPoints(void) const
Definition: Winding2D.h:123
const float PUSH_OUTSIDE_OBSTACLES
Definition: AI_pathing.cpp:53
struct obstacle_s obstacle_t
float x
Definition: Vector.h:54
const char * path
Definition: sws.c:117
struct pathTrace_s pathTrace_t
int GetAllocCount(void) const
Definition: Heap.h:165
idVec3 endVelocity
Definition: AI.h:131
void PrunePathTree(pathNode_t *root, const idVec2 &seekPos)
Definition: AI_pathing.cpp:697
static float InvSqrt(float x)
Definition: Math.h:268
float fraction
Definition: AASFile.h:199
idGameLocal gameLocal
Definition: Game_local.cpp:64
float LengthSqr(void) const
Definition: Vector.h:635
GLubyte GLubyte GLubyte a
Definition: glext.h:4662
int OptimizePath(const pathNode_t *root, const pathNode_t *leafNode, const obstacle_t *obstacles, int numObstacles, idVec2 optimizedPath[MAX_OBSTACLE_PATH])
Definition: AI_pathing.cpp:749
#define DEG2RAD(a)
Definition: Math.h:56
idBounds Expand(const float d) const
Definition: Bounds.h:317
virtual idClipModel * GetClipModel(int id=0) const =0
GLfloat GLfloat v1
Definition: glext.h:3607
GLenum GLsizei GLsizei height
Definition: glext.h:2856
bool LineIntersectsPath(const idVec2 &start, const idVec2 &end, const pathNode_t *node)
Definition: AI_pathing.cpp:98
bool RayIntersection(const idVec2 &start, const idVec2 &dir, float &scale1, float &scale2, int *edgeNums=NULL) const
Definition: Winding2D.cpp:685
GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint GLdouble GLdouble w2
Definition: glext.h:4080
GLubyte GLubyte b
Definition: glext.h:4662
idEntity * entities[MAX_GENTITIES]
Definition: Game_local.h:275
int health
Definition: Entity.h:134
idEntity * GetTraceEntity(const trace_t &trace) const
idVec4 colorCyan
Definition: Lib.cpp:122
idVec3 invGravityDir
Definition: AASFile.h:226
int planeNum
Definition: AASFile.h:201
virtual const idPlane & GetPlane(int planeNum) const =0
virtual void GetEdgeVertexNumbers(int edgeNum, int verts[2]) const =0
void ProjectOntoPlane(const idVec3 &normal, const float overBounce=1.0f)
Definition: Vector.h:769
static bool PredictTrajectory(const idVec3 &firePos, const idVec3 &target, float projectileSpeed, const idVec3 &projGravity, const idClipModel *clip, int clipmask, float max_height, const idEntity *ignore, const idEntity *targetEntity, int drawtime, idVec3 &aimDir)
void Init()
Definition: AI_pathing.cpp:80
bool GetBool(void) const
Definition: CVarSystem.h:142
tuple f
Definition: idal.py:89
void GetBounds(idVec2 bounds[2]) const
Definition: Winding2D.cpp:444
idVec3 startPosOutsideObstacles
Definition: AI.h:114
struct pathNode_s * parent
Definition: AI_pathing.cpp:74
#define FLOATSIGNBITNOTSET(f)
Definition: Math.h:69
virtual void SortWallEdges(int *edges, int numEdges) const =0
#define AREA_REACHABLE_WALK
Definition: AASFile.h:74
virtual const idAASSettings * GetSettings(void) const =0
bool IntersectsBounds(const idBounds &a) const
Definition: Bounds.h:361
static void FreeObstacleAvoidanceNodes(void)
Definition: AI_pathing.cpp:999
virtual const idBounds & GetAbsBounds(int id=-1) const =0
idCVar ai_debugMove("ai_debugMove","0", CVAR_GAME|CVAR_BOOL,"draws movement information for monsters")
#define RAD2DEG(a)
Definition: Math.h:57
#define TFL_INVALID
Definition: AASFile.h:44
static float AngleNormalize180(float angle)
Definition: Math.h:910
void GetPointOutsideObstacles(const obstacle_t *obstacles, const int numObstacles, idVec2 &point, int *obstacle, int *edgeNum)
Definition: AI_pathing.cpp:150
const int MAX_OBSTACLE_PATH
Definition: AI_pathing.cpp:58
int getOutOfSolid
Definition: AASFile.h:197
float Normalize(void)
Definition: Vector.h:170
bool GetFirstBlockingObstacle(const obstacle_t *obstacles, int numObstacles, int skipObstacle, const idVec2 &startPos, const idVec2 &delta, float &blockingScale, int &blockingObstacle, int &blockingEdgeNum)
Definition: AI_pathing.cpp:271
void AddPoint(const idVec2 &point)
Definition: Winding2D.h:119
idVec3 endPos
Definition: AI.h:130
void AxisProjection(const idVec3 &dir, float &min, float &max) const
Definition: Bounds.h:376
void TranslationEntities(trace_t &results, const idVec3 &start, const idVec3 &end, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity)
Definition: Clip.cpp:995
const idBounds & GetAbsBounds(void) const
Definition: Clip.h:202
void Free(type *element)
Definition: Heap.h:216
const int MAX_OBSTACLES
Definition: AI_pathing.cpp:56
const float OVERCLIP
GLint j
Definition: qgl.h:264
idBounds & ExpandSelf(const float d)
Definition: Bounds.h:322
virtual void PushPointIntoAreaNum(int areaNum, idVec3 &origin) const =0
virtual void DebugBounds(const idVec4 &color, const idBounds &bounds, const idVec3 &org=vec3_origin, const int lifetime=0)=0
idRenderWorld * gameRenderWorld
Definition: Game_local.cpp:55
idVec2 bounds[2]
Definition: AI_pathing.cpp:61
bool Translation(trace_t &results, const idVec3 &start, const idVec3 &end, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity)
Definition: Clip.cpp:1056
idEntity * entity
Definition: AI_pathing.cpp:63
#define max(x, y)
Definition: os.h:70
GLfloat GLfloat p
Definition: glext.h:4674
idWinding2D winding
Definition: AI_pathing.cpp:62
GLdouble GLdouble z
Definition: glext.h:3067
void Zero(void)
Definition: Vector.h:415
Definition: Box.h:40
const float MAX_OBSTACLE_RADIUS
Definition: AI_pathing.cpp:52
idVec2 delta
Definition: AI_pathing.cpp:69
GLuint start
Definition: glext.h:2845
float projectileSpeed
Definition: AI.h:348
float LengthSqr(void) const
Definition: Vector.h:166
idVec4 colorBlue
Definition: Lib.cpp:119
Definition: AAS.h:75
GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint GLdouble w1
Definition: glext.h:4080
GLdouble GLdouble t
Definition: glext.h:2943