doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
AASCluster.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 "AASCluster.h"
35 
36 
37 /*
38 ================
39 idAASCluster::UpdatePortal
40 ================
41 */
42 bool idAASCluster::UpdatePortal( int areaNum, int clusterNum ) {
43  int portalNum;
44  aasPortal_t *portal;
45 
46  // find the portal for this area
47  for ( portalNum = 1; portalNum < file->portals.Num(); portalNum++ ) {
48  if ( file->portals[portalNum].areaNum == areaNum ) {
49  break;
50  }
51  }
52 
53  if ( portalNum >= file->portals.Num() ) {
54  common->Error( "no portal for area %d", areaNum );
55  return true;
56  }
57 
58  portal = &file->portals[portalNum];
59 
60  // if the portal is already fully updated
61  if ( portal->clusters[0] == clusterNum ) {
62  return true;
63  }
64  if ( portal->clusters[1] == clusterNum ) {
65  return true;
66  }
67  // if the portal has no front cluster yet
68  if ( !portal->clusters[0] ) {
69  portal->clusters[0] = clusterNum;
70  }
71  // if the portal has no back cluster yet
72  else if ( !portal->clusters[1] )
73  {
74  portal->clusters[1] = clusterNum;
75  }
76  else
77  {
78  // remove the cluster portal flag contents
79  file->areas[areaNum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
80  return false;
81  }
82 
83  // set the area cluster number to the negative portal number
84  file->areas[areaNum].cluster = -portalNum;
85 
86  // add the portal to the cluster using the portal index
87  file->portalIndex.Append( portalNum );
88  file->clusters[clusterNum].numPortals++;
89  return true;
90 }
91 
92 /*
93 ================
94 idAASCluster::FloodClusterAreas_r
95 ================
96 */
97 bool idAASCluster::FloodClusterAreas_r( int areaNum, int clusterNum ) {
98  aasArea_t *area;
99  aasFace_t *face;
100  int faceNum, i;
101  idReachability *reach;
102 
103  area = &file->areas[areaNum];
104 
105  // if the area is already part of a cluster
106  if ( area->cluster > 0 ) {
107  if ( area->cluster == clusterNum ) {
108  return true;
109  }
110  // there's a reachability going from one cluster to another only in one direction
111  common->Error( "cluster %d touched cluster %d at area %d\r\n", clusterNum, file->areas[areaNum].cluster, areaNum );
112  return false;
113  }
114 
115  // if this area is a cluster portal
116  if ( area->contents & AREACONTENTS_CLUSTERPORTAL ) {
117  return UpdatePortal( areaNum, clusterNum );
118  }
119 
120  // set the area cluster number
121  area->cluster = clusterNum;
122 
123  if ( !noFaceFlood ) {
124  // use area faces to flood into adjacent areas
125  for ( i = 0; i < area->numFaces; i++ ) {
126  faceNum = abs(file->faceIndex[area->firstFace + i]);
127  face = &file->faces[faceNum];
128  if ( face->areas[0] == areaNum ) {
129  if ( face->areas[1] ) {
130  if ( !FloodClusterAreas_r( face->areas[1], clusterNum ) ) {
131  return false;
132  }
133  }
134  }
135  else {
136  if ( face->areas[0] ) {
137  if ( !FloodClusterAreas_r( face->areas[0], clusterNum ) ) {
138  return false;
139  }
140  }
141  }
142  }
143  }
144 
145  // use the reachabilities to flood into other areas
146  for ( reach = file->areas[areaNum].reach; reach; reach = reach->next ) {
147  if ( !FloodClusterAreas_r( reach->toAreaNum, clusterNum) ) {
148  return false;
149  }
150  }
151 
152  // use the reversed reachabilities to flood into other areas
153  for ( reach = file->areas[areaNum].rev_reach; reach; reach = reach->rev_next ) {
154  if ( !FloodClusterAreas_r( reach->fromAreaNum, clusterNum) ) {
155  return false;
156  }
157  }
158 
159  return true;
160 }
161 
162 /*
163 ================
164 idAASCluster::RemoveAreaClusterNumbers
165 ================
166 */
168  int i;
169 
170  for ( i = 1; i < file->areas.Num(); i++ ) {
171  file->areas[i].cluster = 0;
172  }
173 }
174 
175 /*
176 ================
177 idAASCluster::NumberClusterAreas
178 ================
179 */
180 void idAASCluster::NumberClusterAreas( int clusterNum ) {
181  int i, portalNum;
182  aasCluster_t *cluster;
183  aasPortal_t *portal;
184 
185  cluster = &file->clusters[clusterNum];
186  cluster->numAreas = 0;
187  cluster->numReachableAreas = 0;
188 
189  // number all areas in this cluster WITH reachabilities
190  for ( i = 1; i < file->areas.Num(); i++ ) {
191 
192  if ( file->areas[i].cluster != clusterNum ) {
193  continue;
194  }
195 
196  if ( !(file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) {
197  continue;
198  }
199 
200  file->areas[i].clusterAreaNum = cluster->numAreas++;
201  cluster->numReachableAreas++;
202  }
203 
204  // number all portals in this cluster WITH reachabilities
205  for ( i = 0; i < cluster->numPortals; i++ ) {
206  portalNum = file->portalIndex[cluster->firstPortal + i];
207  portal = &file->portals[portalNum];
208 
209  if ( !(file->areas[portal->areaNum].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) {
210  continue;
211  }
212 
213  if ( portal->clusters[0] == clusterNum ) {
214  portal->clusterAreaNum[0] = cluster->numAreas++;
215  }
216  else {
217  portal->clusterAreaNum[1] = cluster->numAreas++;
218  }
219  cluster->numReachableAreas++;
220  }
221 
222  // number all areas in this cluster WITHOUT reachabilities
223  for ( i = 1; i < file->areas.Num(); i++ ) {
224 
225  if ( file->areas[i].cluster != clusterNum ) {
226  continue;
227  }
228 
229  if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
230  continue;
231  }
232 
233  file->areas[i].clusterAreaNum = cluster->numAreas++;
234  }
235 
236  // number all portals in this cluster WITHOUT reachabilities
237  for ( i = 0; i < cluster->numPortals; i++ ) {
238  portalNum = file->portalIndex[cluster->firstPortal + i];
239  portal = &file->portals[portalNum];
240 
241  if ( file->areas[portal->areaNum].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
242  continue;
243  }
244 
245  if ( portal->clusters[0] == clusterNum ) {
246  portal->clusterAreaNum[0] = cluster->numAreas++;
247  }
248  else {
249  portal->clusterAreaNum[1] = cluster->numAreas++;
250  }
251  }
252 }
253 
254 /*
255 ================
256 idAASCluster::FindClusters
257 ================
258 */
260  int i, clusterNum;
261  aasCluster_t cluster;
262 
264 
265  for ( i = 1; i < file->areas.Num(); i++ ) {
266  // if the area is already part of a cluster
267  if ( file->areas[i].cluster ) {
268  continue;
269  }
270 
271  // if not flooding through faces only use areas that have reachabilities
272  if ( noFaceFlood ) {
273  if ( !(file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) {
274  continue;
275  }
276  }
277 
278  // if the area is a cluster portal
279  if ( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
280  continue;
281  }
282 
283  cluster.numAreas = 0;
284  cluster.numReachableAreas = 0;
285  cluster.firstPortal = file->portalIndex.Num();
286  cluster.numPortals = 0;
287  clusterNum = file->clusters.Num();
288  file->clusters.Append( cluster );
289 
290  // flood the areas in this cluster
291  if ( !FloodClusterAreas_r( i, clusterNum ) ) {
292  return false;
293  }
294 
295  // number the cluster areas
296  NumberClusterAreas( clusterNum );
297  }
298  return true;
299 }
300 
301 /*
302 ================
303 idAASCluster::CreatePortals
304 ================
305 */
307  int i;
308  aasPortal_t portal;
309 
310  for ( i = 1; i < file->areas.Num(); i++ ) {
311  // if the area is a cluster portal
312  if ( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
313  portal.areaNum = i;
314  portal.clusters[0] = portal.clusters[1] = 0;
315  portal.maxAreaTravelTime = 0;
316  file->portals.Append( portal );
317  }
318  }
319 }
320 
321 /*
322 ================
323 idAASCluster::TestPortals
324 ================
325 */
327  int i;
328  aasPortal_t *portal, *portal2;
329  aasArea_t *area, *area2;
330  idReachability *reach;
331  bool ok;
332 
333  ok = true;
334  for ( i = 1; i < file->portals.Num(); i++ ) {
335  portal = &file->portals[i];
336  area = &file->areas[portal->areaNum];
337 
338  // if this portal was already removed
339  if ( !( area->contents & AREACONTENTS_CLUSTERPORTAL) ) {
340  continue;
341  }
342 
343  // may not removed this portal if it has a reachability to a removed portal
344  for ( reach = area->reach; reach; reach = reach->next ) {
345  area2 = &file->areas[ reach->toAreaNum ];
346  if ( area2->contents & AREACONTENTS_CLUSTERPORTAL ) {
347  continue;
348  }
349  if ( area2->cluster < 0 ) {
350  break;
351  }
352  }
353  if ( reach ) {
354  continue;
355  }
356 
357  // may not removed this portal if it has a reversed reachability to a removed portal
358  for ( reach = area->rev_reach; reach; reach = reach->rev_next ) {
359  area2 = &file->areas[ reach->toAreaNum ];
360  if ( area2->contents & AREACONTENTS_CLUSTERPORTAL ) {
361  continue;
362  }
363  if ( area2->cluster < 0 ) {
364  break;
365  }
366  }
367  if ( reach ) {
368  continue;
369  }
370 
371  // portal should have two clusters set
372  if ( !portal->clusters[0] ) {
374  ok = false;
375  continue;
376  }
377  if ( !portal->clusters[1] ) {
379  ok = false;
380  continue;
381  }
382 
383  // this portal may not have reachabilities to a portal that doesn't seperate the same clusters
384  for ( reach = area->reach; reach; reach = reach->next ) {
385  area2 = &file->areas[ reach->toAreaNum ];
386 
387  if ( !(area2->contents & AREACONTENTS_CLUSTERPORTAL) ) {
388  continue;
389  }
390 
391  if ( area2->cluster > 0 ) {
393  ok = false;
394  continue;
395  }
396 
397  portal2 = &file->portals[ -file->areas[ reach->toAreaNum ].cluster ];
398 
399  if ( ( portal2->clusters[0] != portal->clusters[0] && portal2->clusters[0] != portal->clusters[1] ) ||
400  ( portal2->clusters[1] != portal->clusters[0] && portal2->clusters[1] != portal->clusters[1] ) ) {
402  ok = false;
403  continue;
404  }
405  }
406  }
407 
408  return ok;
409 }
410 
411 /*
412 ================
413 idAASCluster::RemoveInvalidPortals
414 ================
415 */
417  int i, j, k, face1Num, face2Num, otherAreaNum, numOpenAreas, numInvalidPortals;
418  aasFace_t *face1, *face2;
419 
420  numInvalidPortals = 0;
421  for ( i = 0; i < file->areas.Num(); i++ ) {
422  if ( !( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) ) {
423  continue;
424  }
425 
426  numOpenAreas = 0;
427  for ( j = 0; j < file->areas[i].numFaces; j++ ) {
428  face1Num = file->faceIndex[ file->areas[i].firstFace + j ];
429  face1 = &file->faces[ abs(face1Num) ];
430  otherAreaNum = face1->areas[ face1Num < 0 ];
431 
432  if ( !otherAreaNum ) {
433  continue;
434  }
435 
436  for ( k = 0; k < j; k++ ) {
437  face2Num = file->faceIndex[ file->areas[i].firstFace + k ];
438  face2 = &file->faces[ abs(face2Num) ];
439  if ( otherAreaNum == face2->areas[ face2Num < 0 ] ) {
440  break;
441  }
442  }
443  if ( k < j ) {
444  continue;
445  }
446 
447  if ( !( file->areas[otherAreaNum].contents & AREACONTENTS_CLUSTERPORTAL ) ) {
448  numOpenAreas++;
449  }
450  }
451 
452  if ( numOpenAreas <= 1 ) {
453  file->areas[i].contents &= AREACONTENTS_CLUSTERPORTAL;
454  numInvalidPortals++;
455  }
456  }
457 
458  common->Printf( "\r%6d invalid portals removed\n", numInvalidPortals );
459 }
460 
461 /*
462 ================
463 idAASCluster::Build
464 ================
465 */
467 
468  common->Printf( "[Clustering]\n" );
469 
470  this->file = file;
471  this->noFaceFlood = true;
472 
474 
475  while( 1 ) {
476 
477  // delete all existing clusters
478  file->DeleteClusters();
479 
480  // create the portals from the portal areas
481  CreatePortals();
482 
483  common->Printf( "\r%6d", file->portals.Num() );
484 
485  // find the clusters
486  if ( !FindClusters() ) {
487  continue;
488  }
489 
490  // test the portals
491  if ( !TestPortals() ) {
492  continue;
493  }
494 
495  break;
496  }
497 
498  common->Printf( "\r%6d portals\n", file->portals.Num() );
499  common->Printf( "%6d clusters\n", file->clusters.Num() );
500 
501  for ( int i = 0; i < file->clusters.Num(); i++ ) {
502  common->Printf( "%6d reachable areas in cluster %d\n", file->clusters[i].numReachableAreas, i );
503  }
504 
505  file->ReportRoutingEfficiency();
506 
507  return true;
508 }
509 
510 /*
511 ================
512 idAASCluster::BuildSingleCluster
513 ================
514 */
516  int i, numAreas;
517  aasCluster_t cluster;
518 
519  common->Printf( "[Clustering]\n" );
520 
521  this->file = file;
522 
523  // delete all existing clusters
524  file->DeleteClusters();
525 
526  cluster.firstPortal = 0;
527  cluster.numPortals = 0;
528  cluster.numAreas = file->areas.Num();
529  cluster.numReachableAreas = 0;
530  // give all reachable areas in the cluster a number
531  for ( i = 0; i < file->areas.Num(); i++ ) {
532  file->areas[i].cluster = file->clusters.Num();
533  if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
534  file->areas[i].clusterAreaNum = cluster.numReachableAreas++;
535  }
536  }
537  // give the remaining areas a number within the cluster
538  numAreas = cluster.numReachableAreas;
539  for ( i = 0; i < file->areas.Num(); i++ ) {
540  if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
541  continue;
542  }
543  file->areas[i].clusterAreaNum = numAreas++;
544  }
545  file->clusters.Append( cluster );
546 
547  common->Printf( "%6d portals\n", file->portals.Num() );
548  common->Printf( "%6d clusters\n", file->clusters.Num() );
549 
550  for ( i = 0; i < file->clusters.Num(); i++ ) {
551  common->Printf( "%6d reachable areas in cluster %d\n", file->clusters[i].numReachableAreas, i );
552  }
553 
554  file->ReportRoutingEfficiency();
555 
556  return true;
557 }
unsigned short contents
Definition: AASFile.h:161
unsigned short maxAreaTravelTime
Definition: AASFile.h:180
bool noFaceFlood
Definition: AASCluster.h:48
short toAreaNum
Definition: AASFile.h:96
idReachability * rev_next
Definition: AASFile.h:105
void RemoveAreaClusterNumbers(void)
Definition: AASCluster.cpp:167
short fromAreaNum
Definition: AASFile.h:97
bool TestPortals(void)
Definition: AASCluster.cpp:326
idList< aasCluster_t > clusters
Definition: AASFile.h:347
void CreatePortals(void)
Definition: AASCluster.cpp:306
idReachability * reach
Definition: AASFile.h:165
short areaNum
Definition: AASFile.h:177
#define AREA_REACHABLE_FLY
Definition: AASFile.h:75
idAASFileLocal * file
Definition: AASCluster.h:47
short cluster
Definition: AASFile.h:162
int i
Definition: process.py:33
idList< aasIndex_t > faceIndex
Definition: AASFile.h:342
short clusters[2]
Definition: AASFile.h:178
bool FloodClusterAreas_r(int areaNum, int clusterNum)
Definition: AASCluster.cpp:97
int numPortals
Definition: AASFile.h:187
idList< aasFace_t > faces
Definition: AASFile.h:341
idCommon * common
Definition: Common.cpp:206
idList< aasPortal_t > portals
Definition: AASFile.h:345
idReachability * next
Definition: AASFile.h:104
int firstPortal
Definition: AASFile.h:188
bool BuildSingleCluster(idAASFileLocal *file)
Definition: AASCluster.cpp:515
void DeleteClusters(void)
Definition: AASFile.cpp:1299
virtual void Printf(const char *fmt,...) id_attribute((format(printf
bool Build(idAASFileLocal *file)
Definition: AASCluster.cpp:466
void RemoveInvalidPortals(void)
Definition: AASCluster.cpp:416
int Append(const type &obj)
Definition: List.h:646
idReachability * rev_reach
Definition: AASFile.h:166
short clusterAreaNum[2]
Definition: AASFile.h:179
int Num(void) const
Definition: List.h:265
#define AREA_REACHABLE_WALK
Definition: AASFile.h:74
int firstFace
Definition: AASFile.h:157
idList< aasArea_t > areas
Definition: AASFile.h:343
int numFaces
Definition: AASFile.h:156
idList< aasIndex_t > portalIndex
Definition: AASFile.h:346
bool UpdatePortal(int areaNum, int clusterNum)
Definition: AASCluster.cpp:42
short areas[2]
Definition: AASFile.h:151
int numReachableAreas
Definition: AASFile.h:186
void ReportRoutingEfficiency(void) const
Definition: AASFile.cpp:1258
bool FindClusters(void)
Definition: AASCluster.cpp:259
void NumberClusterAreas(int clusterNum)
Definition: AASCluster.cpp:180
GLint j
Definition: qgl.h:264
virtual void Error(const char *fmt,...) id_attribute((format(printf
int numAreas
Definition: AASFile.h:185
#define AREACONTENTS_CLUSTERPORTAL
Definition: AASFile.h:80