1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (RTCW SP Source Code).
8
9 RTCW SP 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 RTCW SP 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 RTCW SP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW SP 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 RTCW SP 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
30 /*****************************************************************************
31 * name: be_aas_cluster.c
32 *
33 * desc: area clustering
34 *
35 *
36 *****************************************************************************/
37
38 #include "../qcommon/q_shared.h"
39 #include "l_memory.h"
40 #include "l_script.h"
41 #include "l_precomp.h"
42 #include "l_struct.h"
43 #include "l_log.h"
44 #include "l_memory.h"
45 #include "l_libvar.h"
46 #include "aasfile.h"
47 #include "botlib.h"
48 #include "be_aas.h"
49 #include "be_aas_funcs.h"
50 #include "be_aas_def.h"
51 #include "be_aas_cluster.h"
52
53 extern botlib_import_t botimport;
54
55 #define AAS_MAX_PORTALS 65536
56 #define AAS_MAX_PORTALINDEXSIZE 65536
57 #define AAS_MAX_CLUSTERS 65536
58 //
59 #define MAX_PORTALAREAS 1024
60
61 // do not flood through area faces, only use reachabilities
62 int nofaceflood = qtrue;
63
64 //===========================================================================
65 //
66 // Parameter: -
67 // Returns: -
68 // Changes Globals: -
69 //===========================================================================
AAS_RemoveClusterAreas(void)70 void AAS_RemoveClusterAreas( void ) {
71 int i;
72
73 for ( i = 1; i < ( *aasworld ).numareas; i++ )
74 {
75 ( *aasworld ).areasettings[i].cluster = 0;
76 } //end for
77 } //end of the function AAS_RemoveClusterAreas
78 //===========================================================================
79 //
80 // Parameter: -
81 // Returns: -
82 // Changes Globals: -
83 //===========================================================================
AAS_ClearCluster(int clusternum)84 void AAS_ClearCluster( int clusternum ) {
85 int i;
86
87 for ( i = 1; i < ( *aasworld ).numareas; i++ )
88 {
89 if ( ( *aasworld ).areasettings[i].cluster == clusternum ) {
90 ( *aasworld ).areasettings[i].cluster = 0;
91 } //end if
92 } //end for
93 } //end of the function AAS_ClearCluster
94 //===========================================================================
95 //
96 // Parameter: -
97 // Returns: -
98 // Changes Globals: -
99 //===========================================================================
AAS_RemovePortalsClusterReference(int clusternum)100 void AAS_RemovePortalsClusterReference( int clusternum ) {
101 int portalnum;
102
103 for ( portalnum = 1; portalnum < ( *aasworld ).numportals; portalnum++ )
104 {
105 if ( ( *aasworld ).portals[portalnum].frontcluster == clusternum ) {
106 ( *aasworld ).portals[portalnum].frontcluster = 0;
107 } //end if
108 if ( ( *aasworld ).portals[portalnum].backcluster == clusternum ) {
109 ( *aasworld ).portals[portalnum].backcluster = 0;
110 } //end if
111 } //end for
112 } //end of the function AAS_RemovePortalsClusterReference
113 //===========================================================================
114 //
115 // Parameter: -
116 // Returns: -
117 // Changes Globals: -
118 //===========================================================================
AAS_UpdatePortal(int areanum,int clusternum)119 int AAS_UpdatePortal( int areanum, int clusternum ) {
120 int portalnum;
121 aas_portal_t *portal;
122 aas_cluster_t *cluster;
123
124 //find the portal of the area
125 for ( portalnum = 1; portalnum < ( *aasworld ).numportals; portalnum++ )
126 {
127 if ( ( *aasworld ).portals[portalnum].areanum == areanum ) {
128 break;
129 }
130 } //end for
131 //
132 if ( portalnum == ( *aasworld ).numportals ) {
133 AAS_Error( "no portal of area %d\n", areanum );
134 return qtrue;
135 } //end if
136 //
137 portal = &( *aasworld ).portals[portalnum];
138 //if the portal is already fully updated
139 if ( portal->frontcluster == clusternum ) {
140 return qtrue;
141 }
142 if ( portal->backcluster == clusternum ) {
143 return qtrue;
144 }
145 //if the portal has no front cluster yet
146 if ( !portal->frontcluster ) {
147 portal->frontcluster = clusternum;
148 } //end if
149 //if the portal has no back cluster yet
150 else if ( !portal->backcluster ) {
151 portal->backcluster = clusternum;
152 } //end else if
153 else
154 {
155 Log_Write( "portal using area %d is separating more than two clusters\r\n", areanum );
156 //remove the cluster portal flag contents
157 ( *aasworld ).areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
158 return qfalse;
159 } //end else
160 if ( ( *aasworld ).portalindexsize >= AAS_MAX_PORTALINDEXSIZE ) {
161 AAS_Error( "AAS_MAX_PORTALINDEXSIZE\n" );
162 return qtrue;
163 } //end if
164 //set the area cluster number to the negative portal number
165 ( *aasworld ).areasettings[areanum].cluster = -portalnum;
166 //add the portal to the cluster using the portal index
167 cluster = &( *aasworld ).clusters[clusternum];
168 ( *aasworld ).portalindex[cluster->firstportal + cluster->numportals] = portalnum;
169 ( *aasworld ).portalindexsize++;
170 cluster->numportals++;
171 return qtrue;
172 } //end of the function AAS_UpdatePortal
173 //===========================================================================
174 //
175 // Parameter: -
176 // Returns: -
177 // Changes Globals: -
178 //===========================================================================
AAS_FloodClusterAreas_r(int areanum,int clusternum)179 int AAS_FloodClusterAreas_r( int areanum, int clusternum ) {
180 aas_area_t *area;
181 aas_face_t *face;
182 int facenum, i;
183
184 //
185 if ( areanum <= 0 || areanum >= ( *aasworld ).numareas ) {
186 AAS_Error( "AAS_FloodClusterAreas_r: areanum out of range\n" );
187 return qfalse;
188 } //end if
189 //if the area is already part of a cluster
190 if ( ( *aasworld ).areasettings[areanum].cluster > 0 ) {
191 if ( ( *aasworld ).areasettings[areanum].cluster == clusternum ) {
192 return qtrue;
193 }
194 //
195 //there's a reachability going from one cluster to another only in one direction
196 //
197 AAS_Error( "cluster %d touched cluster %d at area %d\n",
198 clusternum, ( *aasworld ).areasettings[areanum].cluster, areanum );
199 return qfalse;
200 } //end if
201 //don't add the cluster portal areas to the clusters
202 if ( ( *aasworld ).areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
203 return AAS_UpdatePortal( areanum, clusternum );
204 } //end if
205 //set the area cluster number
206 ( *aasworld ).areasettings[areanum].cluster = clusternum;
207 ( *aasworld ).areasettings[areanum].clusterareanum =
208 ( *aasworld ).clusters[clusternum].numareas;
209 //the cluster has an extra area
210 ( *aasworld ).clusters[clusternum].numareas++;
211
212 area = &( *aasworld ).areas[areanum];
213 //use area faces to flood into adjacent areas
214 if ( !nofaceflood ) {
215 for ( i = 0; i < area->numfaces; i++ )
216 {
217 facenum = abs( ( *aasworld ).faceindex[area->firstface + i] );
218 face = &( *aasworld ).faces[facenum];
219 if ( face->frontarea == areanum ) {
220 if ( face->backarea ) {
221 if ( !AAS_FloodClusterAreas_r( face->backarea, clusternum ) ) {
222 return qfalse;
223 }
224 }
225 } //end if
226 else
227 {
228 if ( face->frontarea ) {
229 if ( !AAS_FloodClusterAreas_r( face->frontarea, clusternum ) ) {
230 return qfalse;
231 }
232 }
233 } //end else
234 } //end for
235 }
236 //use the reachabilities to flood into other areas
237 for ( i = 0; i < ( *aasworld ).areasettings[areanum].numreachableareas; i++ )
238 {
239 if ( !( *aasworld ).reachability[
240 ( *aasworld ).areasettings[areanum].firstreachablearea + i].areanum ) {
241 continue;
242 } //end if
243 if ( !AAS_FloodClusterAreas_r( ( *aasworld ).reachability[
244 ( *aasworld ).areasettings[areanum].firstreachablearea + i].areanum, clusternum ) ) {
245 return qfalse;
246 }
247 } //end for
248 return qtrue;
249 } //end of the function AAS_FloodClusterAreas_r
250 //===========================================================================
251 // try to flood from all areas without cluster into areas with a cluster set
252 //
253 // Parameter: -
254 // Returns: -
255 // Changes Globals: -
256 //===========================================================================
AAS_FloodClusterAreasUsingReachabilities(int clusternum)257 int AAS_FloodClusterAreasUsingReachabilities( int clusternum ) {
258 int i, j, areanum;
259
260 for ( i = 1; i < ( *aasworld ).numareas; i++ )
261 {
262 //if this area already has a cluster set
263 if ( ( *aasworld ).areasettings[i].cluster ) {
264 continue;
265 }
266 //if this area is a cluster portal
267 if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
268 continue;
269 }
270 //loop over the reachable areas from this area
271 for ( j = 0; j < ( *aasworld ).areasettings[i].numreachableareas; j++ )
272 {
273 //the reachable area
274 areanum = ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].areanum;
275 //if this area is a cluster portal
276 if ( ( *aasworld ).areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
277 continue;
278 }
279 //if this area has a cluster set
280 if ( ( *aasworld ).areasettings[areanum].cluster ) {
281 if ( !AAS_FloodClusterAreas_r( i, clusternum ) ) {
282 return qfalse;
283 }
284 i = 0;
285 break;
286 } //end if
287 } //end for
288 } //end for
289 return qtrue;
290 } //end of the function AAS_FloodClusterAreasUsingReachabilities
291 //===========================================================================
292 //
293 // Parameter: -
294 // Returns: -
295 // Changes Globals: -
296 //===========================================================================
AAS_NumberClusterPortals(int clusternum)297 void AAS_NumberClusterPortals( int clusternum ) {
298 int i, portalnum;
299 aas_cluster_t *cluster;
300 aas_portal_t *portal;
301
302 cluster = &( *aasworld ).clusters[clusternum];
303 for ( i = 0; i < cluster->numportals; i++ )
304 {
305 portalnum = ( *aasworld ).portalindex[cluster->firstportal + i];
306 portal = &( *aasworld ).portals[portalnum];
307 if ( portal->frontcluster == clusternum ) {
308 portal->clusterareanum[0] = cluster->numareas++;
309 } //end if
310 else
311 {
312 portal->clusterareanum[1] = cluster->numareas++;
313 } //end else
314 } //end for
315 } //end of the function AAS_NumberClusterPortals
316 //===========================================================================
317 //
318 // Parameter: -
319 // Returns: -
320 // Changes Globals: -
321 //===========================================================================
AAS_NumberClusterAreas(int clusternum)322 void AAS_NumberClusterAreas( int clusternum ) {
323 int i, portalnum;
324 aas_cluster_t *cluster;
325 aas_portal_t *portal;
326
327 ( *aasworld ).clusters[clusternum].numareas = 0;
328 ( *aasworld ).clusters[clusternum].numreachabilityareas = 0;
329 //number all areas in this cluster WITH reachabilities
330 for ( i = 1; i < ( *aasworld ).numareas; i++ )
331 {
332 //
333 if ( ( *aasworld ).areasettings[i].cluster != clusternum ) {
334 continue;
335 }
336 //
337 if ( !AAS_AreaReachability( i ) ) {
338 continue;
339 }
340 //
341 ( *aasworld ).areasettings[i].clusterareanum = ( *aasworld ).clusters[clusternum].numareas;
342 //the cluster has an extra area
343 ( *aasworld ).clusters[clusternum].numareas++;
344 ( *aasworld ).clusters[clusternum].numreachabilityareas++;
345 } //end for
346 //number all portals in this cluster WITH reachabilities
347 cluster = &( *aasworld ).clusters[clusternum];
348 for ( i = 0; i < cluster->numportals; i++ )
349 {
350 portalnum = ( *aasworld ).portalindex[cluster->firstportal + i];
351 portal = &( *aasworld ).portals[portalnum];
352 if ( !AAS_AreaReachability( portal->areanum ) ) {
353 continue;
354 }
355 if ( portal->frontcluster == clusternum ) {
356 portal->clusterareanum[0] = cluster->numareas++;
357 ( *aasworld ).clusters[clusternum].numreachabilityareas++;
358 } //end if
359 else
360 {
361 portal->clusterareanum[1] = cluster->numareas++;
362 ( *aasworld ).clusters[clusternum].numreachabilityareas++;
363 } //end else
364 } //end for
365 //number all areas in this cluster WITHOUT reachabilities
366 for ( i = 1; i < ( *aasworld ).numareas; i++ )
367 {
368 //
369 if ( ( *aasworld ).areasettings[i].cluster != clusternum ) {
370 continue;
371 }
372 //
373 if ( AAS_AreaReachability( i ) ) {
374 continue;
375 }
376 //
377 ( *aasworld ).areasettings[i].clusterareanum = ( *aasworld ).clusters[clusternum].numareas;
378 //the cluster has an extra area
379 ( *aasworld ).clusters[clusternum].numareas++;
380 } //end for
381 //number all portals in this cluster WITHOUT reachabilities
382 cluster = &( *aasworld ).clusters[clusternum];
383 for ( i = 0; i < cluster->numportals; i++ )
384 {
385 portalnum = ( *aasworld ).portalindex[cluster->firstportal + i];
386 portal = &( *aasworld ).portals[portalnum];
387 if ( AAS_AreaReachability( portal->areanum ) ) {
388 continue;
389 }
390 if ( portal->frontcluster == clusternum ) {
391 portal->clusterareanum[0] = cluster->numareas++;
392 } //end if
393 else
394 {
395 portal->clusterareanum[1] = cluster->numareas++;
396 } //end else
397 } //end for
398 } //end of the function AAS_NumberClusterAreas
399 //===========================================================================
400 //
401 // Parameter: -
402 // Returns: -
403 // Changes Globals: -
404 //===========================================================================
AAS_FindClusters(void)405 int AAS_FindClusters( void ) {
406 int i;
407 aas_cluster_t *cluster;
408
409 AAS_RemoveClusterAreas();
410 //
411 for ( i = 1; i < ( *aasworld ).numareas; i++ )
412 {
413 //if the area is already part of a cluster
414 if ( ( *aasworld ).areasettings[i].cluster ) {
415 continue;
416 }
417 // if not flooding through faces only use areas that have reachabilities
418 if ( nofaceflood ) {
419 if ( !( *aasworld ).areasettings[i].numreachableareas ) {
420 continue;
421 }
422 } //end if
423 //if the area is a cluster portal
424 if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
425 continue;
426 }
427 if ( ( *aasworld ).numclusters >= AAS_MAX_CLUSTERS ) {
428 AAS_Error( "AAS_MAX_CLUSTERS\n" );
429 return qfalse;
430 } //end if
431 cluster = &( *aasworld ).clusters[( *aasworld ).numclusters];
432 cluster->numareas = 0;
433 cluster->numreachabilityareas = 0;
434 cluster->firstportal = ( *aasworld ).portalindexsize;
435 cluster->numportals = 0;
436 //flood the areas in this cluster
437 if ( !AAS_FloodClusterAreas_r( i, ( *aasworld ).numclusters ) ) {
438 return qfalse;
439 }
440 if ( !AAS_FloodClusterAreasUsingReachabilities( ( *aasworld ).numclusters ) ) {
441 return qfalse;
442 }
443 //number the cluster areas
444 //AAS_NumberClusterPortals((*aasworld).numclusters);
445 AAS_NumberClusterAreas( ( *aasworld ).numclusters );
446 //Log_Write("cluster %d has %d areas\r\n", (*aasworld).numclusters, cluster->numareas);
447 ( *aasworld ).numclusters++;
448 } //end for
449 return qtrue;
450 } //end of the function AAS_FindClusters
451 //===========================================================================
452 //
453 // Parameter: -
454 // Returns: -
455 // Changes Globals: -
456 //===========================================================================
AAS_CreatePortals(void)457 void AAS_CreatePortals( void ) {
458 int i;
459 aas_portal_t *portal;
460
461 for ( i = 1; i < ( *aasworld ).numareas; i++ )
462 {
463 //if the area is a cluster portal
464 if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
465 if ( ( *aasworld ).numportals >= AAS_MAX_PORTALS ) {
466 AAS_Error( "AAS_MAX_PORTALS\n" );
467 return;
468 } //end if
469 portal = &( *aasworld ).portals[( *aasworld ).numportals];
470 portal->areanum = i;
471 portal->frontcluster = 0;
472 portal->backcluster = 0;
473 Log_Write( "portal %d: area %d\r\n", ( *aasworld ).numportals, portal->areanum );
474 ( *aasworld ).numportals++;
475 } //end if
476 } //end for
477 } //end of the function AAS_CreatePortals
478 /*
479 //===========================================================================
480 //
481 // Parameter: -
482 // Returns: -
483 // Changes Globals: -
484 //===========================================================================
485 int AAS_MapContainsTeleporters(void)
486 {
487 bsp_entity_t *entities, *ent;
488 char *classname;
489
490 entities = AAS_ParseBSPEntities();
491
492 for (ent = entities; ent; ent = ent->next)
493 {
494 classname = AAS_ValueForBSPEpairKey(ent, "classname");
495 if (classname && !strcmp(classname, "misc_teleporter"))
496 {
497 AAS_FreeBSPEntities(entities);
498 return qtrue;
499 } //end if
500 } //end for
501 return qfalse;
502 } //end of the function AAS_MapContainsTeleporters
503 //===========================================================================
504 //
505 // Parameter: -
506 // Returns: -
507 // Changes Globals: -
508 //===========================================================================
509 int AAS_NonConvexFaces(aas_face_t *face1, aas_face_t *face2, int side1, int side2)
510 {
511 int i, j, edgenum;
512 aas_plane_t *plane1, *plane2;
513 aas_edge_t *edge;
514
515
516 plane1 = &(*aasworld).planes[face1->planenum ^ side1];
517 plane2 = &(*aasworld).planes[face2->planenum ^ side2];
518
519 //check if one of the points of face1 is at the back of the plane of face2
520 for (i = 0; i < face1->numedges; i++)
521 {
522 edgenum = abs((*aasworld).edgeindex[face1->firstedge + i]);
523 edge = &(*aasworld).edges[edgenum];
524 for (j = 0; j < 2; j++)
525 {
526 if (DotProduct(plane2->normal, (*aasworld).vertexes[edge->v[j]]) -
527 plane2->dist < -0.01) return qtrue;
528 } //end for
529 } //end for
530 for (i = 0; i < face2->numedges; i++)
531 {
532 edgenum = abs((*aasworld).edgeindex[face2->firstedge + i]);
533 edge = &(*aasworld).edges[edgenum];
534 for (j = 0; j < 2; j++)
535 {
536 if (DotProduct(plane1->normal, (*aasworld).vertexes[edge->v[j]]) -
537 plane1->dist < -0.01) return qtrue;
538 } //end for
539 } //end for
540
541 return qfalse;
542 } //end of the function AAS_NonConvexFaces
543 //===========================================================================
544 //
545 // Parameter: -
546 // Returns: -
547 // Changes Globals: -
548 //===========================================================================
549 qboolean AAS_CanMergeAreas(int *areanums, int numareas)
550 {
551 int i, j, s, face1num, face2num, side1, side2, fn1, fn2;
552 aas_face_t *face1, *face2;
553 aas_area_t *area1, *area2;
554
555 for (i = 0; i < numareas; i++)
556 {
557 area1 = &(*aasworld).areas[areanums[i]];
558 for (fn1 = 0; fn1 < area1->numfaces; fn1++)
559 {
560 face1num = abs((*aasworld).faceindex[area1->firstface + fn1]);
561 face1 = &(*aasworld).faces[face1num];
562 side1 = face1->frontarea != areanums[i];
563 //check if the face isn't a shared one with one of the other areas
564 for (s = 0; s < numareas; s++)
565 {
566 if (s == i) continue;
567 if (face1->frontarea == s || face1->backarea == s) break;
568 } //end for
569 //if the face was a shared one
570 if (s != numareas) continue;
571 //
572 for (j = 0; j < numareas; j++)
573 {
574 if (j == i) continue;
575 area2 = &(*aasworld).areas[areanums[j]];
576 for (fn2 = 0; fn2 < area2->numfaces; fn2++)
577 {
578 face2num = abs((*aasworld).faceindex[area2->firstface + fn2]);
579 face2 = &(*aasworld).faces[face2num];
580 side2 = face2->frontarea != areanums[j];
581 //check if the face isn't a shared one with one of the other areas
582 for (s = 0; s < numareas; s++)
583 {
584 if (s == j) continue;
585 if (face2->frontarea == s || face2->backarea == s) break;
586 } //end for
587 //if the face was a shared one
588 if (s != numareas) continue;
589 //
590 if (AAS_NonConvexFaces(face1, face2, side1, side2)) return qfalse;
591 } //end for
592 } //end for
593 } //end for
594 } //end for
595 return qtrue;
596 } //end of the function AAS_CanMergeAreas
597 //===========================================================================
598 //
599 // Parameter: -
600 // Returns: -
601 // Changes Globals: -
602 //===========================================================================
603 qboolean AAS_NonConvexEdges(aas_edge_t *edge1, aas_edge_t *edge2, int side1, int side2, int planenum)
604 {
605 int i;
606 vec3_t edgevec1, edgevec2, normal1, normal2;
607 float dist1, dist2;
608 aas_plane_t *plane;
609
610 plane = &(*aasworld).planes[planenum];
611 VectorSubtract((*aasworld).vertexes[edge1->v[1]], (*aasworld).vertexes[edge1->v[0]], edgevec1);
612 VectorSubtract((*aasworld).vertexes[edge2->v[1]], (*aasworld).vertexes[edge2->v[0]], edgevec2);
613 if (side1) VectorInverse(edgevec1);
614 if (side2) VectorInverse(edgevec2);
615 //
616 CrossProduct(edgevec1, plane->normal, normal1);
617 dist1 = DotProduct(normal1, (*aasworld).vertexes[edge1->v[0]]);
618 CrossProduct(edgevec2, plane->normal, normal2);
619 dist2 = DotProduct(normal2, (*aasworld).vertexes[edge2->v[0]]);
620
621 for (i = 0; i < 2; i++)
622 {
623 if (DotProduct((*aasworld).vertexes[edge1->v[i]], normal2) - dist2 < -0.01) return qfalse;
624 } //end for
625 for (i = 0; i < 2; i++)
626 {
627 if (DotProduct((*aasworld).vertexes[edge2->v[i]], normal1) - dist1 < -0.01) return qfalse;
628 } //end for
629 return qtrue;
630 } //end of the function AAS_NonConvexEdges
631 //===========================================================================
632 //
633 // Parameter: -
634 // Returns: -
635 // Changes Globals: -
636 //===========================================================================
637 qboolean AAS_CanMergeFaces(int *facenums, int numfaces, int planenum)
638 {
639 int i, j, s, edgenum1, edgenum2, side1, side2, en1, en2, ens;
640 aas_face_t *face1, *face2, *otherface;
641 aas_edge_t *edge1, *edge2;
642
643 for (i = 0; i < numfaces; i++)
644 {
645 face1 = &(*aasworld).faces[facenums[i]];
646 for (en1 = 0; en1 < face1->numedges; en1++)
647 {
648 edgenum1 = (*aasworld).edgeindex[face1->firstedge + en1];
649 side1 = (edgenum1 < 0) ^ (face1->planenum != planenum);
650 edgenum1 = abs(edgenum1);
651 edge1 = &(*aasworld).edges[edgenum1];
652 //check if the edge is shared with another face
653 for (s = 0; s < numfaces; s++)
654 {
655 if (s == i) continue;
656 otherface = &(*aasworld).faces[facenums[s]];
657 for (ens = 0; ens < otherface->numedges; ens++)
658 {
659 if (edgenum1 == abs((*aasworld).edgeindex[otherface->firstedge + ens])) break;
660 } //end for
661 if (ens != otherface->numedges) break;
662 } //end for
663 //if the edge was shared
664 if (s != numfaces) continue;
665 //
666 for (j = 0; j < numfaces; j++)
667 {
668 if (j == i) continue;
669 face2 = &(*aasworld).faces[facenums[j]];
670 for (en2 = 0; en2 < face2->numedges; en2++)
671 {
672 edgenum2 = (*aasworld).edgeindex[face2->firstedge + en2];
673 side2 = (edgenum2 < 0) ^ (face2->planenum != planenum);
674 edgenum2 = abs(edgenum2);
675 edge2 = &(*aasworld).edges[edgenum2];
676 //check if the edge is shared with another face
677 for (s = 0; s < numfaces; s++)
678 {
679 if (s == i) continue;
680 otherface = &(*aasworld).faces[facenums[s]];
681 for (ens = 0; ens < otherface->numedges; ens++)
682 {
683 if (edgenum2 == abs((*aasworld).edgeindex[otherface->firstedge + ens])) break;
684 } //end for
685 if (ens != otherface->numedges) break;
686 } //end for
687 //if the edge was shared
688 if (s != numfaces) continue;
689 //
690 if (AAS_NonConvexEdges(edge1, edge2, side1, side2, planenum)) return qfalse;
691 } //end for
692 } //end for
693 } //end for
694 } //end for
695 return qtrue;
696 } //end of the function AAS_CanMergeFaces*/
697 //===========================================================================
698 //
699 // Parameter: -
700 // Returns: -
701 // Changes Globals: -
702 //===========================================================================
AAS_ConnectedAreas_r(int * areanums,int numareas,int * connectedareas,int curarea)703 void AAS_ConnectedAreas_r( int *areanums, int numareas, int *connectedareas, int curarea ) {
704 int i, j, otherareanum, facenum;
705 aas_area_t *area;
706 aas_face_t *face;
707
708 connectedareas[curarea] = qtrue;
709 area = &( *aasworld ).areas[areanums[curarea]];
710 for ( i = 0; i < area->numfaces; i++ )
711 {
712 facenum = abs( ( *aasworld ).faceindex[area->firstface + i] );
713 face = &( *aasworld ).faces[facenum];
714 //if the face is solid
715 if ( face->faceflags & FACE_SOLID ) {
716 continue;
717 }
718 //get the area at the other side of the face
719 if ( face->frontarea != areanums[curarea] ) {
720 otherareanum = face->frontarea;
721 } else { otherareanum = face->backarea;}
722 //check if the face is leading to one of the other areas
723 for ( j = 0; j < numareas; j++ )
724 {
725 if ( areanums[j] == otherareanum ) {
726 break;
727 }
728 } //end for
729 //if the face isn't leading to one of the other areas
730 if ( j == numareas ) {
731 continue;
732 }
733 //if the other area is already connected
734 if ( connectedareas[j] ) {
735 continue;
736 }
737 //recursively proceed with the other area
738 AAS_ConnectedAreas_r( areanums, numareas, connectedareas, j );
739 } //end for
740 } //end of the function AAS_ConnectedAreas_r
741 //===========================================================================
742 //
743 // Parameter: -
744 // Returns: -
745 // Changes Globals: -
746 //===========================================================================
AAS_ConnectedAreas(int * areanums,int numareas)747 qboolean AAS_ConnectedAreas( int *areanums, int numareas ) {
748 int connectedareas[MAX_PORTALAREAS], i;
749
750 memset( connectedareas, 0, sizeof( connectedareas ) );
751 if ( numareas < 1 ) {
752 return qfalse;
753 }
754 if ( numareas == 1 ) {
755 return qtrue;
756 }
757 AAS_ConnectedAreas_r( areanums, numareas, connectedareas, 0 );
758 for ( i = 0; i < numareas; i++ )
759 {
760 if ( !connectedareas[i] ) {
761 return qfalse;
762 }
763 } //end for
764 return qtrue;
765 } //end of the function AAS_ConnectedAreas
766 //===========================================================================
767 // gets adjacent areas with less presence types recursively
768 //
769 // Parameter: -
770 // Returns: -
771 // Changes Globals: -
772 //===========================================================================
AAS_GetAdjacentAreasWithLessPresenceTypes_r(int * areanums,int numareas,int curareanum)773 int AAS_GetAdjacentAreasWithLessPresenceTypes_r( int *areanums, int numareas, int curareanum ) {
774 int i, j, presencetype, otherpresencetype, otherareanum, facenum;
775 aas_area_t *area;
776 aas_face_t *face;
777
778 areanums[numareas++] = curareanum;
779 area = &( *aasworld ).areas[curareanum];
780 presencetype = ( *aasworld ).areasettings[curareanum].presencetype;
781 for ( i = 0; i < area->numfaces; i++ )
782 {
783 facenum = abs( ( *aasworld ).faceindex[area->firstface + i] );
784 face = &( *aasworld ).faces[facenum];
785 //if the face is solid
786 if ( face->faceflags & FACE_SOLID ) {
787 continue;
788 }
789 //the area at the other side of the face
790 if ( face->frontarea != curareanum ) {
791 otherareanum = face->frontarea;
792 } else { otherareanum = face->backarea;}
793 //
794 otherpresencetype = ( *aasworld ).areasettings[otherareanum].presencetype;
795 //if the other area has less presence types
796 if ( ( presencetype & ~otherpresencetype ) &&
797 !( otherpresencetype & ~presencetype ) ) {
798 //check if the other area isn't already in the list
799 for ( j = 0; j < numareas; j++ )
800 {
801 if ( otherareanum == areanums[j] ) {
802 break;
803 }
804 } //end for
805 //if the other area isn't already in the list
806 if ( j == numareas ) {
807 if ( numareas >= MAX_PORTALAREAS ) {
808 AAS_Error( "MAX_PORTALAREAS\n" );
809 return numareas;
810 } //end if
811 numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r( areanums, numareas, otherareanum );
812 } //end if
813 } //end if
814 } //end for
815 return numareas;
816 } //end of the function AAS_GetAdjacentAreasWithLessPresenceTypes_r
817 //===========================================================================
818 //
819 // Parameter: -
820 // Returns: -
821 // Changes Globals: -
822 //===========================================================================
AAS_CheckAreaForPossiblePortals(int areanum)823 int AAS_CheckAreaForPossiblePortals( int areanum ) {
824 int i, j, k, fen, ben, frontedgenum, backedgenum, facenum;
825 int areanums[MAX_PORTALAREAS], numareas, otherareanum;
826 int numareafrontfaces[MAX_PORTALAREAS], numareabackfaces[MAX_PORTALAREAS];
827 int frontfacenums[MAX_PORTALAREAS], backfacenums[MAX_PORTALAREAS];
828 int numfrontfaces, numbackfaces;
829 int frontareanums[MAX_PORTALAREAS], backareanums[MAX_PORTALAREAS];
830 int numfrontareas, numbackareas;
831 int frontplanenum, backplanenum, faceplanenum;
832 aas_area_t *area;
833 aas_face_t *frontface, *backface, *face;
834
835 //if it isn't already a portal
836 if ( ( *aasworld ).areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
837 return 0;
838 }
839 //it must be a grounded area
840 if ( !( ( *aasworld ).areasettings[areanum].areaflags & AREA_GROUNDED ) ) {
841 return 0;
842 }
843 //
844 memset( numareafrontfaces, 0, sizeof( numareafrontfaces ) );
845 memset( numareabackfaces, 0, sizeof( numareabackfaces ) );
846 numfrontfaces = numbackfaces = 0;
847 numfrontareas = numbackareas = 0;
848 frontplanenum = backplanenum = -1;
849 //add any adjacent areas with less presence types
850 numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r( areanums, 0, areanum );
851 //
852 for ( i = 0; i < numareas; i++ )
853 {
854 area = &( *aasworld ).areas[areanums[i]];
855 for ( j = 0; j < area->numfaces; j++ )
856 {
857 facenum = abs( ( *aasworld ).faceindex[area->firstface + j] );
858 face = &( *aasworld ).faces[facenum];
859 //if the face is solid
860 if ( face->faceflags & FACE_SOLID ) {
861 continue;
862 }
863 //check if the face is shared with one of the other areas
864 for ( k = 0; k < numareas; k++ )
865 {
866 if ( k == i ) {
867 continue;
868 }
869 if ( face->frontarea == areanums[k] || face->backarea == areanums[k] ) {
870 break;
871 }
872 } //end for
873 //if the face is shared
874 if ( k != numareas ) {
875 continue;
876 }
877 //the number of the area at the other side of the face
878 if ( face->frontarea == areanums[i] ) {
879 otherareanum = face->backarea;
880 } else { otherareanum = face->frontarea;}
881 //if the other area already is a cluter portal
882 if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
883 return 0;
884 }
885 //number of the plane of the area
886 faceplanenum = face->planenum & ~1;
887 //
888 if ( frontplanenum < 0 || faceplanenum == frontplanenum ) {
889 frontplanenum = faceplanenum;
890 frontfacenums[numfrontfaces++] = facenum;
891 for ( k = 0; k < numfrontareas; k++ )
892 {
893 if ( frontareanums[k] == otherareanum ) {
894 break;
895 }
896 } //end for
897 if ( k == numfrontareas ) {
898 frontareanums[numfrontareas++] = otherareanum;
899 }
900 numareafrontfaces[i]++;
901 } //end if
902 else if ( backplanenum < 0 || faceplanenum == backplanenum ) {
903 backplanenum = faceplanenum;
904 backfacenums[numbackfaces++] = facenum;
905 for ( k = 0; k < numbackareas; k++ )
906 {
907 if ( backareanums[k] == otherareanum ) {
908 break;
909 }
910 } //end for
911 if ( k == numbackareas ) {
912 backareanums[numbackareas++] = otherareanum;
913 }
914 numareabackfaces[i]++;
915 } //end else
916 else
917 {
918 return 0;
919 } //end else
920 } //end for
921 } //end for
922 //every area should have at least one front face and one back face
923 for ( i = 0; i < numareas; i++ )
924 {
925 if ( !numareafrontfaces[i] || !numareabackfaces[i] ) {
926 return 0;
927 }
928 } //end for
929 //the front areas should all be connected
930 if ( !AAS_ConnectedAreas( frontareanums, numfrontareas ) ) {
931 return 0;
932 }
933 //the back areas should all be connected
934 if ( !AAS_ConnectedAreas( backareanums, numbackareas ) ) {
935 return 0;
936 }
937 //none of the front faces should have a shared edge with a back face
938 for ( i = 0; i < numfrontfaces; i++ )
939 {
940 frontface = &( *aasworld ).faces[frontfacenums[i]];
941 for ( fen = 0; fen < frontface->numedges; fen++ )
942 {
943 frontedgenum = abs( ( *aasworld ).edgeindex[frontface->firstedge + fen] );
944 for ( j = 0; j < numbackfaces; j++ )
945 {
946 backface = &( *aasworld ).faces[backfacenums[j]];
947 for ( ben = 0; ben < backface->numedges; ben++ )
948 {
949 backedgenum = abs( ( *aasworld ).edgeindex[backface->firstedge + ben] );
950 if ( frontedgenum == backedgenum ) {
951 break;
952 }
953 } //end for
954 if ( ben != backface->numedges ) {
955 break;
956 }
957 } //end for
958 if ( j != numbackfaces ) {
959 break;
960 }
961 } //end for
962 if ( fen != frontface->numedges ) {
963 break;
964 }
965 } //end for
966 if ( i != numfrontfaces ) {
967 return 0;
968 }
969 //set the cluster portal contents
970 for ( i = 0; i < numareas; i++ )
971 {
972 ( *aasworld ).areasettings[areanums[i]].contents |= AREACONTENTS_CLUSTERPORTAL;
973 //this area can be used as a route portal
974 ( *aasworld ).areasettings[areanums[i]].contents |= AREACONTENTS_ROUTEPORTAL;
975 Log_Write( "possible portal: %d\r\n", areanums[i] );
976 } //end for
977 //
978 return numareas;
979 } //end of the function AAS_CheckAreaForPossiblePortals
980 //===========================================================================
981 //
982 // Parameter: -
983 // Returns: -
984 // Changes Globals: -
985 //===========================================================================
AAS_FindPossiblePortals(void)986 void AAS_FindPossiblePortals( void ) {
987 int i, numpossibleportals;
988
989 numpossibleportals = 0;
990 for ( i = 1; i < ( *aasworld ).numareas; i++ )
991 {
992 numpossibleportals += AAS_CheckAreaForPossiblePortals( i );
993 } //end for
994 botimport.Print( PRT_MESSAGE, "\r%6d possible portals\n", numpossibleportals );
995 } //end of the function AAS_FindPossiblePortals
996 //===========================================================================
997 //
998 // Parameter: -
999 // Returns: -
1000 // Changes Globals: -
1001 //===========================================================================
AAS_RemoveAllPortals(void)1002 void AAS_RemoveAllPortals( void ) {
1003 int i;
1004
1005 for ( i = 1; i < ( *aasworld ).numareas; i++ )
1006 {
1007 ( *aasworld ).areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1008 } //end for
1009 } //end of the function AAS_RemoveAllPortals
1010
1011 #if 0
1012 //===========================================================================
1013 //
1014 // Parameter: -
1015 // Returns: -
1016 // Changes Globals: -
1017 //===========================================================================
1018 void AAS_FloodCluster_r( int areanum, int clusternum ) {
1019 int i, otherareanum;
1020 aas_face_t *face;
1021 aas_area_t *area;
1022
1023 //set cluster mark
1024 ( *aasworld ).areasettings[areanum].cluster = clusternum;
1025 //if the area is a portal
1026 //if ((*aasworld).areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return;
1027 //
1028 area = &( *aasworld ).areas[areanum];
1029 //use area faces to flood into adjacent areas
1030 for ( i = 0; i < area->numfaces; i++ )
1031 {
1032 face = &( *aasworld ).faces[abs( ( *aasworld ).faceindex[area->firstface + i] )];
1033 //
1034 if ( face->frontarea != areanum ) {
1035 otherareanum = face->frontarea;
1036 } else { otherareanum = face->backarea;}
1037 //if there's no area at the other side
1038 if ( !otherareanum ) {
1039 continue;
1040 }
1041 //if the area is a portal
1042 if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1043 continue;
1044 }
1045 //if the area is already marked
1046 if ( ( *aasworld ).areasettings[otherareanum].cluster ) {
1047 continue;
1048 }
1049 //
1050 AAS_FloodCluster_r( otherareanum, clusternum );
1051 } //end for
1052 //use the reachabilities to flood into other areas
1053 for ( i = 0; i < ( *aasworld ).areasettings[areanum].numreachableareas; i++ )
1054 {
1055 otherareanum = ( *aasworld ).reachability[
1056 ( *aasworld ).areasettings[areanum].firstreachablearea + i].areanum;
1057 if ( !otherareanum ) {
1058 continue;
1059 AAS_Error( "reachability %d has zero area\n", ( *aasworld ).areasettings[areanum].firstreachablearea + i );
1060 } //end if
1061 //if the area is a portal
1062 if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1063 continue;
1064 }
1065 //if the area is already marked
1066 if ( ( *aasworld ).areasettings[otherareanum].cluster ) {
1067 continue;
1068 }
1069 //
1070 AAS_FloodCluster_r( otherareanum, clusternum );
1071 } //end for
1072 } //end of the function AAS_FloodCluster_r
1073 //===========================================================================
1074 //
1075 // Parameter: -
1076 // Returns: -
1077 // Changes Globals: -
1078 //===========================================================================
1079 void AAS_RemoveTeleporterPortals( void ) {
1080 int i, j, areanum;
1081
1082 for ( i = 1; i < ( *aasworld ).numareas; i++ )
1083 {
1084 for ( j = 0; j < ( *aasworld ).areasettings[i].numreachableareas; j++ )
1085 {
1086 areanum = ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].areanum;
1087 if ( ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].traveltype == TRAVEL_TELEPORT ) {
1088 ( *aasworld ).areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1089 ( *aasworld ).areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1090 break;
1091 } //end if
1092 } //end for
1093 } //end for
1094 } //end of the function AAS_RemoveTeleporterPortals
1095 //===========================================================================
1096 //
1097 // Parameter: -
1098 // Returns: -
1099 // Changes Globals: -
1100 //===========================================================================
1101 void AAS_FloodClusterReachabilities( int clusternum ) {
1102 int i, j, areanum;
1103
1104 for ( i = 1; i < ( *aasworld ).numareas; i++ )
1105 {
1106 //if this area already has a cluster set
1107 if ( ( *aasworld ).areasettings[i].cluster ) {
1108 continue;
1109 }
1110 //if this area is a cluster portal
1111 if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
1112 continue;
1113 }
1114 //loop over the reachable areas from this area
1115 for ( j = 0; j < ( *aasworld ).areasettings[i].numreachableareas; j++ )
1116 {
1117 //the reachable area
1118 areanum = ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].areanum;
1119 //if this area is a cluster portal
1120 if ( ( *aasworld ).areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1121 continue;
1122 }
1123 //if this area has a cluster set
1124 if ( ( *aasworld ).areasettings[areanum].cluster == clusternum ) {
1125 AAS_FloodCluster_r( i, clusternum );
1126 i = 0;
1127 break;
1128 } //end if
1129 } //end for
1130 } //end for
1131 } //end of the function AAS_FloodClusterReachabilities
1132 //===========================================================================
1133 //
1134 // Parameter: -
1135 // Returns: -
1136 // Changes Globals: -
1137 //===========================================================================
1138
1139 void AAS_RemoveNotClusterClosingPortals( void ) {
1140 int i, j, k, facenum, otherareanum, nonclosingportals;
1141 aas_area_t *area;
1142 aas_face_t *face;
1143
1144 AAS_RemoveTeleporterPortals();
1145 //
1146 nonclosingportals = 0;
1147 for ( i = 1; i < ( *aasworld ).numareas; i++ )
1148 {
1149 if ( !( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) ) {
1150 continue;
1151 }
1152 //find a non-portal area adjacent to the portal area and flood
1153 //the cluster from there
1154 area = &( *aasworld ).areas[i];
1155 for ( j = 0; j < area->numfaces; j++ )
1156 {
1157 facenum = abs( ( *aasworld ).faceindex[area->firstface + j] );
1158 face = &( *aasworld ).faces[facenum];
1159 //
1160 if ( face->frontarea != i ) {
1161 otherareanum = face->frontarea;
1162 } else { otherareanum = face->backarea;}
1163 //
1164 if ( !otherareanum ) {
1165 continue;
1166 }
1167 //
1168 if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1169 continue;
1170 } //end if
1171 //reset all cluster fields
1172 AAS_RemoveClusterAreas();
1173 //
1174 AAS_FloodCluster_r( otherareanum, 1 );
1175 AAS_FloodClusterReachabilities( 1 );
1176 //check if all adjacent non-portal areas have a cluster set
1177 for ( k = 0; k < area->numfaces; k++ )
1178 {
1179 facenum = abs( ( *aasworld ).faceindex[area->firstface + k] );
1180 face = &( *aasworld ).faces[facenum];
1181 //
1182 if ( face->frontarea != i ) {
1183 otherareanum = face->frontarea;
1184 } else { otherareanum = face->backarea;}
1185 //
1186 if ( !otherareanum ) {
1187 continue;
1188 }
1189 //
1190 if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1191 continue;
1192 } //end if
1193 //
1194 if ( !( *aasworld ).areasettings[otherareanum].cluster ) {
1195 break;
1196 }
1197 } //end for
1198 //if all adjacent non-portal areas have a cluster set then the portal
1199 //didn't seal a cluster
1200 if ( k >= area->numfaces ) {
1201 ( *aasworld ).areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1202 nonclosingportals++;
1203 //recheck all the other portals again
1204 i = 0;
1205 break;
1206 } //end if
1207 } //end for
1208 } //end for
1209 botimport.Print( PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals );
1210 } //end of the function AAS_RemoveNotClusterClosingPortals*/
1211 //===========================================================================
1212 //
1213 // Parameter: -
1214 // Returns: -
1215 // Changes Globals: -
1216 //===========================================================================
1217
1218 void AAS_RemoveNotClusterClosingPortals( void ) {
1219 int i, j, facenum, otherareanum, nonclosingportals, numseperatedclusters;
1220 aas_area_t *area;
1221 aas_face_t *face;
1222
1223 AAS_RemoveTeleporterPortals();
1224 //
1225 nonclosingportals = 0;
1226 for ( i = 1; i < ( *aasworld ).numareas; i++ )
1227 {
1228 if ( !( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) ) {
1229 continue;
1230 }
1231 //
1232 numseperatedclusters = 0;
1233 //reset all cluster fields
1234 AAS_RemoveClusterAreas();
1235 //find a non-portal area adjacent to the portal area and flood
1236 //the cluster from there
1237 area = &( *aasworld ).areas[i];
1238 for ( j = 0; j < area->numfaces; j++ )
1239 {
1240 facenum = abs( ( *aasworld ).faceindex[area->firstface + j] );
1241 face = &( *aasworld ).faces[facenum];
1242 //
1243 if ( face->frontarea != i ) {
1244 otherareanum = face->frontarea;
1245 } else { otherareanum = face->backarea;}
1246 //if not solid at the other side of the face
1247 if ( !otherareanum ) {
1248 continue;
1249 }
1250 //don't flood into other portals
1251 if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1252 continue;
1253 }
1254 //if the area already has a cluster set
1255 if ( ( *aasworld ).areasettings[otherareanum].cluster ) {
1256 continue;
1257 }
1258 //another cluster is seperated by this portal
1259 numseperatedclusters++;
1260 //flood the cluster
1261 AAS_FloodCluster_r( otherareanum, numseperatedclusters );
1262 AAS_FloodClusterReachabilities( numseperatedclusters );
1263 } //end for
1264 //use the reachabilities to flood into other areas
1265 for ( j = 0; j < ( *aasworld ).areasettings[i].numreachableareas; j++ )
1266 {
1267 otherareanum = ( *aasworld ).reachability[
1268 ( *aasworld ).areasettings[i].firstreachablearea + j].areanum;
1269 //this should never be qtrue but we check anyway
1270 if ( !otherareanum ) {
1271 continue;
1272 }
1273 //don't flood into other portals
1274 if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1275 continue;
1276 }
1277 //if the area already has a cluster set
1278 if ( ( *aasworld ).areasettings[otherareanum].cluster ) {
1279 continue;
1280 }
1281 //another cluster is seperated by this portal
1282 numseperatedclusters++;
1283 //flood the cluster
1284 AAS_FloodCluster_r( otherareanum, numseperatedclusters );
1285 AAS_FloodClusterReachabilities( numseperatedclusters );
1286 } //end for
1287 //a portal must seperate no more and no less than 2 clusters
1288 if ( numseperatedclusters != 2 ) {
1289 ( *aasworld ).areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1290 nonclosingportals++;
1291 //recheck all the other portals again
1292 i = 0;
1293 } //end if
1294 } //end for
1295 botimport.Print( PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals );
1296 } //end of the function AAS_RemoveNotClusterClosingPortals
1297
1298 //===========================================================================
1299 //
1300 // Parameter: -
1301 // Returns: -
1302 // Changes Globals: -
1303 //===========================================================================
1304
1305 void AAS_AddTeleporterPortals( void ) {
1306 int j, area2num, facenum, otherareanum;
1307 char *target, *targetname, *classname;
1308 bsp_entity_t *entities, *ent, *dest;
1309 vec3_t origin, destorigin, mins, maxs, end;
1310 vec3_t bbmins, bbmaxs;
1311 aas_area_t *area;
1312 aas_face_t *face;
1313 aas_trace_t trace;
1314 aas_link_t *areas, *link;
1315
1316 entities = AAS_ParseBSPEntities();
1317
1318 for ( ent = entities; ent; ent = ent->next )
1319 {
1320 classname = AAS_ValueForBSPEpairKey( ent, "classname" );
1321 if ( classname && !strcmp( classname, "misc_teleporter" ) ) {
1322 if ( !AAS_VectorForBSPEpairKey( ent, "origin", origin ) ) {
1323 botimport.Print( PRT_ERROR, "teleporter (%s) without origin\n", target );
1324 continue;
1325 } //end if
1326 //
1327 target = AAS_ValueForBSPEpairKey( ent, "target" );
1328 if ( !target ) {
1329 botimport.Print( PRT_ERROR, "teleporter (%s) without target\n", target );
1330 continue;
1331 } //end if
1332 for ( dest = entities; dest; dest = dest->next )
1333 {
1334 classname = AAS_ValueForBSPEpairKey( dest, "classname" );
1335 if ( classname && !strcmp( classname, "misc_teleporter_dest" ) ) {
1336 targetname = AAS_ValueForBSPEpairKey( dest, "targetname" );
1337 if ( targetname && !strcmp( targetname, target ) ) {
1338 break;
1339 } //end if
1340 } //end if
1341 } //end for
1342 if ( !dest ) {
1343 botimport.Print( PRT_ERROR, "teleporter without destination (%s)\n", target );
1344 continue;
1345 } //end if
1346 if ( !AAS_VectorForBSPEpairKey( dest, "origin", destorigin ) ) {
1347 botimport.Print( PRT_ERROR, "teleporter destination (%s) without origin\n", target );
1348 continue;
1349 } //end if
1350 destorigin[2] += 24; //just for q2e1m2, the dork has put the telepads in the ground
1351 VectorCopy( destorigin, end );
1352 end[2] -= 100;
1353 trace = AAS_TraceClientBBox( destorigin, end, PRESENCE_CROUCH, -1 );
1354 if ( trace.startsolid ) {
1355 botimport.Print( PRT_ERROR, "teleporter destination (%s) in solid\n", target );
1356 continue;
1357 } //end if
1358 VectorCopy( trace.endpos, destorigin );
1359 area2num = AAS_PointAreaNum( destorigin );
1360 //reset all cluster fields
1361 for ( j = 0; j < ( *aasworld ).numareas; j++ )
1362 {
1363 ( *aasworld ).areasettings[j].cluster = 0;
1364 } //end for
1365 //
1366 VectorSet( mins, -8, -8, 8 );
1367 VectorSet( maxs, 8, 8, 24 );
1368 //
1369 AAS_PresenceTypeBoundingBox( PRESENCE_CROUCH, bbmins, bbmaxs );
1370 //
1371 VectorAdd( origin, mins, mins );
1372 VectorAdd( origin, maxs, maxs );
1373 //add bounding box size
1374 VectorSubtract( mins, bbmaxs, mins );
1375 VectorSubtract( maxs, bbmins, maxs );
1376 //link an invalid (-1) entity
1377 areas = AAS_AASLinkEntity( mins, maxs, -1 );
1378 //
1379 for ( link = areas; link; link = link->next_area )
1380 {
1381 if ( !AAS_AreaGrounded( link->areanum ) ) {
1382 continue;
1383 }
1384 //add the teleporter portal mark
1385 ( *aasworld ).areasettings[link->areanum].contents |= AREACONTENTS_CLUSTERPORTAL |
1386 AREACONTENTS_TELEPORTAL;
1387 } //end for
1388 //
1389 for ( link = areas; link; link = link->next_area )
1390 {
1391 if ( !AAS_AreaGrounded( link->areanum ) ) {
1392 continue;
1393 }
1394 //find a non-portal area adjacent to the portal area and flood
1395 //the cluster from there
1396 area = &( *aasworld ).areas[link->areanum];
1397 for ( j = 0; j < area->numfaces; j++ )
1398 {
1399 facenum = abs( ( *aasworld ).faceindex[area->firstface + j] );
1400 face = &( *aasworld ).faces[facenum];
1401 //
1402 if ( face->frontarea != link->areanum ) {
1403 otherareanum = face->frontarea;
1404 } else { otherareanum = face->backarea;}
1405 //
1406 if ( !otherareanum ) {
1407 continue;
1408 }
1409 //
1410 if ( ( *aasworld ).areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL ) {
1411 continue;
1412 } //end if
1413 //
1414 AAS_FloodCluster_r( otherareanum, 1 );
1415 } //end for
1416 } //end for
1417 //if the teleport destination IS in the same cluster
1418 if ( ( *aasworld ).areasettings[area2num].cluster ) {
1419 for ( link = areas; link; link = link->next_area )
1420 {
1421 if ( !AAS_AreaGrounded( link->areanum ) ) {
1422 continue;
1423 }
1424 //add the teleporter portal mark
1425 ( *aasworld ).areasettings[link->areanum].contents &= ~( AREACONTENTS_CLUSTERPORTAL |
1426 AREACONTENTS_TELEPORTAL );
1427 } //end for
1428 } //end if
1429 } //end if
1430 } //end for
1431 AAS_FreeBSPEntities( entities );
1432 } //end of the function AAS_AddTeleporterPortals
1433
1434 //===========================================================================
1435 //
1436 // Parameter: -
1437 // Returns: -
1438 // Changes Globals: -
1439 //===========================================================================
1440 void AAS_AddTeleporterPortals( void ) {
1441 int i, j, areanum;
1442
1443 for ( i = 1; i < ( *aasworld ).numareas; i++ )
1444 {
1445 for ( j = 0; j < ( *aasworld ).areasettings[i].numreachableareas; j++ )
1446 {
1447 if ( ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].traveltype != TRAVEL_TELEPORT ) {
1448 continue;
1449 }
1450 areanum = ( *aasworld ).reachability[( *aasworld ).areasettings[i].firstreachablearea + j].areanum;
1451 ( *aasworld ).areasettings[areanum].contents |= AREACONTENTS_CLUSTERPORTAL;
1452 } //end for
1453 } //end for
1454 } //end of the function AAS_AddTeleporterPortals*/
1455 #endif
1456 //===========================================================================
1457 //
1458 // Parameter: -
1459 // Returns: -
1460 // Changes Globals: -
1461 //===========================================================================
AAS_TestPortals(void)1462 int AAS_TestPortals( void ) {
1463 int i;
1464 aas_portal_t *portal;
1465
1466 for ( i = 1; i < ( *aasworld ).numportals; i++ )
1467 {
1468 portal = &( *aasworld ).portals[i];
1469 if ( !portal->frontcluster ) {
1470 ( *aasworld ).areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1471 Log_Write( "portal area %d has no front cluster\r\n", portal->areanum );
1472 return qfalse;
1473 } //end if
1474 if ( !portal->backcluster ) {
1475 ( *aasworld ).areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
1476 Log_Write( "portal area %d has no back cluster\r\n", portal->areanum );
1477 return qfalse;
1478 } //end if
1479 } //end for
1480 return qtrue;
1481 } //end of the function
1482 //===========================================================================
1483 //
1484 // Parameter: -
1485 // Returns: -
1486 // Changes Globals: -
1487 //===========================================================================
AAS_CountForcedClusterPortals(void)1488 void AAS_CountForcedClusterPortals( void ) {
1489 int num, i;
1490
1491 num = 0;
1492 for ( i = 1; i < ( *aasworld ).numareas; i++ )
1493 {
1494 if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
1495 num++;
1496 } //end if
1497 } //end for
1498 botimport.Print( PRT_MESSAGE, "%6d forced portals\n", num );
1499 } //end of the function AAS_CountForcedClusterPortals
1500 //===========================================================================
1501 //
1502 // Parameter: -
1503 // Returns: -
1504 // Changes Globals: -
1505 //===========================================================================
AAS_CreateViewPortals(void)1506 void AAS_CreateViewPortals( void ) {
1507 int i;
1508
1509 for ( i = 1; i < ( *aasworld ).numareas; i++ )
1510 {
1511 if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
1512 ( *aasworld ).areasettings[i].contents |= AREACONTENTS_VIEWPORTAL;
1513 } //end if
1514 } //end for
1515 } //end of the function AAS_CreateViewPortals
1516 //===========================================================================
1517 //
1518 // Parameter: -
1519 // Returns: -
1520 // Changes Globals: -
1521 //===========================================================================
AAS_SetViewPortalsAsClusterPortals(void)1522 void AAS_SetViewPortalsAsClusterPortals( void ) {
1523 int i;
1524
1525 for ( i = 1; i < ( *aasworld ).numareas; i++ )
1526 {
1527 if ( ( *aasworld ).areasettings[i].contents & AREACONTENTS_VIEWPORTAL ) {
1528 ( *aasworld ).areasettings[i].contents |= AREACONTENTS_CLUSTERPORTAL;
1529 } //end if
1530 } //end for
1531 } //end of the function AAS_SetViewPortalsAsClusterPortals
1532 //===========================================================================
1533 //
1534 // Parameter: -
1535 // Returns: -
1536 // Changes Globals: -
1537 //===========================================================================
AAS_InitClustering(void)1538 void AAS_InitClustering( void ) {
1539 int i, removedPortalAreas;
1540 int n, total, numreachabilityareas;
1541
1542 if ( !( *aasworld ).loaded ) {
1543 return;
1544 }
1545 //if there are clusters
1546 if ( ( *aasworld ).numclusters >= 1 ) {
1547 #ifndef BSPC
1548 //if clustering isn't forced
1549 if ( !( (int)LibVarGetValue( "forceclustering" ) ) &&
1550 !( (int)LibVarGetValue( "forcereachability" ) ) ) {
1551 return;
1552 }
1553 #else
1554 return;
1555 #endif
1556 } //end if
1557 //
1558 AAS_CountForcedClusterPortals();
1559 //remove all the existing portals
1560 //AAS_RemoveAllPortals();
1561 //remove all area cluster marks
1562 AAS_RemoveClusterAreas();
1563 //find possible cluster portals
1564 AAS_FindPossiblePortals();
1565 //craete portals to for the bot view
1566 AAS_CreateViewPortals();
1567 //remove all portals that are not closing a cluster
1568 //AAS_RemoveNotClusterClosingPortals();
1569 //initialize portal memory
1570 if ( ( *aasworld ).portals ) {
1571 FreeMemory( ( *aasworld ).portals );
1572 }
1573 ( *aasworld ).portals = (aas_portal_t *) GetClearedMemory( AAS_MAX_PORTALS * sizeof( aas_portal_t ) );
1574 //initialize portal index memory
1575 if ( ( *aasworld ).portalindex ) {
1576 FreeMemory( ( *aasworld ).portalindex );
1577 }
1578 ( *aasworld ).portalindex = (aas_portalindex_t *) GetClearedMemory( AAS_MAX_PORTALINDEXSIZE * sizeof( aas_portalindex_t ) );
1579 //initialize cluster memory
1580 if ( ( *aasworld ).clusters ) {
1581 FreeMemory( ( *aasworld ).clusters );
1582 }
1583 ( *aasworld ).clusters = (aas_cluster_t *) GetClearedMemory( AAS_MAX_CLUSTERS * sizeof( aas_cluster_t ) );
1584 //
1585 removedPortalAreas = 0;
1586 botimport.Print( PRT_MESSAGE, "\r%6d removed portal areas", removedPortalAreas );
1587 while ( 1 )
1588 {
1589 botimport.Print( PRT_MESSAGE, "\r%6d", removedPortalAreas );
1590 //initialize the number of portals and clusters
1591 ( *aasworld ).numportals = 1; //portal 0 is a dummy
1592 ( *aasworld ).portalindexsize = 0;
1593 ( *aasworld ).numclusters = 1; //cluster 0 is a dummy
1594 //create the portals from the portal areas
1595 AAS_CreatePortals();
1596 //
1597 removedPortalAreas++;
1598 //find the clusters
1599 if ( !AAS_FindClusters() ) {
1600 continue;
1601 }
1602 //test the portals
1603 if ( !AAS_TestPortals() ) {
1604 continue;
1605 }
1606 //
1607 break;
1608 } //end while
1609 botimport.Print( PRT_MESSAGE, "\n" );
1610 //the AAS file should be saved
1611 ( *aasworld ).savefile = qtrue;
1612 // report cluster info
1613 botimport.Print( PRT_MESSAGE, "%6d portals created\n", ( *aasworld ).numportals );
1614 botimport.Print( PRT_MESSAGE, "%6d clusters created\n", ( *aasworld ).numclusters );
1615 for ( i = 1; i < ( *aasworld ).numclusters; i++ )
1616 {
1617 botimport.Print( PRT_MESSAGE, "cluster %d has %d reachability areas\n", i,
1618 ( *aasworld ).clusters[i].numreachabilityareas );
1619 } //end for
1620 // report AAS file efficiency
1621 numreachabilityareas = 0;
1622 total = 0;
1623 for ( i = 0; i < ( *aasworld ).numclusters; i++ ) {
1624 n = ( *aasworld ).clusters[i].numreachabilityareas;
1625 numreachabilityareas += n;
1626 total += n * n;
1627 }
1628 total += numreachabilityareas * ( *aasworld ).numportals;
1629 //
1630 botimport.Print( PRT_MESSAGE, "%6i total reachability areas\n", numreachabilityareas );
1631 botimport.Print( PRT_MESSAGE, "%6i AAS memory/CPU usage (the lower the better)\n", total * 3 );
1632 } //end of the function AAS_InitClustering
1633