doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
AASReach.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 "AASFile.h"
33 #include "AASFile_local.h"
34 #include "AASReach.h"
35 
36 #define INSIDEUNITS 2.0f
37 #define INSIDEUNITS_WALKEND 0.5f
38 #define INSIDEUNITS_WALKSTART 0.1f
39 #define INSIDEUNITS_SWIMEND 0.5f
40 #define INSIDEUNITS_FLYEND 0.5f
41 #define INSIDEUNITS_WATERJUMP 15.0f
42 
43 
44 /*
45 ================
46 idAASReach::ReachabilityExists
47 ================
48 */
49 bool idAASReach::ReachabilityExists( int fromAreaNum, int toAreaNum ) {
50  aasArea_t *area;
51  idReachability *reach;
52 
53  area = &file->areas[fromAreaNum];
54  for ( reach = area->reach; reach; reach = reach->next ) {
55  if ( reach->toAreaNum == toAreaNum ) {
56  return true;
57  }
58  }
59  return false;
60 }
61 
62 /*
63 ================
64 idAASReach::CanSwimInArea
65 ================
66 */
67 ID_INLINE bool idAASReach::CanSwimInArea( int areaNum ) {
68  return ( file->areas[areaNum].contents & AREACONTENTS_WATER ) != 0;
69 }
70 
71 /*
72 ================
73 idAASReach::AreaHasFloor
74 ================
75 */
76 ID_INLINE bool idAASReach::AreaHasFloor( int areaNum ) {
77  return ( file->areas[areaNum].flags & AREA_FLOOR ) != 0;
78 }
79 
80 /*
81 ================
82 idAASReach::AreaIsClusterPortal
83 ================
84 */
85 ID_INLINE bool idAASReach::AreaIsClusterPortal( int areaNum ) {
86  return ( file->areas[areaNum].contents & AREACONTENTS_CLUSTERPORTAL ) != 0;
87 }
88 
89 /*
90 ================
91 idAASReach::AddReachabilityToArea
92 ================
93 */
95  aasArea_t *area;
96 
97  area = &file->areas[areaNum];
98  reach->next = area->reach;
99  area->reach = reach;
101 }
102 
103 /*
104 ================
105 idAASReach::Reachability_Fly
106 ================
107 */
108 void idAASReach::Reachability_Fly( int areaNum ) {
109  int i, faceNum, otherAreaNum;
110  aasArea_t *area;
111  aasFace_t *face;
112  idReachability_Fly *reach;
113 
114  area = &file->areas[areaNum];
115 
116  for ( i = 0; i < area->numFaces; i++ ) {
117  faceNum = file->faceIndex[area->firstFace + i];
118  face = &file->faces[abs(faceNum)];
119 
120  otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)];
121 
122  if ( otherAreaNum == 0 ) {
123  continue;
124  }
125 
126  if ( ReachabilityExists( areaNum, otherAreaNum ) ) {
127  continue;
128  }
129 
130  // create reachability going through this face
131  reach = new idReachability_Fly();
132  reach->travelType = TFL_FLY;
133  reach->toAreaNum = otherAreaNum;
134  reach->fromAreaNum = areaNum;
135  reach->edgeNum = 0;
136  reach->travelTime = 1;
137  reach->start = file->FaceCenter( abs(faceNum) );
138  if ( faceNum < 0 ) {
139  reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_FLYEND;
140  } else {
141  reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_FLYEND;
142  }
143  AddReachabilityToArea( reach, areaNum );
144  }
145 }
146 
147 /*
148 ================
149 idAASReach::Reachability_Swim
150 ================
151 */
152 void idAASReach::Reachability_Swim( int areaNum ) {
153  int i, faceNum, otherAreaNum;
154  aasArea_t *area;
155  aasFace_t *face;
156  idReachability_Swim *reach;
157 
158  if ( !CanSwimInArea( areaNum ) ) {
159  return;
160  }
161 
162  area = &file->areas[areaNum];
163 
164  for ( i = 0; i < area->numFaces; i++ ) {
165  faceNum = file->faceIndex[area->firstFace + i];
166  face = &file->faces[abs(faceNum)];
167 
168  otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)];
169 
170  if ( otherAreaNum == 0 ) {
171  continue;
172  }
173 
174  if ( !CanSwimInArea( otherAreaNum ) ) {
175  continue;
176  }
177 
178  if ( ReachabilityExists( areaNum, otherAreaNum ) ) {
179  continue;
180  }
181 
182  // create reachability going through this face
183  reach = new idReachability_Swim();
184  reach->travelType = TFL_SWIM;
185  reach->toAreaNum = otherAreaNum;
186  reach->fromAreaNum = areaNum;
187  reach->edgeNum = 0;
188  reach->travelTime = 1;
189  reach->start = file->FaceCenter( abs(faceNum) );
190  if ( faceNum < 0 ) {
191  reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_SWIMEND;
192  } else {
193  reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_SWIMEND;
194  }
195  AddReachabilityToArea( reach, areaNum );
196  }
197 }
198 
199 /*
200 ================
201 idAASReach::Reachability_EqualFloorHeight
202 ================
203 */
205  int i, k, l, m, n, faceNum, face1Num, face2Num, otherAreaNum, edge1Num, edge2Num;
206  aasArea_t *area, *otherArea;
207  aasFace_t *face, *face1, *face2;
208  idReachability_Walk *reach;
209 
210  if ( !AreaHasFloor( areaNum ) ) {
211  return;
212  }
213 
214  area = &file->areas[areaNum];
215 
216  for ( i = 0; i < area->numFaces; i++ ) {
217  faceNum = file->faceIndex[area->firstFace + i];
218  face = &file->faces[abs(faceNum)];
219 
220  otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)];
221  if ( !AreaHasFloor( otherAreaNum ) ) {
222  continue;
223  }
224 
225  otherArea = &file->areas[otherAreaNum];
226 
227  for ( k = 0; k < area->numFaces; k++ ) {
228  face1Num = file->faceIndex[area->firstFace + k];
229  face1 = &file->faces[abs(face1Num)];
230 
231  if ( !( face1->flags & FACE_FLOOR ) ) {
232  continue;
233  }
234  for ( l = 0; l < otherArea->numFaces; l++ ) {
235  face2Num = file->faceIndex[otherArea->firstFace + l];
236  face2 = &file->faces[abs(face2Num)];
237 
238  if ( !( face2->flags & FACE_FLOOR ) ) {
239  continue;
240  }
241 
242  for ( m = 0; m < face1->numEdges; m++ ) {
243  edge1Num = abs(file->edgeIndex[face1->firstEdge + m]);
244  for ( n = 0; n < face2->numEdges; n++ ) {
245  edge2Num = abs(file->edgeIndex[face2->firstEdge + n]);
246  if ( edge1Num == edge2Num ) {
247  break;
248  }
249  }
250  if ( n < face2->numEdges ) {
251  break;
252  }
253  }
254  if ( m < face1->numEdges ) {
255  break;
256  }
257  }
258  if ( l < otherArea->numFaces ) {
259  break;
260  }
261  }
262  if ( k < area->numFaces ) {
263  // create reachability
264  reach = new idReachability_Walk();
265  reach->travelType = TFL_WALK;
266  reach->toAreaNum = otherAreaNum;
267  reach->fromAreaNum = areaNum;
268  reach->edgeNum = abs( edge1Num );
269  reach->travelTime = 1;
270  reach->start = file->EdgeCenter( edge1Num );
271  if ( faceNum < 0 ) {
272  reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_WALKEND;
273  }
274  else {
275  reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_WALKEND;
276  }
277  AddReachabilityToArea( reach, areaNum );
278  }
279  }
280 }
281 
282 /*
283 ================
284 idAASReach::Reachability_Step_Barrier_WaterJump_WalkOffLedge
285 ================
286 */
288  int i, j, k, l, edge1Num, edge2Num, areas[10];
289  int floor_bestArea1FloorEdgeNum, floor_bestArea2FloorEdgeNum, floor_foundReach;
290  int water_bestArea1FloorEdgeNum, water_bestArea2FloorEdgeNum, water_foundReach;
291  int side1, faceSide1, floorFace1Num;
292  float dist, dist1, dist2, diff, invGravityDot, orthogonalDot;
293  float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y;
294  float length, floor_bestLength, water_bestLength, floor_bestDist, water_bestDist;
295  idVec3 v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2;
296  idVec3 normal, orthogonal, edgeVec, start, end;
297  idVec3 floor_bestStart, floor_bestEnd, floor_bestNormal;
298  idVec3 water_bestStart, water_bestEnd, water_bestNormal;
299  idVec3 testPoint;
300  idPlane *plane;
301  aasArea_t *area1, *area2;
302  aasFace_t *floorFace1, *floorFace2, *floor_bestFace1, *water_bestFace1;
303  aasEdge_t *edge1, *edge2;
304  idReachability_Walk *walkReach;
305  idReachability_BarrierJump *barrierJumpReach;
306  idReachability_WaterJump *waterJumpReach;
307  idReachability_WalkOffLedge *walkOffLedgeReach;
308  aasTrace_t trace;
309 
310  // must be able to walk or swim in the first area
311  if ( !AreaHasFloor( area1num ) && !CanSwimInArea( area1num ) ) {
312  return false;
313  }
314 
315  if ( !AreaHasFloor( area2num ) && !CanSwimInArea( area2num ) ) {
316  return false;
317  }
318 
319  area1 = &file->areas[area1num];
320  area2 = &file->areas[area2num];
321 
322  // if the areas are not near anough in the x-y direction
323  for ( i = 0; i < 2; i++ ) {
324  if ( area1->bounds[0][i] > area2->bounds[1][i] + 2.0f ) {
325  return false;
326  }
327  if ( area1->bounds[1][i] < area2->bounds[0][i] - 2.0f ) {
328  return false;
329  }
330  }
331 
332  floor_foundReach = false;
333  floor_bestDist = 99999;
334  floor_bestLength = 0;
335  floor_bestArea2FloorEdgeNum = 0;
336 
337  water_foundReach = false;
338  water_bestDist = 99999;
339  water_bestLength = 0;
340  water_bestArea2FloorEdgeNum = 0;
341 
342  for ( i = 0; i < area1->numFaces; i++ ) {
343  floorFace1Num = file->faceIndex[area1->firstFace + i];
344  faceSide1 = floorFace1Num < 0;
345  floorFace1 = &file->faces[abs(floorFace1Num)];
346 
347  // if this isn't a floor face
348  if ( !(floorFace1->flags & FACE_FLOOR) ) {
349 
350  // if we can swim in the first area
351  if ( CanSwimInArea( area1num ) ) {
352 
353  // face plane must be more or less horizontal
354  plane = &file->planeList[ floorFace1->planeNum ^ (!faceSide1) ];
355  if ( plane->Normal() * file->settings.invGravityDir < file->settings.minFloorCos ) {
356  continue;
357  }
358  }
359  else {
360  // if we can't swim in the area it must be a ground face
361  continue;
362  }
363  }
364 
365  for ( k = 0; k < floorFace1->numEdges; k++ ) {
366  edge1Num = file->edgeIndex[floorFace1->firstEdge + k];
367  side1 = (edge1Num < 0);
368  // NOTE: for water faces we must take the side area 1 is on into
369  // account because the face is shared and doesn't have to be oriented correctly
370  if ( !(floorFace1->flags & FACE_FLOOR) ) {
371  side1 = (side1 == faceSide1);
372  }
373  edge1Num = abs(edge1Num);
374  edge1 = &file->edges[edge1Num];
375  // vertices of the edge
376  v1 = file->vertices[edge1->vertexNum[!side1]];
377  v2 = file->vertices[edge1->vertexNum[side1]];
378  // get a vertical plane through the edge
379  // NOTE: normal is pointing into area 2 because the face edges are stored counter clockwise
380  edgeVec = v2 - v1;
381  normal = edgeVec.Cross( file->settings.invGravityDir );
382  normal.Normalize();
383  dist = normal * v1;
384 
385  // check the faces from the second area
386  for ( j = 0; j < area2->numFaces; j++ ) {
387  floorFace2 = &file->faces[abs(file->faceIndex[area2->firstFace + j])];
388  // must be a ground face
389  if ( !(floorFace2->flags & FACE_FLOOR) ) {
390  continue;
391  }
392  // check the edges of this ground face
393  for ( l = 0; l < floorFace2->numEdges; l++ ) {
394  edge2Num = abs(file->edgeIndex[floorFace2->firstEdge + l]);
395  edge2 = &file->edges[edge2Num];
396  // vertices of the edge
397  v3 = file->vertices[edge2->vertexNum[0]];
398  v4 = file->vertices[edge2->vertexNum[1]];
399  // check the distance between the two points and the vertical plane through the edge of area1
400  diff = normal * v3 - dist;
401  if ( diff < -0.2f || diff > 0.2f ) {
402  continue;
403  }
404  diff = normal * v4 - dist;
405  if ( diff < -0.2f || diff > 0.2f ) {
406  continue;
407  }
408 
409  // project the two ground edges into the step side plane
410  // and calculate the shortest distance between the two
411  // edges if they overlap in the direction orthogonal to
412  // the gravity direction
413  orthogonal = file->settings.invGravityDir.Cross( normal );
415  orthogonalDot = orthogonal * orthogonal;
416  // projection into the step plane
417  // NOTE: since gravity is vertical this is just the z coordinate
418  y1 = v1[2];//(v1 * file->settings.invGravity) / invGravityDot;
419  y2 = v2[2];//(v2 * file->settings.invGravity) / invGravityDot;
420  y3 = v3[2];//(v3 * file->settings.invGravity) / invGravityDot;
421  y4 = v4[2];//(v4 * file->settings.invGravity) / invGravityDot;
422 
423  x1 = (v1 * orthogonal) / orthogonalDot;
424  x2 = (v2 * orthogonal) / orthogonalDot;
425  x3 = (v3 * orthogonal) / orthogonalDot;
426  x4 = (v4 * orthogonal) / orthogonalDot;
427 
428  if ( x1 > x2 ) {
429  tmp = x1; x1 = x2; x2 = tmp;
430  tmp = y1; y1 = y2; y2 = tmp;
431  tmpv = v1; v1 = v2; v2 = tmpv;
432  }
433  if ( x3 > x4 ) {
434  tmp = x3; x3 = x4; x4 = tmp;
435  tmp = y3; y3 = y4; y4 = tmp;
436  tmpv = v3; v3 = v4; v4 = tmpv;
437  }
438  // if the two projected edge lines have no overlap
439  if ( x2 <= x3 || x4 <= x1 ) {
440  continue;
441  }
442  // if the two lines fully overlap
443  if ( (x1 - 0.5f < x3 && x4 < x2 + 0.5f) && (x3 - 0.5f < x1 && x2 < x4 + 0.5f) ) {
444  dist1 = y3 - y1;
445  dist2 = y4 - y2;
446  p1area1 = v1;
447  p2area1 = v2;
448  p1area2 = v3;
449  p2area2 = v4;
450  }
451  else {
452  // if the points are equal
453  if ( x1 > x3 - 0.1f && x1 < x3 + 0.1f ) {
454  dist1 = y3 - y1;
455  p1area1 = v1;
456  p1area2 = v3;
457  }
458  else if ( x1 < x3 ) {
459  y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1);
460  dist1 = y3 - y;
461  p1area1 = v3;
462  p1area1[2] = y;
463  p1area2 = v3;
464  }
465  else {
466  y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3);
467  dist1 = y - y1;
468  p1area1 = v1;
469  p1area2 = v1;
470  p1area2[2] = y;
471  }
472  // if the points are equal
473  if ( x2 > x4 - 0.1f && x2 < x4 + 0.1f ) {
474  dist2 = y4 - y2;
475  p2area1 = v2;
476  p2area2 = v4;
477  }
478  else if ( x2 < x4 ) {
479  y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3);
480  dist2 = y - y2;
481  p2area1 = v2;
482  p2area2 = v2;
483  p2area2[2] = y;
484  }
485  else {
486  y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1);
487  dist2 = y4 - y;
488  p2area1 = v4;
489  p2area1[2] = y;
490  p2area2 = v4;
491  }
492  }
493 
494  // if both distances are pretty much equal then we take the middle of the points
495  if ( dist1 > dist2 - 1.0f && dist1 < dist2 + 1.0f ) {
496  dist = dist1;
497  start = ( p1area1 + p2area1 ) * 0.5f;
498  end = ( p1area2 + p2area2 ) * 0.5f;
499  }
500  else if (dist1 < dist2) {
501  dist = dist1;
502  start = p1area1;
503  end = p1area2;
504  }
505  else {
506  dist = dist2;
507  start = p2area1;
508  end = p2area2;
509  }
510 
511  // get the length of the overlapping part of the edges of the two areas
512  length = (p2area2 - p1area2).Length();
513 
514  if ( floorFace1->flags & FACE_FLOOR ) {
515  // if the vertical distance is smaller
516  if ( dist < floor_bestDist ||
517  // or the vertical distance is pretty much the same
518  // but the overlapping part of the edges is longer
519  (dist < floor_bestDist + 1.0f && length > floor_bestLength) ) {
520  floor_bestDist = dist;
521  floor_bestLength = length;
522  floor_foundReach = true;
523  floor_bestArea1FloorEdgeNum = edge1Num;
524  floor_bestArea2FloorEdgeNum = edge2Num;
525  floor_bestFace1 = floorFace1;
526  floor_bestStart = start;
527  floor_bestNormal = normal;
528  floor_bestEnd = end;
529  }
530  }
531  else {
532  // if the vertical distance is smaller
533  if ( dist < water_bestDist ||
534  //or the vertical distance is pretty much the same
535  //but the overlapping part of the edges is longer
536  (dist < water_bestDist + 1.0f && length > water_bestLength) ) {
537  water_bestDist = dist;
538  water_bestLength = length;
539  water_foundReach = true;
540  water_bestArea1FloorEdgeNum = edge1Num;
541  water_bestArea2FloorEdgeNum = edge2Num;
542  water_bestFace1 = floorFace1;
543  water_bestStart = start; // best start point in area1
544  water_bestNormal = normal; // normal is pointing into area2
545  water_bestEnd = end; // best point towards area2
546  }
547  }
548  }
549  }
550  }
551  }
552  //
553  // NOTE: swim reachabilities should already be filtered out
554  //
555  // Steps
556  //
557  // ---------
558  // | step height -> TFL_WALK
559  // --------|
560  //
561  // ---------
562  // ~~~~~~~~| step height and low water -> TFL_WALK
563  // --------|
564  //
565  // ~~~~~~~~~~~~~~~~~~
566  // ---------
567  // | step height and low water up to the step -> TFL_WALK
568  // --------|
569  //
570  // check for a step reachability
571  if ( floor_foundReach ) {
572  // if area2 is higher but lower than the maximum step height
573  // NOTE: floor_bestDist >= 0 also catches equal floor reachabilities
574  if ( floor_bestDist >= 0 && floor_bestDist < file->settings.maxStepHeight ) {
575  // create walk reachability from area1 to area2
576  walkReach = new idReachability_Walk();
577  walkReach->travelType = TFL_WALK;
578  walkReach->toAreaNum = area2num;
579  walkReach->fromAreaNum = area1num;
580  walkReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal;
581  walkReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal;
582  walkReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
583  walkReach->travelTime = 0;
584  if ( area2->flags & AREA_CROUCH ) {
585  walkReach->travelTime += file->settings.tt_startCrouching;
586  }
587  AddReachabilityToArea( walkReach, area1num );
588  return true;
589  }
590  }
591  //
592  // Water Jumps
593  //
594  // ---------
595  // |
596  // ~~~~~~~~|
597  // |
598  // | higher than step height and water up to waterjump height -> TFL_WATERJUMP
599  // --------|
600  //
601  // ~~~~~~~~~~~~~~~~~~
602  // ---------
603  // |
604  // |
605  // |
606  // | higher than step height and low water up to the step -> TFL_WATERJUMP
607  // --------|
608  //
609  // check for a waterjump reachability
610  if ( water_foundReach ) {
611  // get a test point a little bit towards area1
612  testPoint = water_bestEnd - INSIDEUNITS * water_bestNormal;
613  // go down the maximum waterjump height
614  testPoint[2] -= file->settings.maxWaterJumpHeight;
615  // if there IS water the sv_maxwaterjump height below the bestend point
616  if ( area1->flags & AREA_LIQUID ) {
617  // don't create rediculous water jump reachabilities from areas very far below the water surface
618  if ( water_bestDist < file->settings.maxWaterJumpHeight + 24 ) {
619  // water jumping from or towards a crouch only areas is not possible
620  if ( !(area1->flags & AREA_CROUCH) && !(area2->flags & AREA_CROUCH) ) {
621  // create water jump reachability from area1 to area2
622  waterJumpReach = new idReachability_WaterJump();
623  waterJumpReach->travelType = TFL_WATERJUMP;
624  waterJumpReach->toAreaNum = area2num;
625  waterJumpReach->fromAreaNum = area1num;
626  waterJumpReach->start = water_bestStart;
627  waterJumpReach->end = water_bestEnd + INSIDEUNITS_WATERJUMP * water_bestNormal;
628  waterJumpReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
629  waterJumpReach->travelTime = file->settings.tt_waterJump;
630  AddReachabilityToArea( waterJumpReach, area1num );
631  return true;
632  }
633  }
634  }
635  }
636  //
637  // Barrier Jumps
638  //
639  // ---------
640  // |
641  // |
642  // |
643  // | higher than max step height lower than max barrier height -> TFL_BARRIERJUMP
644  // --------|
645  //
646  // ---------
647  // |
648  // |
649  // |
650  // ~~~~~~~~| higher than max step height lower than max barrier height
651  // --------| and a thin layer of water in the area to jump from -> TFL_BARRIERJUMP
652  //
653  // check for a barrier jump reachability
654  if ( floor_foundReach ) {
655  //if area2 is higher but lower than the maximum barrier jump height
656  if ( floor_bestDist > 0 && floor_bestDist < file->settings.maxBarrierHeight ) {
657  //if no water in area1 or a very thin layer of water on the ground
658  if ( !water_foundReach || (floor_bestDist - water_bestDist < 16) ) {
659  // cannot perform a barrier jump towards or from a crouch area
660  if ( !(area1->flags & AREA_CROUCH) && !(area2->flags & AREA_CROUCH) ) {
661  // create barrier jump reachability from area1 to area2
662  barrierJumpReach = new idReachability_BarrierJump();
663  barrierJumpReach->travelType = TFL_BARRIERJUMP;
664  barrierJumpReach->toAreaNum = area2num;
665  barrierJumpReach->fromAreaNum = area1num;
666  barrierJumpReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal;
667  barrierJumpReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal;
668  barrierJumpReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
669  barrierJumpReach->travelTime = file->settings.tt_barrierJump;
670  AddReachabilityToArea( barrierJumpReach, area1num );
671  return true;
672  }
673  }
674  }
675  }
676  //
677  // Walk and Walk Off Ledge
678  //
679  // --------|
680  // | can walk or step back -> TFL_WALK
681  // ---------
682  //
683  // --------|
684  // |
685  // |
686  // |
687  // | cannot walk/step back -> TFL_WALKOFFLEDGE
688  // ---------
689  //
690  // --------|
691  // |
692  // |~~~~~~~~
693  // |
694  // | cannot step back but can waterjump back -> TFL_WALKOFFLEDGE
695  // --------- FIXME: create TFL_WALK reach??
696  //
697  // check for a walk or walk off ledge reachability
698  if ( floor_foundReach ) {
699  if ( floor_bestDist < 0 ) {
700  if ( floor_bestDist > -file->settings.maxStepHeight ) {
701  // create walk reachability from area1 to area2
702  walkReach = new idReachability_Walk();
703  walkReach->travelType = TFL_WALK;
704  walkReach->toAreaNum = area2num;
705  walkReach->fromAreaNum = area1num;
706  walkReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal;
707  walkReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal;
708  walkReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
709  walkReach->travelTime = 1;
710  AddReachabilityToArea( walkReach, area1num );
711  return true;
712  }
713  // if no maximum fall height set or less than the max
714  if ( !file->settings.maxFallHeight || idMath::Fabs(floor_bestDist) < file->settings.maxFallHeight ) {
715  // trace a bounding box vertically to check for solids
716  floor_bestEnd += INSIDEUNITS * floor_bestNormal;
717  start = floor_bestEnd;
718  start[2] = floor_bestStart[2];
719  end = floor_bestEnd;
720  end[2] += 4;
721  trace.areas = areas;
722  trace.maxAreas = sizeof(areas) / sizeof(int);
723  file->Trace( trace, start, end );
724  // if the trace didn't start in solid and nothing was hit
725  if ( trace.lastAreaNum && trace.fraction >= 1.0f ) {
726  // the trace end point must be in the goal area
727  if ( trace.lastAreaNum == area2num ) {
728  // don't create reachability if going through a cluster portal
729  for (i = 0; i < trace.numAreas; i++) {
730  if ( AreaIsClusterPortal( trace.areas[i] ) ) {
731  break;
732  }
733  }
734  if ( i >= trace.numAreas ) {
735  // create a walk off ledge reachability from area1 to area2
736  walkOffLedgeReach = new idReachability_WalkOffLedge();
737  walkOffLedgeReach->travelType = TFL_WALKOFFLEDGE;
738  walkOffLedgeReach->toAreaNum = area2num;
739  walkOffLedgeReach->fromAreaNum = area1num;
740  walkOffLedgeReach->start = floor_bestStart;
741  walkOffLedgeReach->end = floor_bestEnd;
742  walkOffLedgeReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
743  walkOffLedgeReach->travelTime = file->settings.tt_startWalkOffLedge + idMath::Fabs(floor_bestDist) * 50 / file->settings.gravityValue;
744  AddReachabilityToArea( walkOffLedgeReach, area1num );
745  return true;
746  }
747  }
748  }
749  }
750  }
751  }
752  return false;
753 }
754 
755 /*
756 ================
757 idAASReach::Reachability_WalkOffLedge
758 ================
759 */
761  int i, j, faceNum, edgeNum, side, reachAreaNum, p, areas[10];
762  aasArea_t *area;
763  aasFace_t *face;
764  aasEdge_t *edge;
765  idPlane *plane;
766  idVec3 v1, v2, mid, dir, testEnd;
768  aasTrace_t trace;
769 
770  if ( !AreaHasFloor( areaNum ) || CanSwimInArea( areaNum ) ) {
771  return;
772  }
773 
774  area = &file->areas[areaNum];
775 
776  for ( i = 0; i < area->numFaces; i++ ) {
777  faceNum = file->faceIndex[area->firstFace + i];
778  face = &file->faces[abs(faceNum)];
779 
780  // face must be a floor face
781  if ( !(face->flags & FACE_FLOOR) ) {
782  continue;
783  }
784 
785  for ( j = 0; j < face->numEdges; j++ ) {
786 
787  edgeNum = file->edgeIndex[face->firstEdge + j];
788  edge = &file->edges[abs(edgeNum)];
789 
790  //if ( !(edge->flags & EDGE_LEDGE) ) {
791  // continue;
792  //}
793 
794  side = edgeNum < 0;
795 
796  v1 = file->vertices[edge->vertexNum[side]];
797  v2 = file->vertices[edge->vertexNum[!side]];
798 
799  plane = &file->planeList[face->planeNum ^ INTSIGNBITSET(faceNum) ];
800 
801  // get the direction into the other area
802  dir = plane->Normal().Cross( v2 - v1 );
803  dir.Normalize();
804 
805  mid = ( v1 + v2 ) * 0.5f;
806  testEnd = mid + INSIDEUNITS_WALKEND * dir;
807  testEnd[2] -= file->settings.maxFallHeight + 1.0f;
808  trace.areas = areas;
809  trace.maxAreas = sizeof(areas) / sizeof(int);
810  file->Trace( trace, mid, testEnd );
811 
812  reachAreaNum = trace.lastAreaNum;
813  if ( !reachAreaNum || reachAreaNum == areaNum ) {
814  continue;
815  }
816  if ( idMath::Fabs( mid[2] - trace.endpos[2] ) > file->settings.maxFallHeight ) {
817  continue;
818  }
819  if ( !AreaHasFloor( reachAreaNum ) && !CanSwimInArea( reachAreaNum ) ) {
820  continue;
821  }
822  if ( ReachabilityExists( areaNum, reachAreaNum) ) {
823  continue;
824  }
825  // if not going through a cluster portal
826  for ( p = 0; p < trace.numAreas; p++ ) {
827  if ( AreaIsClusterPortal( trace.areas[p] ) ) {
828  break;
829  }
830  }
831  if ( p < trace.numAreas ) {
832  continue;
833  }
834 
835  reach = new idReachability_WalkOffLedge();
836  reach->travelType = TFL_WALKOFFLEDGE;
837  reach->toAreaNum = reachAreaNum;
838  reach->fromAreaNum = areaNum;
839  reach->start = mid;
840  reach->end = trace.endpos;
841  reach->edgeNum = abs( edgeNum );
843  AddReachabilityToArea( reach, areaNum );
844  }
845  }
846 }
847 
848 /*
849 ================
850 idAASReach::FlagReachableAreas
851 ================
852 */
854  int i, numReachableAreas;
855 
856  numReachableAreas = 0;
857  for ( i = 1; i < file->areas.Num(); i++ ) {
858 
859  if ( ( file->areas[i].flags & ( AREA_FLOOR | AREA_LADDER ) ) ||
860  ( file->areas[i].contents & AREACONTENTS_WATER ) ) {
861  file->areas[i].flags |= AREA_REACHABLE_WALK;
862  }
863  if ( file->GetSettings().allowFlyReachabilities ) {
864  file->areas[i].flags |= AREA_REACHABLE_FLY;
865  }
866  numReachableAreas++;
867  }
868 
869  common->Printf( "%6d reachable areas\n", numReachableAreas );
870 }
871 
872 /*
873 ================
874 idAASReach::Build
875 ================
876 */
877 bool idAASReach::Build( const idMapFile *mapFile, idAASFileLocal *file ) {
878  int i, j, lastPercent, percent;
879 
880  this->mapFile = mapFile;
881  this->file = file;
882  numReachabilities = 0;
883 
884  common->Printf( "[Reachability]\n" );
885 
886  // delete all existing reachabilities
887  file->DeleteReachabilities();
888 
889  FlagReachableAreas( file );
890 
891  for ( i = 1; i < file->areas.Num(); i++ ) {
892  if ( !( file->areas[i].flags & AREA_REACHABLE_WALK ) ) {
893  continue;
894  }
895  if ( file->GetSettings().allowSwimReachabilities ) {
896  Reachability_Swim( i );
897  }
899  }
900 
901  lastPercent = -1;
902  for ( i = 1; i < file->areas.Num(); i++ ) {
903 
904  if ( !( file->areas[i].flags & AREA_REACHABLE_WALK ) ) {
905  continue;
906  }
907 
908  for ( j = 0; j < file->areas.Num(); j++ ) {
909  if ( i == j ) {
910  continue;
911  }
912 
913  if ( !( file->areas[j].flags & AREA_REACHABLE_WALK ) ) {
914  continue;
915  }
916 
917  if ( ReachabilityExists( i, j ) ) {
918  continue;
919  }
921  continue;
922  }
923  }
924 
925  //Reachability_WalkOffLedge( i );
926 
927  percent = 100 * i / file->areas.Num();
928  if ( percent > lastPercent ) {
929  common->Printf( "\r%6d%%", percent );
930  lastPercent = percent;
931  }
932  }
933 
934  if ( file->GetSettings().allowFlyReachabilities ) {
935  for ( i = 1; i < file->areas.Num(); i++ ) {
936  Reachability_Fly( i );
937  }
938  }
939 
940  file->LinkReversedReachability();
941 
942  common->Printf( "\r%6d reachabilities\n", numReachabilities );
943 
944  return true;
945 }
unsigned short planeNum
Definition: AASFile.h:147
virtual idVec3 FaceCenter(int faceNum) const
#define INSIDEUNITS_WALKSTART
Definition: AASReach.cpp:38
float Normalize(void)
Definition: Vector.h:646
const idAASSettings & GetSettings(void) const
Definition: AASFile.h:312
const idVec3 & Normal(void) const
Definition: Plane.h:239
short toAreaNum
Definition: AASFile.h:96
idBounds bounds
Definition: AASFile.h:158
float maxStepHeight
Definition: AASFile.h:228
short fromAreaNum
Definition: AASFile.h:97
GLdouble GLdouble x2
Definition: qgl.h:415
#define INSIDEUNITS
Definition: AASReach.cpp:36
#define AREA_LIQUID
Definition: AASFile.h:72
bool allowSwimReachabilities
Definition: AASFile.h:220
bool Build(const idMapFile *mapFile, idAASFileLocal *file)
Definition: AASReach.cpp:877
GLenum GLint GLint y
Definition: glext.h:2849
GLenum GLsizei n
Definition: glext.h:3705
const idMapFile * mapFile
Definition: AASReach.h:46
int numReachabilities
Definition: AASReach.h:48
#define INSIDEUNITS_WATERJUMP
Definition: AASReach.cpp:41
int numAreas
Definition: AASFile.h:204
int tt_startCrouching
Definition: AASFile.h:235
int vertexNum[2]
Definition: AASFile.h:142
idReachability * reach
Definition: AASFile.h:165
Definition: Vector.h:316
int maxAreas
Definition: AASFile.h:196
idAASFileLocal * file
Definition: AASReach.h:47
int * areas
Definition: AASFile.h:205
idAASSettings settings
Definition: AASFile.h:348
#define INSIDEUNITS_SWIMEND
Definition: AASReach.cpp:39
#define AREA_REACHABLE_FLY
Definition: AASFile.h:75
idVec3 Cross(const idVec3 &a) const
Definition: Vector.h:619
float minFloorCos
Definition: AASFile.h:232
void Reachability_Swim(int areaNum)
Definition: AASReach.cpp:152
int i
Definition: process.py:33
float gravityValue
Definition: AASFile.h:227
idList< aasIndex_t > faceIndex
Definition: AASFile.h:342
int firstEdge
Definition: AASFile.h:150
idVec3 start
Definition: AASFile.h:98
list l
Definition: prepare.py:17
unsigned short flags
Definition: AASFile.h:160
idList< aasIndex_t > edgeIndex
Definition: AASFile.h:340
#define AREA_LADDER
Definition: AASFile.h:71
GLfloat GLfloat GLfloat v2
Definition: glext.h:3608
int travelType
Definition: AASFile.h:95
#define TFL_FLY
Definition: AASFile.h:55
unsigned short travelTime
Definition: AASFile.h:101
int tt_startWalkOffLedge
Definition: AASFile.h:237
bool AreaIsClusterPortal(int areaNum)
Definition: AASReach.cpp:85
idList< aasFace_t > faces
Definition: AASFile.h:341
idVec3 endpos
Definition: AASFile.h:200
GLuint GLuint end
Definition: glext.h:2845
static float Fabs(float f)
Definition: Math.h:779
#define INTSIGNBITNOTSET(i)
Definition: Math.h:72
idCommon * common
Definition: Common.cpp:206
idVec3 end
Definition: AASFile.h:99
void FlagReachableAreas(idAASFileLocal *file)
Definition: AASReach.cpp:853
bool allowFlyReachabilities
Definition: AASFile.h:221
float maxFallHeight
Definition: AASFile.h:231
#define AREA_CROUCH
Definition: AASFile.h:73
virtual idVec3 EdgeCenter(int edgeNum) const
#define TFL_WALK
Definition: AASFile.h:45
Definition: Plane.h:71
void LinkReversedReachability(void)
Definition: AASFile.cpp:941
idPlaneSet planeList
Definition: AASFile.h:337
idReachability * next
Definition: AASFile.h:104
int lastAreaNum
Definition: AASFile.h:202
#define INSIDEUNITS_WALKEND
Definition: AASReach.cpp:37
#define TFL_WATERJUMP
Definition: AASFile.h:52
bool Reachability_Step_Barrier_WaterJump_WalkOffLedge(int fromAreaNum, int toAreaNum)
Definition: AASReach.cpp:287
float fraction
Definition: AASFile.h:199
virtual void Printf(const char *fmt,...) id_attribute((format(printf
GLdouble GLdouble GLdouble y2
Definition: qgl.h:415
#define AREACONTENTS_WATER
Definition: AASFile.h:79
bool ReachabilityExists(int fromAreaNum, int toAreaNum)
Definition: AASReach.cpp:49
unsigned short flags
Definition: AASFile.h:148
float maxWaterJumpHeight
Definition: AASFile.h:230
GLfloat GLfloat v1
Definition: glext.h:3607
void Reachability_Fly(int areaNum)
Definition: AASReach.cpp:108
GLfloat GLfloat GLfloat GLfloat v3
Definition: glext.h:3609
idVec3 invGravityDir
Definition: AASFile.h:226
void Reachability_WalkOffLedge(int areaNum)
Definition: AASReach.cpp:760
idList< aasEdge_t > edges
Definition: AASFile.h:339
idList< aasVertex_t > vertices
Definition: AASFile.h:338
#define AREA_FLOOR
Definition: AASFile.h:68
int tt_waterJump
Definition: AASFile.h:236
#define INTSIGNBITSET(i)
Definition: Math.h:71
tuple f
Definition: idal.py:89
int numEdges
Definition: AASFile.h:149
#define AREA_REACHABLE_WALK
Definition: AASFile.h:74
virtual bool Trace(aasTrace_t &trace, const idVec3 &start, const idVec3 &end) const
#define TFL_WALKOFFLEDGE
Definition: AASFile.h:47
#define INSIDEUNITS_FLYEND
Definition: AASReach.cpp:40
int firstFace
Definition: AASFile.h:157
GLdouble y1
Definition: qgl.h:415
#define TFL_BARRIERJUMP
Definition: AASFile.h:48
idList< aasArea_t > areas
Definition: AASFile.h:343
int tt_barrierJump
Definition: AASFile.h:234
int numFaces
Definition: AASFile.h:156
GLsizei const GLcharARB const GLint * length
Definition: glext.h:3599
#define FACE_FLOOR
Definition: AASFile.h:63
bool CanSwimInArea(int areaNum)
Definition: AASReach.cpp:67
#define TFL_SWIM
Definition: AASFile.h:51
void Reachability_EqualFloorHeight(int areaNum)
Definition: AASReach.cpp:204
short areas[2]
Definition: AASFile.h:151
GLint j
Definition: qgl.h:264
void AddReachabilityToArea(idReachability *reach, int areaNum)
Definition: AASReach.cpp:94
GLfloat GLfloat p
Definition: glext.h:4674
bool AreaHasFloor(int areaNum)
Definition: AASReach.cpp:76
void DeleteReachabilities(void)
Definition: AASFile.cpp:1280
GLuint start
Definition: glext.h:2845
#define AREACONTENTS_CLUSTERPORTAL
Definition: AASFile.h:80