AAS_pathing.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition 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 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. 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.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../../idlib/precompiled.h"
  22. #include "AAS_local.h"
  23. #define SUBSAMPLE_WALK_PATH 1
  24. #define SUBSAMPLE_FLY_PATH 0
  25. const int maxWalkPathIterations = 10;
  26. const float maxWalkPathDistance = 500.0f;
  27. const float walkPathSampleDistance = 8.0f;
  28. const int maxFlyPathIterations = 10;
  29. const float maxFlyPathDistance = 500.0f;
  30. const float flyPathSampleDistance = 8.0f;
  31. /*
  32. ============
  33. idAASLocal::EdgeSplitPoint
  34. calculates split point of the edge with the plane
  35. returns true if the split point is between the edge vertices
  36. ============
  37. */
  38. bool idAASLocal::EdgeSplitPoint( idVec3 &split, int edgeNum, const idPlane &plane ) const {
  39. const aasEdge_t *edge;
  40. idVec3 v1, v2;
  41. float d1, d2;
  42. edge = &file->GetEdge( edgeNum );
  43. v1 = file->GetVertex( edge->vertexNum[0] );
  44. v2 = file->GetVertex( edge->vertexNum[1] );
  45. d1 = v1 * plane.Normal() - plane.Dist();
  46. d2 = v2 * plane.Normal() - plane.Dist();
  47. //if ( (d1 < CM_CLIP_EPSILON && d2 < CM_CLIP_EPSILON) || (d1 > -CM_CLIP_EPSILON && d2 > -CM_CLIP_EPSILON) ) {
  48. if ( IEEE_FLT_SIGNBITSET( d1 ) == IEEE_FLT_SIGNBITSET( d2 ) ) {
  49. return false;
  50. }
  51. split = v1 + (d1 / (d1 - d2)) * (v2 - v1);
  52. return true;
  53. }
  54. /*
  55. ============
  56. idAASLocal::FloorEdgeSplitPoint
  57. calculates either the closest or furthest point on the floor of the area which also lies on the pathPlane
  58. the point has to be on the front side of the frontPlane to be valid
  59. ============
  60. */
  61. bool idAASLocal::FloorEdgeSplitPoint( idVec3 &bestSplit, int areaNum, const idPlane &pathPlane, const idPlane &frontPlane, bool closest ) const {
  62. int i, j, faceNum, edgeNum;
  63. const aasArea_t *area;
  64. const aasFace_t *face;
  65. idVec3 split;
  66. float dist, bestDist;
  67. if ( closest ) {
  68. bestDist = maxWalkPathDistance;
  69. } else {
  70. bestDist = -0.1f;
  71. }
  72. area = &file->GetArea( areaNum );
  73. for ( i = 0; i < area->numFaces; i++ ) {
  74. faceNum = file->GetFaceIndex( area->firstFace + i );
  75. face = &file->GetFace( abs(faceNum) );
  76. if ( !(face->flags & FACE_FLOOR ) ) {
  77. continue;
  78. }
  79. for ( j = 0; j < face->numEdges; j++ ) {
  80. edgeNum = file->GetEdgeIndex( face->firstEdge + j );
  81. if ( !EdgeSplitPoint( split, abs( edgeNum ), pathPlane ) ) {
  82. continue;
  83. }
  84. dist = frontPlane.Distance( split );
  85. if ( closest ) {
  86. if ( dist >= -0.1f && dist < bestDist ) {
  87. bestDist = dist;
  88. bestSplit = split;
  89. }
  90. } else {
  91. if ( dist > bestDist ) {
  92. bestDist = dist;
  93. bestSplit = split;
  94. }
  95. }
  96. }
  97. }
  98. if ( closest ) {
  99. return ( bestDist < maxWalkPathDistance );
  100. } else {
  101. return ( bestDist > -0.1f );
  102. }
  103. }
  104. /*
  105. ============
  106. idAASLocal::WalkPathValid
  107. returns true if one can walk in a straight line between origin and goalOrigin
  108. ============
  109. */
  110. bool idAASLocal::WalkPathValid( int areaNum, const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin, int travelFlags, idVec3 &endPos, int &endAreaNum ) const {
  111. int curAreaNum, lastAreaNum, lastAreas[4], lastAreaIndex;
  112. idPlane pathPlane, frontPlane, farPlane;
  113. idReachability *reach;
  114. const aasArea_t *area;
  115. idVec3 p, dir;
  116. if ( file == NULL ) {
  117. endPos = goalOrigin;
  118. endAreaNum = 0;
  119. return true;
  120. }
  121. lastAreas[0] = lastAreas[1] = lastAreas[2] = lastAreas[3] = areaNum;
  122. lastAreaIndex = 0;
  123. pathPlane.SetNormal( (goalOrigin - origin).Cross( file->GetSettings().gravityDir ) );
  124. pathPlane.Normalize();
  125. pathPlane.FitThroughPoint( origin );
  126. frontPlane.SetNormal( goalOrigin - origin );
  127. frontPlane.Normalize();
  128. frontPlane.FitThroughPoint( origin );
  129. farPlane.SetNormal( frontPlane.Normal() );
  130. farPlane.FitThroughPoint( goalOrigin );
  131. curAreaNum = areaNum;
  132. lastAreaNum = curAreaNum;
  133. while ( 1 ) {
  134. // find the furthest floor face split point on the path
  135. if ( !FloorEdgeSplitPoint( endPos, curAreaNum, pathPlane, frontPlane, false ) ) {
  136. endPos = origin;
  137. }
  138. // if we found a point near or further than the goal we're done
  139. if ( farPlane.Distance( endPos ) > -0.5f ) {
  140. break;
  141. }
  142. // if we reached the goal area we're done
  143. if ( curAreaNum == goalAreaNum ) {
  144. break;
  145. }
  146. frontPlane.SetDist( frontPlane.Normal() * endPos );
  147. area = &file->GetArea( curAreaNum );
  148. for ( reach = area->reach; reach; reach = reach->next ) {
  149. if ( reach->travelType != TFL_WALK ) {
  150. continue;
  151. }
  152. // if the reachability goes back to a previous area
  153. if ( reach->toAreaNum == lastAreas[0] || reach->toAreaNum == lastAreas[1] ||
  154. reach->toAreaNum == lastAreas[2] || reach->toAreaNum == lastAreas[3] ) {
  155. continue;
  156. }
  157. // if undesired travel flags are required to travel through the area
  158. if ( file->GetArea( reach->toAreaNum ).travelFlags & ~travelFlags ) {
  159. continue;
  160. }
  161. // don't optimize through an area near a ledge
  162. if ( file->GetArea( reach->toAreaNum ).flags & AREA_LEDGE ) {
  163. continue;
  164. }
  165. // find the closest floor face split point on the path
  166. if ( !FloorEdgeSplitPoint( p, reach->toAreaNum, pathPlane, frontPlane, true ) ) {
  167. continue;
  168. }
  169. // direction parallel to gravity
  170. dir = ( file->GetSettings().gravityDir * endPos * file->GetSettings().gravityDir ) -
  171. ( file->GetSettings().gravityDir * p * file->GetSettings().gravityDir );
  172. if ( dir.LengthSqr() > Square( file->GetSettings().maxStepHeight ) ) {
  173. continue;
  174. }
  175. // direction orthogonal to gravity
  176. dir = endPos - p - dir;
  177. if ( dir.LengthSqr() > Square( 0.2f ) ) {
  178. continue;
  179. }
  180. break;
  181. }
  182. if ( !reach ) {
  183. return false;
  184. }
  185. lastAreas[lastAreaIndex] = curAreaNum;
  186. lastAreaIndex = ( lastAreaIndex + 1 ) & 3;
  187. curAreaNum = reach->toAreaNum;
  188. }
  189. endAreaNum = curAreaNum;
  190. return true;
  191. }
  192. /*
  193. ============
  194. idAASLocal::SubSampleWalkPath
  195. ============
  196. */
  197. idVec3 idAASLocal::SubSampleWalkPath( int areaNum, const idVec3 &origin, const idVec3 &start, const idVec3 &end, int travelFlags, int &endAreaNum ) const {
  198. int i, numSamples, curAreaNum;
  199. idVec3 dir, point, nextPoint, endPos;
  200. dir = end - start;
  201. numSamples = (int) (dir.Length() / walkPathSampleDistance) + 1;
  202. point = start;
  203. for ( i = 1; i < numSamples; i++ ) {
  204. nextPoint = start + dir * ((float) i / numSamples);
  205. if ( (point - nextPoint).LengthSqr() > Square( maxWalkPathDistance ) ) {
  206. return point;
  207. }
  208. if ( !idAASLocal::WalkPathValid( areaNum, origin, 0, nextPoint, travelFlags, endPos, curAreaNum ) ) {
  209. return point;
  210. }
  211. point = nextPoint;
  212. endAreaNum = curAreaNum;
  213. }
  214. return point;
  215. }
  216. /*
  217. ============
  218. idAASLocal::WalkPathToGoal
  219. FIXME: don't stop optimizing on first failure ?
  220. ============
  221. */
  222. bool idAASLocal::WalkPathToGoal( aasPath_t &path, int areaNum, const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin, int travelFlags ) const {
  223. int i, travelTime, curAreaNum, lastAreas[4], lastAreaIndex, endAreaNum;
  224. idReachability * reach = NULL;
  225. idVec3 endPos;
  226. path.type = PATHTYPE_WALK;
  227. path.moveGoal = origin;
  228. path.moveAreaNum = areaNum;
  229. path.secondaryGoal = origin;
  230. path.reachability = NULL;
  231. if ( file == NULL || areaNum == goalAreaNum ) {
  232. path.moveGoal = goalOrigin;
  233. return true;
  234. }
  235. lastAreas[0] = lastAreas[1] = lastAreas[2] = lastAreas[3] = areaNum;
  236. lastAreaIndex = 0;
  237. curAreaNum = areaNum;
  238. for ( i = 0; i < maxWalkPathIterations; i++ ) {
  239. if ( !idAASLocal::RouteToGoalArea( curAreaNum, path.moveGoal, goalAreaNum, travelFlags, travelTime, &reach ) ) {
  240. break;
  241. }
  242. if ( !reach ) {
  243. return false;
  244. }
  245. // no need to check through the first area
  246. if ( areaNum != curAreaNum ) {
  247. // only optimize a limited distance ahead
  248. if ( (reach->start - origin).LengthSqr() > Square( maxWalkPathDistance ) ) {
  249. #if SUBSAMPLE_WALK_PATH
  250. path.moveGoal = SubSampleWalkPath( areaNum, origin, path.moveGoal, reach->start, travelFlags, path.moveAreaNum );
  251. #endif
  252. return true;
  253. }
  254. if ( !idAASLocal::WalkPathValid( areaNum, origin, 0, reach->start, travelFlags, endPos, endAreaNum ) ) {
  255. #if SUBSAMPLE_WALK_PATH
  256. path.moveGoal = SubSampleWalkPath( areaNum, origin, path.moveGoal, reach->start, travelFlags, path.moveAreaNum );
  257. #endif
  258. return true;
  259. }
  260. }
  261. path.moveGoal = reach->start;
  262. path.moveAreaNum = curAreaNum;
  263. if ( reach->travelType != TFL_WALK ) {
  264. break;
  265. }
  266. if ( !idAASLocal::WalkPathValid( areaNum, origin, 0, reach->end, travelFlags, endPos, endAreaNum ) ) {
  267. return true;
  268. }
  269. path.moveGoal = reach->end;
  270. path.moveAreaNum = reach->toAreaNum;
  271. if ( reach->toAreaNum == goalAreaNum ) {
  272. if ( !idAASLocal::WalkPathValid( areaNum, origin, 0, goalOrigin, travelFlags, endPos, endAreaNum ) ) {
  273. #if SUBSAMPLE_WALK_PATH
  274. path.moveGoal = SubSampleWalkPath( areaNum, origin, path.moveGoal, goalOrigin, travelFlags, path.moveAreaNum );
  275. #endif
  276. return true;
  277. }
  278. path.moveGoal = goalOrigin;
  279. path.moveAreaNum = goalAreaNum;
  280. return true;
  281. }
  282. lastAreas[lastAreaIndex] = curAreaNum;
  283. lastAreaIndex = ( lastAreaIndex + 1 ) & 3;
  284. curAreaNum = reach->toAreaNum;
  285. if ( curAreaNum == lastAreas[0] || curAreaNum == lastAreas[1] ||
  286. curAreaNum == lastAreas[2] || curAreaNum == lastAreas[3] ) {
  287. common->Warning( "idAASLocal::WalkPathToGoal: local routing minimum going from area %d to area %d", areaNum, goalAreaNum );
  288. break;
  289. }
  290. }
  291. if ( reach == NULL ) {
  292. return false;
  293. }
  294. switch( reach->travelType ) {
  295. case TFL_WALKOFFLEDGE:
  296. path.type = PATHTYPE_WALKOFFLEDGE;
  297. path.secondaryGoal = reach->end;
  298. path.reachability = reach;
  299. break;
  300. case TFL_BARRIERJUMP:
  301. path.type |= PATHTYPE_BARRIERJUMP;
  302. path.secondaryGoal = reach->end;
  303. path.reachability = reach;
  304. break;
  305. case TFL_JUMP:
  306. path.type |= PATHTYPE_JUMP;
  307. path.secondaryGoal = reach->end;
  308. path.reachability = reach;
  309. break;
  310. default:
  311. break;
  312. }
  313. return true;
  314. }
  315. /*
  316. ============
  317. idAASLocal::FlyPathValid
  318. returns true if one can fly in a straight line between origin and goalOrigin
  319. ============
  320. */
  321. bool idAASLocal::FlyPathValid( int areaNum, const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin, int travelFlags, idVec3 &endPos, int &endAreaNum ) const {
  322. aasTrace_t trace;
  323. if ( file == NULL ) {
  324. endPos = goalOrigin;
  325. endAreaNum = 0;
  326. return true;
  327. }
  328. file->Trace( trace, origin, goalOrigin );
  329. endPos = trace.endpos;
  330. endAreaNum = trace.lastAreaNum;
  331. if ( trace.fraction >= 1.0f ) {
  332. return true;
  333. }
  334. return false;
  335. }
  336. /*
  337. ============
  338. idAASLocal::SubSampleFlyPath
  339. ============
  340. */
  341. idVec3 idAASLocal::SubSampleFlyPath( int areaNum, const idVec3 &origin, const idVec3 &start, const idVec3 &end, int travelFlags, int &endAreaNum ) const {
  342. int i, numSamples, curAreaNum;
  343. idVec3 dir, point, nextPoint, endPos;
  344. dir = end - start;
  345. numSamples = (int) (dir.Length() / flyPathSampleDistance) + 1;
  346. point = start;
  347. for ( i = 1; i < numSamples; i++ ) {
  348. nextPoint = start + dir * ((float) i / numSamples);
  349. if ( (point - nextPoint).LengthSqr() > Square( maxFlyPathDistance ) ) {
  350. return point;
  351. }
  352. if ( !idAASLocal::FlyPathValid( areaNum, origin, 0, nextPoint, travelFlags, endPos, curAreaNum ) ) {
  353. return point;
  354. }
  355. point = nextPoint;
  356. endAreaNum = curAreaNum;
  357. }
  358. return point;
  359. }
  360. /*
  361. ============
  362. idAASLocal::FlyPathToGoal
  363. FIXME: don't stop optimizing on first failure ?
  364. ============
  365. */
  366. bool idAASLocal::FlyPathToGoal( aasPath_t &path, int areaNum, const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin, int travelFlags ) const {
  367. int i, travelTime, curAreaNum, lastAreas[4], lastAreaIndex, endAreaNum;
  368. idReachability *reach = NULL;
  369. idVec3 endPos;
  370. path.type = PATHTYPE_WALK;
  371. path.moveGoal = origin;
  372. path.moveAreaNum = areaNum;
  373. path.secondaryGoal = origin;
  374. path.reachability = NULL;
  375. if ( file == NULL || areaNum == goalAreaNum ) {
  376. path.moveGoal = goalOrigin;
  377. return true;
  378. }
  379. lastAreas[0] = lastAreas[1] = lastAreas[2] = lastAreas[3] = areaNum;
  380. lastAreaIndex = 0;
  381. curAreaNum = areaNum;
  382. for ( i = 0; i < maxFlyPathIterations; i++ ) {
  383. if ( !idAASLocal::RouteToGoalArea( curAreaNum, path.moveGoal, goalAreaNum, travelFlags, travelTime, &reach ) ) {
  384. break;
  385. }
  386. if ( !reach ) {
  387. return false;
  388. }
  389. // no need to check through the first area
  390. if ( areaNum != curAreaNum ) {
  391. if ( (reach->start - origin).LengthSqr() > Square( maxFlyPathDistance ) ) {
  392. #if SUBSAMPLE_FLY_PATH
  393. path.moveGoal = SubSampleFlyPath( areaNum, origin, path.moveGoal, reach->start, travelFlags, path.moveAreaNum );
  394. #endif
  395. return true;
  396. }
  397. if ( !idAASLocal::FlyPathValid( areaNum, origin, 0, reach->start, travelFlags, endPos, endAreaNum ) ) {
  398. #if SUBSAMPLE_FLY_PATH
  399. path.moveGoal = SubSampleFlyPath( areaNum, origin, path.moveGoal, reach->start, travelFlags, path.moveAreaNum );
  400. #endif
  401. return true;
  402. }
  403. }
  404. path.moveGoal = reach->start;
  405. path.moveAreaNum = curAreaNum;
  406. if ( !idAASLocal::FlyPathValid( areaNum, origin, 0, reach->end, travelFlags, endPos, endAreaNum ) ) {
  407. return true;
  408. }
  409. path.moveGoal = reach->end;
  410. path.moveAreaNum = reach->toAreaNum;
  411. if ( reach->toAreaNum == goalAreaNum ) {
  412. if ( !idAASLocal::FlyPathValid( areaNum, origin, 0, goalOrigin, travelFlags, endPos, endAreaNum ) ) {
  413. #if SUBSAMPLE_FLY_PATH
  414. path.moveGoal = SubSampleFlyPath( areaNum, origin, path.moveGoal, goalOrigin, travelFlags, path.moveAreaNum );
  415. #endif
  416. return true;
  417. }
  418. path.moveGoal = goalOrigin;
  419. path.moveAreaNum = goalAreaNum;
  420. return true;
  421. }
  422. lastAreas[lastAreaIndex] = curAreaNum;
  423. lastAreaIndex = ( lastAreaIndex + 1 ) & 3;
  424. curAreaNum = reach->toAreaNum;
  425. if ( curAreaNum == lastAreas[0] || curAreaNum == lastAreas[1] ||
  426. curAreaNum == lastAreas[2] || curAreaNum == lastAreas[3] ) {
  427. common->Warning( "idAASLocal::FlyPathToGoal: local routing minimum going from area %d to area %d", areaNum, goalAreaNum );
  428. break;
  429. }
  430. }
  431. if ( reach == NULL ) {
  432. return false;
  433. }
  434. return true;
  435. }
  436. typedef struct wallEdge_s {
  437. int edgeNum;
  438. int verts[2];
  439. struct wallEdge_s * next;
  440. } wallEdge_t;
  441. /*
  442. ============
  443. idAASLocal::SortWallEdges
  444. ============
  445. */
  446. void idAASLocal::SortWallEdges( int *edges, int numEdges ) const {
  447. int i, j, k, numSequences;
  448. wallEdge_t **sequenceFirst, **sequenceLast, *wallEdges, *wallEdge;
  449. wallEdges = (wallEdge_t *) _alloca16( numEdges * sizeof( wallEdge_t ) );
  450. sequenceFirst = (wallEdge_t **)_alloca16( numEdges * sizeof( wallEdge_t * ) );
  451. sequenceLast = (wallEdge_t **)_alloca16( numEdges * sizeof( wallEdge_t * ) );
  452. for ( i = 0; i < numEdges; i++ ) {
  453. wallEdges[i].edgeNum = edges[i];
  454. GetEdgeVertexNumbers( edges[i], wallEdges[i].verts );
  455. wallEdges[i].next = NULL;
  456. sequenceFirst[i] = &wallEdges[i];
  457. sequenceLast[i] = &wallEdges[i];
  458. }
  459. numSequences = numEdges;
  460. for ( i = 0; i < numSequences; i++ ) {
  461. for ( j = i+1; j < numSequences; j++ ) {
  462. if ( sequenceFirst[i]->verts[0] == sequenceLast[j]->verts[1] ) {
  463. sequenceLast[j]->next = sequenceFirst[i];
  464. sequenceFirst[i] = sequenceFirst[j];
  465. break;
  466. }
  467. if ( sequenceLast[i]->verts[1] == sequenceFirst[j]->verts[0] ) {
  468. sequenceLast[i]->next = sequenceFirst[j];
  469. break;
  470. }
  471. }
  472. if ( j < numSequences ) {
  473. numSequences--;
  474. for ( k = j; k < numSequences; k++ ) {
  475. sequenceFirst[k] = sequenceFirst[k+1];
  476. sequenceLast[k] = sequenceLast[k+1];
  477. }
  478. i = -1;
  479. }
  480. }
  481. k = 0;
  482. for ( i = 0; i < numSequences; i++ ) {
  483. for ( wallEdge = sequenceFirst[i]; wallEdge; wallEdge = wallEdge->next ) {
  484. edges[k++] = wallEdge->edgeNum;
  485. }
  486. }
  487. }
  488. /*
  489. ============
  490. idAASLocal::GetWallEdges
  491. ============
  492. */
  493. int idAASLocal::GetWallEdges( int areaNum, const idBounds &bounds, int travelFlags, int *edges, int maxEdges ) const {
  494. int i, j, k, l, face1Num, face2Num, edge1Num, edge2Num, numEdges, absEdge1Num;
  495. int *areaQueue, curArea, queueStart, queueEnd;
  496. byte *areasVisited;
  497. const aasArea_t *area;
  498. const aasFace_t *face1, *face2;
  499. idReachability *reach;
  500. if ( !file ) {
  501. return 0;
  502. }
  503. numEdges = 0;
  504. areasVisited = (byte *) _alloca16( file->GetNumAreas() );
  505. memset( areasVisited, 0, file->GetNumAreas() * sizeof( byte ) );
  506. areaQueue = (int *) _alloca16( file->GetNumAreas() * sizeof( int ) );
  507. queueStart = -1;
  508. queueEnd = 0;
  509. areaQueue[0] = areaNum;
  510. areasVisited[areaNum] = true;
  511. for ( curArea = areaNum; queueStart < queueEnd; curArea = areaQueue[++queueStart] ) {
  512. area = &file->GetArea( curArea );
  513. for ( i = 0; i < area->numFaces; i++ ) {
  514. face1Num = file->GetFaceIndex( area->firstFace + i );
  515. face1 = &file->GetFace( abs(face1Num) );
  516. if ( !(face1->flags & FACE_FLOOR ) ) {
  517. continue;
  518. }
  519. for ( j = 0; j < face1->numEdges; j++ ) {
  520. edge1Num = file->GetEdgeIndex( face1->firstEdge + j );
  521. absEdge1Num = abs( edge1Num );
  522. // test if the edge is shared by another floor face of this area
  523. for ( k = 0; k < area->numFaces; k++ ) {
  524. if ( k == i ) {
  525. continue;
  526. }
  527. face2Num = file->GetFaceIndex( area->firstFace + k );
  528. face2 = &file->GetFace( abs(face2Num) );
  529. if ( !(face2->flags & FACE_FLOOR ) ) {
  530. continue;
  531. }
  532. for ( l = 0; l < face2->numEdges; l++ ) {
  533. edge2Num = abs( file->GetEdgeIndex( face2->firstEdge + l ) );
  534. if ( edge2Num == absEdge1Num ) {
  535. break;
  536. }
  537. }
  538. if ( l < face2->numEdges ) {
  539. break;
  540. }
  541. }
  542. if ( k < area->numFaces ) {
  543. continue;
  544. }
  545. // test if the edge is used by a reachability
  546. for ( reach = area->reach; reach; reach = reach->next ) {
  547. if ( reach->travelType & travelFlags ) {
  548. if ( reach->edgeNum == absEdge1Num ) {
  549. break;
  550. }
  551. }
  552. }
  553. if ( reach ) {
  554. continue;
  555. }
  556. // test if the edge is already in the list
  557. for ( k = 0; k < numEdges; k++ ) {
  558. if ( edge1Num == edges[k] ) {
  559. break;
  560. }
  561. }
  562. if ( k < numEdges ) {
  563. continue;
  564. }
  565. // add the edge to the list
  566. edges[numEdges++] = edge1Num;
  567. if ( numEdges >= maxEdges ) {
  568. return numEdges;
  569. }
  570. }
  571. }
  572. // add new areas to the queue
  573. for ( reach = area->reach; reach; reach = reach->next ) {
  574. if ( reach->travelType & travelFlags ) {
  575. // if the area the reachability leads to hasn't been visited yet and the area bounds touch the search bounds
  576. if ( !areasVisited[reach->toAreaNum] && bounds.IntersectsBounds( file->GetArea( reach->toAreaNum ).bounds ) ) {
  577. areaQueue[queueEnd++] = reach->toAreaNum;
  578. areasVisited[reach->toAreaNum] = true;
  579. }
  580. }
  581. }
  582. }
  583. return numEdges;
  584. }