Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

be_aas_cluster.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 
00023 /*****************************************************************************
00024  * name:        be_aas_cluster.c
00025  *
00026  * desc:        area clustering
00027  *
00028  * $Archive: /MissionPack/code/botlib/be_aas_cluster.c $
00029  *
00030  *****************************************************************************/
00031 
00032 #include "../game/q_shared.h"
00033 #include "l_memory.h"
00034 #include "l_script.h"
00035 #include "l_precomp.h"
00036 #include "l_struct.h"
00037 #include "l_log.h"
00038 #include "l_memory.h"
00039 #include "l_libvar.h"
00040 #include "aasfile.h"
00041 #include "../game/botlib.h"
00042 #include "../game/be_aas.h"
00043 #include "be_aas_funcs.h"
00044 #include "be_aas_def.h"
00045 
00046 extern botlib_import_t botimport;
00047 
00048 #define AAS_MAX_PORTALS                 65536
00049 #define AAS_MAX_PORTALINDEXSIZE         65536
00050 #define AAS_MAX_CLUSTERS                65536
00051 //
00052 #define MAX_PORTALAREAS         1024
00053 
00054 // do not flood through area faces, only use reachabilities
00055 int nofaceflood = qtrue;
00056 
00057 //===========================================================================
00058 //
00059 // Parameter:               -
00060 // Returns:                 -
00061 // Changes Globals:     -
00062 //===========================================================================
00063 void AAS_RemoveClusterAreas(void)
00064 {
00065     int i;
00066 
00067     for (i = 1; i < aasworld.numareas; i++)
00068     {
00069         aasworld.areasettings[i].cluster = 0;
00070     } //end for
00071 } //end of the function AAS_RemoveClusterAreas
00072 //===========================================================================
00073 //
00074 // Parameter:               -
00075 // Returns:                 -
00076 // Changes Globals:     -
00077 //===========================================================================
00078 void AAS_ClearCluster(int clusternum)
00079 {
00080     int i;
00081 
00082     for (i = 1; i < aasworld.numareas; i++)
00083     {
00084         if (aasworld.areasettings[i].cluster == clusternum)
00085         {
00086             aasworld.areasettings[i].cluster = 0;
00087         } //end if
00088     } //end for
00089 } //end of the function AAS_ClearCluster
00090 //===========================================================================
00091 //
00092 // Parameter:               -
00093 // Returns:                 -
00094 // Changes Globals:     -
00095 //===========================================================================
00096 void AAS_RemovePortalsClusterReference(int clusternum)
00097 {
00098     int portalnum;
00099 
00100     for (portalnum = 1; portalnum < aasworld.numportals; portalnum++)
00101     {
00102         if (aasworld.portals[portalnum].frontcluster == clusternum)
00103         {
00104             aasworld.portals[portalnum].frontcluster = 0;
00105         } //end if
00106         if (aasworld.portals[portalnum].backcluster == clusternum)
00107         {
00108             aasworld.portals[portalnum].backcluster = 0;
00109         } //end if
00110     } //end for
00111 } //end of the function AAS_RemovePortalsClusterReference
00112 //===========================================================================
00113 //
00114 // Parameter:               -
00115 // Returns:                 -
00116 // Changes Globals:     -
00117 //===========================================================================
00118 int AAS_UpdatePortal(int areanum, int clusternum)
00119 {
00120     int portalnum;
00121     aas_portal_t *portal;
00122     aas_cluster_t *cluster;
00123 
00124     //find the portal of the area
00125     for (portalnum = 1; portalnum < aasworld.numportals; portalnum++)
00126     {
00127         if (aasworld.portals[portalnum].areanum == areanum) break;
00128     } //end for
00129     //
00130     if (portalnum == aasworld.numportals)
00131     {
00132         AAS_Error("no portal of area %d", areanum);
00133         return qtrue;
00134     } //end if
00135     //
00136     portal = &aasworld.portals[portalnum];
00137     //if the portal is already fully updated
00138     if (portal->frontcluster == clusternum) return qtrue;
00139     if (portal->backcluster == clusternum) return qtrue;
00140     //if the portal has no front cluster yet
00141     if (!portal->frontcluster)
00142     {
00143         portal->frontcluster = clusternum;
00144     } //end if
00145     //if the portal has no back cluster yet
00146     else if (!portal->backcluster)
00147     {
00148         portal->backcluster = clusternum;
00149     } //end else if
00150     else
00151     {
00152         //remove the cluster portal flag contents
00153         aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
00154         Log_Write("portal area %d is seperating more than two clusters\r\n", areanum);
00155         return qfalse;
00156     } //end else
00157     if (aasworld.portalindexsize >= AAS_MAX_PORTALINDEXSIZE)
00158     {
00159         AAS_Error("AAS_MAX_PORTALINDEXSIZE");
00160         return qtrue;
00161     } //end if
00162     //set the area cluster number to the negative portal number
00163     aasworld.areasettings[areanum].cluster = -portalnum;
00164     //add the portal to the cluster using the portal index
00165     cluster = &aasworld.clusters[clusternum];
00166     aasworld.portalindex[cluster->firstportal + cluster->numportals] = portalnum;
00167     aasworld.portalindexsize++;
00168     cluster->numportals++;
00169     return qtrue;
00170 } //end of the function AAS_UpdatePortal
00171 //===========================================================================
00172 //
00173 // Parameter:               -
00174 // Returns:                 -
00175 // Changes Globals:     -
00176 //===========================================================================
00177 int AAS_FloodClusterAreas_r(int areanum, int clusternum)
00178 {
00179     aas_area_t *area;
00180     aas_face_t *face;
00181     int facenum, i;
00182 
00183     //
00184     if (areanum <= 0 || areanum >= aasworld.numareas)
00185     {
00186         AAS_Error("AAS_FloodClusterAreas_r: areanum out of range");
00187         return qfalse;
00188     } //end if
00189     //if the area is already part of a cluster
00190     if (aasworld.areasettings[areanum].cluster > 0)
00191     {
00192         if (aasworld.areasettings[areanum].cluster == clusternum) return qtrue;
00193         //
00194         //there's a reachability going from one cluster to another only in one direction
00195         //
00196         AAS_Error("cluster %d touched cluster %d at area %d\r\n",
00197                 clusternum, aasworld.areasettings[areanum].cluster, areanum);
00198         return qfalse;
00199     } //end if
00200     //don't add the cluster portal areas to the clusters
00201     if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL)
00202     {
00203         return AAS_UpdatePortal(areanum, clusternum);
00204     } //end if
00205     //set the area cluster number
00206     aasworld.areasettings[areanum].cluster = clusternum;
00207     aasworld.areasettings[areanum].clusterareanum =
00208                 aasworld.clusters[clusternum].numareas;
00209     //the cluster has an extra area
00210     aasworld.clusters[clusternum].numareas++;
00211 
00212     area = &aasworld.areas[areanum];
00213     //use area faces to flood into adjacent areas
00214     if (!nofaceflood)
00215     {
00216         for (i = 0; i < area->numfaces; i++)
00217         {
00218             facenum = abs(aasworld.faceindex[area->firstface + i]);
00219             face = &aasworld.faces[facenum];
00220             if (face->frontarea == areanum)
00221             {
00222                 if (face->backarea) if (!AAS_FloodClusterAreas_r(face->backarea, clusternum)) return qfalse;
00223             } //end if
00224             else
00225             {
00226                 if (face->frontarea) if (!AAS_FloodClusterAreas_r(face->frontarea, clusternum)) return qfalse;
00227             } //end else
00228         } //end for
00229     } //end if
00230     //use the reachabilities to flood into other areas
00231     for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++)
00232     {
00233         if (!aasworld.reachability[
00234                     aasworld.areasettings[areanum].firstreachablearea + i].areanum)
00235         {
00236             continue;
00237         } //end if
00238         if (!AAS_FloodClusterAreas_r(aasworld.reachability[
00239                 aasworld.areasettings[areanum].firstreachablearea + i].areanum, clusternum)) return qfalse;
00240     } //end for
00241     return qtrue;
00242 } //end of the function AAS_FloodClusterAreas_r
00243 //===========================================================================
00244 // try to flood from all areas without cluster into areas with a cluster set
00245 //
00246 // Parameter:               -
00247 // Returns:                 -
00248 // Changes Globals:     -
00249 //===========================================================================
00250 int AAS_FloodClusterAreasUsingReachabilities(int clusternum)
00251 {
00252     int i, j, areanum;
00253 
00254     for (i = 1; i < aasworld.numareas; i++)
00255     {
00256         //if this area already has a cluster set
00257         if (aasworld.areasettings[i].cluster)
00258             continue;
00259         //if this area is a cluster portal
00260         if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)
00261             continue;
00262         //loop over the reachable areas from this area
00263         for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)
00264         {
00265             //the reachable area
00266             areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum;
00267             //if this area is a cluster portal
00268             if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL)
00269                 continue;
00270             //if this area has a cluster set
00271             if (aasworld.areasettings[areanum].cluster)
00272             {
00273                 if (!AAS_FloodClusterAreas_r(i, clusternum))
00274                     return qfalse;
00275                 i = 0;
00276                 break;
00277             } //end if
00278         } //end for
00279     } //end for
00280     return qtrue;
00281 } //end of the function AAS_FloodClusterAreasUsingReachabilities
00282 //===========================================================================
00283 //
00284 // Parameter:           -
00285 // Returns:             -
00286 // Changes Globals:     -
00287 //===========================================================================
00288 void AAS_NumberClusterPortals(int clusternum)
00289 {
00290     int i, portalnum;
00291     aas_cluster_t *cluster;
00292     aas_portal_t *portal;
00293 
00294     cluster = &aasworld.clusters[clusternum];
00295     for (i = 0; i < cluster->numportals; i++)
00296     {
00297         portalnum = aasworld.portalindex[cluster->firstportal + i];
00298         portal = &aasworld.portals[portalnum];
00299         if (portal->frontcluster == clusternum)
00300         {
00301             portal->clusterareanum[0] = cluster->numareas++;
00302         } //end if
00303         else
00304         {
00305             portal->clusterareanum[1] = cluster->numareas++;
00306         } //end else
00307     } //end for
00308 } //end of the function AAS_NumberClusterPortals
00309 //===========================================================================
00310 //
00311 // Parameter:           -
00312 // Returns:             -
00313 // Changes Globals:     -
00314 //===========================================================================
00315 void AAS_NumberClusterAreas(int clusternum)
00316 {
00317     int i, portalnum;
00318     aas_cluster_t *cluster;
00319     aas_portal_t *portal;
00320 
00321     aasworld.clusters[clusternum].numareas = 0;
00322     aasworld.clusters[clusternum].numreachabilityareas = 0;
00323     //number all areas in this cluster WITH reachabilities
00324     for (i = 1; i < aasworld.numareas; i++)
00325     {
00326         //
00327         if (aasworld.areasettings[i].cluster != clusternum) continue;
00328         //
00329         if (!AAS_AreaReachability(i)) continue;
00330         //
00331         aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas;
00332         //the cluster has an extra area
00333         aasworld.clusters[clusternum].numareas++;
00334         aasworld.clusters[clusternum].numreachabilityareas++;
00335     } //end for
00336     //number all portals in this cluster WITH reachabilities
00337     cluster = &aasworld.clusters[clusternum];
00338     for (i = 0; i < cluster->numportals; i++)
00339     {
00340         portalnum = aasworld.portalindex[cluster->firstportal + i];
00341         portal = &aasworld.portals[portalnum];
00342         if (!AAS_AreaReachability(portal->areanum)) continue;
00343         if (portal->frontcluster == clusternum)
00344         {
00345             portal->clusterareanum[0] = cluster->numareas++;
00346             aasworld.clusters[clusternum].numreachabilityareas++;
00347         } //end if
00348         else
00349         {
00350             portal->clusterareanum[1] = cluster->numareas++;
00351             aasworld.clusters[clusternum].numreachabilityareas++;
00352         } //end else
00353     } //end for
00354     //number all areas in this cluster WITHOUT reachabilities
00355     for (i = 1; i < aasworld.numareas; i++)
00356     {
00357         //
00358         if (aasworld.areasettings[i].cluster != clusternum) continue;
00359         //
00360         if (AAS_AreaReachability(i)) continue;
00361         //
00362         aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas;
00363         //the cluster has an extra area
00364         aasworld.clusters[clusternum].numareas++;
00365     } //end for
00366     //number all portals in this cluster WITHOUT reachabilities
00367     cluster = &aasworld.clusters[clusternum];
00368     for (i = 0; i < cluster->numportals; i++)
00369     {
00370         portalnum = aasworld.portalindex[cluster->firstportal + i];
00371         portal = &aasworld.portals[portalnum];
00372         if (AAS_AreaReachability(portal->areanum)) continue;
00373         if (portal->frontcluster == clusternum)
00374         {
00375             portal->clusterareanum[0] = cluster->numareas++;
00376         } //end if
00377         else
00378         {
00379             portal->clusterareanum[1] = cluster->numareas++;
00380         } //end else
00381     } //end for
00382 } //end of the function AAS_NumberClusterAreas
00383 //===========================================================================
00384 //
00385 // Parameter:           -
00386 // Returns:             -
00387 // Changes Globals:     -
00388 //===========================================================================
00389 int AAS_FindClusters(void)
00390 {
00391     int i;
00392     aas_cluster_t *cluster;
00393 
00394     AAS_RemoveClusterAreas();
00395     //
00396     for (i = 1; i < aasworld.numareas; i++)
00397     {
00398         //if the area is already part of a cluster
00399         if (aasworld.areasettings[i].cluster)
00400             continue;
00401         // if not flooding through faces only use areas that have reachabilities
00402         if (nofaceflood)
00403         {
00404             if (!aasworld.areasettings[i].numreachableareas)
00405                 continue;
00406         } //end if
00407         //if the area is a cluster portal
00408         if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)
00409             continue;
00410         if (aasworld.numclusters >= AAS_MAX_CLUSTERS)
00411         {
00412             AAS_Error("AAS_MAX_CLUSTERS");
00413             return qfalse;
00414         } //end if
00415         cluster = &aasworld.clusters[aasworld.numclusters];
00416         cluster->numareas = 0;
00417         cluster->numreachabilityareas = 0;
00418         cluster->firstportal = aasworld.portalindexsize;
00419         cluster->numportals = 0;
00420         //flood the areas in this cluster
00421         if (!AAS_FloodClusterAreas_r(i, aasworld.numclusters))
00422             return qfalse;
00423         if (!AAS_FloodClusterAreasUsingReachabilities(aasworld.numclusters))
00424             return qfalse;
00425         //number the cluster areas
00426         //AAS_NumberClusterPortals(aasworld.numclusters);
00427         AAS_NumberClusterAreas(aasworld.numclusters);
00428         //Log_Write("cluster %d has %d areas\r\n", aasworld.numclusters, cluster->numareas);
00429         aasworld.numclusters++;
00430     } //end for
00431     return qtrue;
00432 } //end of the function AAS_FindClusters
00433 //===========================================================================
00434 //
00435 // Parameter:               -
00436 // Returns:                 -
00437 // Changes Globals:     -
00438 //===========================================================================
00439 void AAS_CreatePortals(void)
00440 {
00441     int i;
00442     aas_portal_t *portal;
00443 
00444     for (i = 1; i < aasworld.numareas; i++)
00445     {
00446         //if the area is a cluster portal
00447         if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)
00448         {
00449             if (aasworld.numportals >= AAS_MAX_PORTALS)
00450             {
00451                 AAS_Error("AAS_MAX_PORTALS");
00452                 return;
00453             } //end if
00454             portal = &aasworld.portals[aasworld.numportals];
00455             portal->areanum = i;
00456             portal->frontcluster = 0;
00457             portal->backcluster = 0;
00458             aasworld.numportals++;
00459         } //end if
00460     } //end for
00461 } //end of the function AAS_CreatePortals
00462 /*
00463 //===========================================================================
00464 //
00465 // Parameter:               -
00466 // Returns:                 -
00467 // Changes Globals:     -
00468 //===========================================================================
00469 int AAS_MapContainsTeleporters(void)
00470 {
00471     bsp_entity_t *entities, *ent;
00472     char *classname;
00473 
00474     entities = AAS_ParseBSPEntities();
00475 
00476     for (ent = entities; ent; ent = ent->next)
00477     {
00478         classname = AAS_ValueForBSPEpairKey(ent, "classname");
00479         if (classname && !strcmp(classname, "misc_teleporter"))
00480         {
00481             AAS_FreeBSPEntities(entities);
00482             return qtrue;
00483         } //end if
00484     } //end for
00485     return qfalse;
00486 } //end of the function AAS_MapContainsTeleporters
00487 //===========================================================================
00488 //
00489 // Parameter:               -
00490 // Returns:                 -
00491 // Changes Globals:     -
00492 //===========================================================================
00493 int AAS_NonConvexFaces(aas_face_t *face1, aas_face_t *face2, int side1, int side2)
00494 {
00495     int i, j, edgenum;
00496     aas_plane_t *plane1, *plane2;
00497     aas_edge_t *edge;
00498     
00499 
00500     plane1 = &aasworld.planes[face1->planenum ^ side1];
00501     plane2 = &aasworld.planes[face2->planenum ^ side2];
00502 
00503     //check if one of the points of face1 is at the back of the plane of face2
00504     for (i = 0; i < face1->numedges; i++)
00505     {
00506         edgenum = abs(aasworld.edgeindex[face1->firstedge + i]);
00507         edge = &aasworld.edges[edgenum];
00508         for (j = 0; j < 2; j++)
00509         {
00510             if (DotProduct(plane2->normal, aasworld.vertexes[edge->v[j]]) -
00511                             plane2->dist < -0.01) return qtrue;
00512         } //end for
00513     } //end for
00514     for (i = 0; i < face2->numedges; i++)
00515     {
00516         edgenum = abs(aasworld.edgeindex[face2->firstedge + i]);
00517         edge = &aasworld.edges[edgenum];
00518         for (j = 0; j < 2; j++)
00519         {
00520             if (DotProduct(plane1->normal, aasworld.vertexes[edge->v[j]]) -
00521                             plane1->dist < -0.01) return qtrue;
00522         } //end for
00523     } //end for
00524 
00525     return qfalse;
00526 } //end of the function AAS_NonConvexFaces
00527 //===========================================================================
00528 //
00529 // Parameter:               -
00530 // Returns:                 -
00531 // Changes Globals:     -
00532 //===========================================================================
00533 qboolean AAS_CanMergeAreas(int *areanums, int numareas)
00534 {
00535     int i, j, s, face1num, face2num, side1, side2, fn1, fn2;
00536     aas_face_t *face1, *face2;
00537     aas_area_t *area1, *area2;
00538 
00539     for (i = 0; i < numareas; i++)
00540     {
00541         area1 = &aasworld.areas[areanums[i]];
00542         for (fn1 = 0; fn1 < area1->numfaces; fn1++)
00543         {
00544             face1num = abs(aasworld.faceindex[area1->firstface + fn1]);
00545             face1 = &aasworld.faces[face1num];
00546             side1 = face1->frontarea != areanums[i];
00547             //check if the face isn't a shared one with one of the other areas
00548             for (s = 0; s < numareas; s++)
00549             {
00550                 if (s == i) continue;
00551                 if (face1->frontarea == s || face1->backarea == s) break;
00552             } //end for
00553             //if the face was a shared one
00554             if (s != numareas) continue;
00555             //
00556             for (j = 0; j < numareas; j++)
00557             {
00558                 if (j == i) continue;
00559                 area2 = &aasworld.areas[areanums[j]];
00560                 for (fn2 = 0; fn2 < area2->numfaces; fn2++)
00561                 {
00562                     face2num = abs(aasworld.faceindex[area2->firstface + fn2]);
00563                     face2 = &aasworld.faces[face2num];
00564                     side2 = face2->frontarea != areanums[j];
00565                     //check if the face isn't a shared one with one of the other areas
00566                     for (s = 0; s < numareas; s++)
00567                     {
00568                         if (s == j) continue;
00569                         if (face2->frontarea == s || face2->backarea == s) break;
00570                     } //end for
00571                     //if the face was a shared one
00572                     if (s != numareas) continue;
00573                     //
00574                     if (AAS_NonConvexFaces(face1, face2, side1, side2)) return qfalse;
00575                 } //end for
00576             } //end for
00577         } //end for
00578     } //end for
00579     return qtrue;
00580 } //end of the function AAS_CanMergeAreas
00581 //===========================================================================
00582 //
00583 // Parameter:               -
00584 // Returns:                 -
00585 // Changes Globals:     -
00586 //===========================================================================
00587 qboolean AAS_NonConvexEdges(aas_edge_t *edge1, aas_edge_t *edge2, int side1, int side2, int planenum)
00588 {
00589     int i;
00590     vec3_t edgevec1, edgevec2, normal1, normal2;
00591     float dist1, dist2;
00592     aas_plane_t *plane;
00593 
00594     plane = &aasworld.planes[planenum];
00595     VectorSubtract(aasworld.vertexes[edge1->v[1]], aasworld.vertexes[edge1->v[0]], edgevec1);
00596     VectorSubtract(aasworld.vertexes[edge2->v[1]], aasworld.vertexes[edge2->v[0]], edgevec2);
00597     if (side1) VectorInverse(edgevec1);
00598     if (side2) VectorInverse(edgevec2);
00599     //
00600     CrossProduct(edgevec1, plane->normal, normal1);
00601     dist1 = DotProduct(normal1, aasworld.vertexes[edge1->v[0]]);
00602     CrossProduct(edgevec2, plane->normal, normal2);
00603     dist2 = DotProduct(normal2, aasworld.vertexes[edge2->v[0]]);
00604 
00605     for (i = 0; i < 2; i++)
00606     {
00607         if (DotProduct(aasworld.vertexes[edge1->v[i]], normal2) - dist2 < -0.01) return qfalse;
00608     } //end for
00609     for (i = 0; i < 2; i++)
00610     {
00611         if (DotProduct(aasworld.vertexes[edge2->v[i]], normal1) - dist1 < -0.01) return qfalse;
00612     } //end for
00613     return qtrue;
00614 } //end of the function AAS_NonConvexEdges
00615 //===========================================================================
00616 //
00617 // Parameter:               -
00618 // Returns:                 -
00619 // Changes Globals:     -
00620 //===========================================================================
00621 qboolean AAS_CanMergeFaces(int *facenums, int numfaces, int planenum)
00622 {
00623     int i, j, s, edgenum1, edgenum2, side1, side2, en1, en2, ens;
00624     aas_face_t *face1, *face2, *otherface;
00625     aas_edge_t *edge1, *edge2;
00626 
00627     for (i = 0; i < numfaces; i++)
00628     {
00629         face1 = &aasworld.faces[facenums[i]];
00630         for (en1 = 0; en1 < face1->numedges; en1++)
00631         {
00632             edgenum1 = aasworld.edgeindex[face1->firstedge + en1];
00633             side1 = (edgenum1 < 0) ^ (face1->planenum != planenum);
00634             edgenum1 = abs(edgenum1);
00635             edge1 = &aasworld.edges[edgenum1];
00636             //check if the edge is shared with another face
00637             for (s = 0; s < numfaces; s++)
00638             {
00639                 if (s == i) continue;
00640                 otherface = &aasworld.faces[facenums[s]];
00641                 for (ens = 0; ens < otherface->numedges; ens++)
00642                 {
00643                     if (edgenum1 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break;
00644                 } //end for
00645                 if (ens != otherface->numedges) break;
00646             } //end for
00647             //if the edge was shared
00648             if (s != numfaces) continue;
00649             //
00650             for (j = 0; j < numfaces; j++)
00651             {
00652                 if (j == i) continue;
00653                 face2 = &aasworld.faces[facenums[j]];
00654                 for (en2 = 0; en2 < face2->numedges; en2++)
00655                 {
00656                     edgenum2 = aasworld.edgeindex[face2->firstedge + en2];
00657                     side2 = (edgenum2 < 0) ^ (face2->planenum != planenum);
00658                     edgenum2 = abs(edgenum2);
00659                     edge2 = &aasworld.edges[edgenum2];
00660                     //check if the edge is shared with another face
00661                     for (s = 0; s < numfaces; s++)
00662                     {
00663                         if (s == i) continue;
00664                         otherface = &aasworld.faces[facenums[s]];
00665                         for (ens = 0; ens < otherface->numedges; ens++)
00666                         {
00667                             if (edgenum2 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break;
00668                         } //end for
00669                         if (ens != otherface->numedges) break;
00670                     } //end for
00671                     //if the edge was shared
00672                     if (s != numfaces) continue;
00673                     //
00674                     if (AAS_NonConvexEdges(edge1, edge2, side1, side2, planenum)) return qfalse;
00675                 } //end for
00676             } //end for
00677         } //end for
00678     } //end for
00679     return qtrue;
00680 } //end of the function AAS_CanMergeFaces*/
00681 //===========================================================================
00682 //
00683 // Parameter:               -
00684 // Returns:                 -
00685 // Changes Globals:     -
00686 //===========================================================================
00687 void AAS_ConnectedAreas_r(int *areanums, int numareas, int *connectedareas, int curarea)
00688 {
00689     int i, j, otherareanum, facenum;
00690     aas_area_t *area;
00691     aas_face_t *face;
00692 
00693     connectedareas[curarea] = qtrue;
00694     area = &aasworld.areas[areanums[curarea]];
00695     for (i = 0; i < area->numfaces; i++)
00696     {
00697         facenum = abs(aasworld.faceindex[area->firstface + i]);
00698         face = &aasworld.faces[facenum];
00699         //if the face is solid
00700         if (face->faceflags & FACE_SOLID) continue;
00701         //get the area at the other side of the face
00702         if (face->frontarea != areanums[curarea]) otherareanum = face->frontarea;
00703         else otherareanum = face->backarea;
00704         //check if the face is leading to one of the other areas
00705         for (j = 0; j < numareas; j++)
00706         {
00707             if (areanums[j] == otherareanum) break;
00708         } //end for
00709         //if the face isn't leading to one of the other areas
00710         if (j == numareas) continue;
00711         //if the other area is already connected
00712         if (connectedareas[j]) continue;
00713         //recursively proceed with the other area
00714         AAS_ConnectedAreas_r(areanums, numareas, connectedareas, j);
00715     } //end for
00716 } //end of the function AAS_ConnectedAreas_r
00717 //===========================================================================
00718 //
00719 // Parameter:               -
00720 // Returns:                 -
00721 // Changes Globals:     -
00722 //===========================================================================
00723 qboolean AAS_ConnectedAreas(int *areanums, int numareas)
00724 {
00725     int connectedareas[MAX_PORTALAREAS], i;
00726 
00727     Com_Memset(connectedareas, 0, sizeof(connectedareas));
00728     if (numareas < 1) return qfalse;
00729     if (numareas == 1) return qtrue;
00730     AAS_ConnectedAreas_r(areanums, numareas, connectedareas, 0);
00731     for (i = 0; i < numareas; i++)
00732     {
00733         if (!connectedareas[i]) return qfalse;
00734     } //end for
00735     return qtrue;
00736 } //end of the function AAS_ConnectedAreas
00737 //===========================================================================
00738 // gets adjacent areas with less presence types recursively
00739 //
00740 // Parameter:               -
00741 // Returns:                 -
00742 // Changes Globals:     -
00743 //===========================================================================
00744 int AAS_GetAdjacentAreasWithLessPresenceTypes_r(int *areanums, int numareas, int curareanum)
00745 {
00746     int i, j, presencetype, otherpresencetype, otherareanum, facenum;
00747     aas_area_t *area;
00748     aas_face_t *face;
00749 
00750     areanums[numareas++] = curareanum;
00751     area = &aasworld.areas[curareanum];
00752     presencetype = aasworld.areasettings[curareanum].presencetype;
00753     for (i = 0; i < area->numfaces; i++)
00754     {
00755         facenum = abs(aasworld.faceindex[area->firstface + i]);
00756         face = &aasworld.faces[facenum];
00757         //if the face is solid
00758         if (face->faceflags & FACE_SOLID) continue;
00759         //the area at the other side of the face
00760         if (face->frontarea != curareanum) otherareanum = face->frontarea;
00761         else otherareanum = face->backarea;
00762         //
00763         otherpresencetype = aasworld.areasettings[otherareanum].presencetype;
00764         //if the other area has less presence types
00765         if ((presencetype & ~otherpresencetype) &&
00766                 !(otherpresencetype & ~presencetype))
00767         {
00768             //check if the other area isn't already in the list
00769             for (j = 0; j < numareas; j++)
00770             {
00771                 if (otherareanum == areanums[j]) break;
00772             } //end for
00773             //if the other area isn't already in the list
00774             if (j == numareas)
00775             {
00776                 if (numareas >= MAX_PORTALAREAS)
00777                 {
00778                     AAS_Error("MAX_PORTALAREAS");
00779                     return numareas;
00780                 } //end if
00781                 numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, numareas, otherareanum);
00782             } //end if
00783         } //end if
00784     } //end for
00785     return numareas;
00786 } //end of the function AAS_GetAdjacentAreasWithLessPresenceTypes_r
00787 //===========================================================================
00788 //
00789 // Parameter:               -
00790 // Returns:                 -
00791 // Changes Globals:     -
00792 //===========================================================================
00793 int AAS_CheckAreaForPossiblePortals(int areanum)
00794 {
00795     int i, j, k, fen, ben, frontedgenum, backedgenum, facenum;
00796     int areanums[MAX_PORTALAREAS], numareas, otherareanum;
00797     int numareafrontfaces[MAX_PORTALAREAS], numareabackfaces[MAX_PORTALAREAS];
00798     int frontfacenums[MAX_PORTALAREAS], backfacenums[MAX_PORTALAREAS];
00799     int numfrontfaces, numbackfaces;
00800     int frontareanums[MAX_PORTALAREAS], backareanums[MAX_PORTALAREAS];
00801     int numfrontareas, numbackareas;
00802     int frontplanenum, backplanenum, faceplanenum;
00803     aas_area_t *area;
00804     aas_face_t *frontface, *backface, *face;
00805 
00806     //if it isn't already a portal
00807     if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0;
00808     //it must be a grounded area
00809     if (!(aasworld.areasettings[areanum].areaflags & AREA_GROUNDED)) return 0;
00810     //
00811     Com_Memset(numareafrontfaces, 0, sizeof(numareafrontfaces));
00812     Com_Memset(numareabackfaces, 0, sizeof(numareabackfaces));
00813     numareas = numfrontfaces = numbackfaces = 0;
00814     numfrontareas = numbackareas = 0;
00815     frontplanenum = backplanenum = -1;
00816     //add any adjacent areas with less presence types
00817     numareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, 0, areanum);
00818     //
00819     for (i = 0; i < numareas; i++)
00820     {
00821         area = &aasworld.areas[areanums[i]];
00822         for (j = 0; j < area->numfaces; j++)
00823         {
00824             facenum = abs(aasworld.faceindex[area->firstface + j]);
00825             face = &aasworld.faces[facenum];
00826             //if the face is solid
00827             if (face->faceflags & FACE_SOLID) continue;
00828             //check if the face is shared with one of the other areas
00829             for (k = 0; k < numareas; k++)
00830             {
00831                 if (k == i) continue;
00832                 if (face->frontarea == areanums[k] || face->backarea == areanums[k]) break;
00833             } //end for
00834             //if the face is shared
00835             if (k != numareas) continue;
00836             //the number of the area at the other side of the face
00837             if (face->frontarea == areanums[i]) otherareanum = face->backarea;
00838             else otherareanum = face->frontarea;
00839             //if the other area already is a cluter portal
00840             if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0;
00841             //number of the plane of the area
00842             faceplanenum = face->planenum & ~1;
00843             //
00844             if (frontplanenum < 0 || faceplanenum == frontplanenum)
00845             {
00846                 frontplanenum = faceplanenum;
00847                 frontfacenums[numfrontfaces++] = facenum;
00848                 for (k = 0; k < numfrontareas; k++)
00849                 {
00850                     if (frontareanums[k] == otherareanum) break;
00851                 } //end for
00852                 if (k == numfrontareas) frontareanums[numfrontareas++] = otherareanum;
00853                 numareafrontfaces[i]++;
00854             } //end if
00855             else if (backplanenum < 0 || faceplanenum == backplanenum)
00856             {
00857                 backplanenum = faceplanenum;
00858                 backfacenums[numbackfaces++] = facenum;
00859                 for (k = 0; k < numbackareas; k++)
00860                 {
00861                     if (backareanums[k] == otherareanum) break;
00862                 } //end for
00863                 if (k == numbackareas) backareanums[numbackareas++] = otherareanum;
00864                 numareabackfaces[i]++;
00865             } //end else
00866             else
00867             {
00868                 return 0;
00869             } //end else
00870         } //end for
00871     } //end for
00872     //every area should have at least one front face and one back face
00873     for (i = 0; i < numareas; i++)
00874     {
00875         if (!numareafrontfaces[i] || !numareabackfaces[i]) return 0;
00876     } //end for
00877     //the front areas should all be connected
00878     if (!AAS_ConnectedAreas(frontareanums, numfrontareas)) return 0;
00879     //the back areas should all be connected
00880     if (!AAS_ConnectedAreas(backareanums, numbackareas)) return 0;
00881     //none of the front faces should have a shared edge with a back face
00882     for (i = 0; i < numfrontfaces; i++)
00883     {
00884         frontface = &aasworld.faces[frontfacenums[i]];
00885         for (fen = 0; fen < frontface->numedges; fen++)
00886         {
00887             frontedgenum = abs(aasworld.edgeindex[frontface->firstedge + fen]);
00888             for (j = 0; j < numbackfaces; j++)
00889             {
00890                 backface = &aasworld.faces[backfacenums[j]];
00891                 for (ben = 0; ben < backface->numedges; ben++)
00892                 {
00893                     backedgenum = abs(aasworld.edgeindex[backface->firstedge + ben]);
00894                     if (frontedgenum == backedgenum) break;
00895                 } //end for
00896                 if (ben != backface->numedges) break;
00897             } //end for
00898             if (j != numbackfaces) break;
00899         } //end for
00900         if (fen != frontface->numedges) break;
00901     } //end for
00902     if (i != numfrontfaces) return 0;
00903     //set the cluster portal contents
00904     for (i = 0; i < numareas; i++)
00905     {
00906         aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_CLUSTERPORTAL;
00907         //this area can be used as a route portal
00908         aasworld.areasettings[areanums[i]].contents |= AREACONTENTS_ROUTEPORTAL;
00909         Log_Write("possible portal: %d\r\n", areanums[i]);
00910     } //end for
00911     //
00912     return numareas;
00913 } //end of the function AAS_CheckAreaForPossiblePortals
00914 //===========================================================================
00915 //
00916 // Parameter:               -
00917 // Returns:                 -
00918 // Changes Globals:     -
00919 //===========================================================================
00920 void AAS_FindPossiblePortals(void)
00921 {
00922     int i, numpossibleportals;
00923 
00924     numpossibleportals = 0;
00925     for (i = 1; i < aasworld.numareas; i++)
00926     {
00927         numpossibleportals += AAS_CheckAreaForPossiblePortals(i);
00928     } //end for
00929     botimport.Print(PRT_MESSAGE, "\r%6d possible portal areas\n", numpossibleportals);
00930 } //end of the function AAS_FindPossiblePortals
00931 //===========================================================================
00932 //
00933 // Parameter:               -
00934 // Returns:                 -
00935 // Changes Globals:     -
00936 //===========================================================================
00937 void AAS_RemoveAllPortals(void)
00938 {
00939     int i;
00940 
00941     for (i = 1; i < aasworld.numareas; i++)
00942     {
00943         aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
00944     } //end for
00945 } //end of the function AAS_RemoveAllPortals
00946 
00947 #if 0
00948 //===========================================================================
00949 //
00950 // Parameter:               -
00951 // Returns:                 -
00952 // Changes Globals:     -
00953 //===========================================================================
00954 void AAS_FloodCluster_r(int areanum, int clusternum)
00955 {
00956     int i, otherareanum;
00957     aas_face_t *face;
00958     aas_area_t *area;
00959 
00960     //set cluster mark
00961     aasworld.areasettings[areanum].cluster = clusternum;
00962     //if the area is a portal
00963     //if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return;
00964     //
00965     area = &aasworld.areas[areanum];
00966     //use area faces to flood into adjacent areas
00967     for (i = 0; i < area->numfaces; i++)
00968     {
00969         face = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])];
00970         //
00971         if (face->frontarea != areanum) otherareanum = face->frontarea;
00972         else otherareanum = face->backarea;
00973         //if there's no area at the other side
00974         if (!otherareanum) continue;
00975         //if the area is a portal
00976         if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;
00977         //if the area is already marked
00978         if (aasworld.areasettings[otherareanum].cluster) continue;
00979         //
00980         AAS_FloodCluster_r(otherareanum, clusternum);
00981     } //end for
00982     //use the reachabilities to flood into other areas
00983     for (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++)
00984     {
00985         otherareanum = aasworld.reachability[
00986                     aasworld.areasettings[areanum].firstreachablearea + i].areanum;
00987         if (!otherareanum)
00988         {
00989             continue;
00990             AAS_Error("reachability %d has zero area\n", aasworld.areasettings[areanum].firstreachablearea + i);
00991         } //end if
00992         //if the area is a portal
00993         if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;
00994         //if the area is already marked
00995         if (aasworld.areasettings[otherareanum].cluster) continue;
00996         //
00997         AAS_FloodCluster_r(otherareanum, clusternum);
00998     } //end for
00999 } //end of the function AAS_FloodCluster_r
01000 //===========================================================================
01001 //
01002 // Parameter:               -
01003 // Returns:                 -
01004 // Changes Globals:     -
01005 //===========================================================================
01006 void AAS_RemoveTeleporterPortals(void)
01007 {
01008     int i, j, areanum;
01009 
01010     for (i = 1; i < aasworld.numareas; i++)
01011     {
01012         for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)
01013         {
01014             areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum;
01015             if (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype == TRAVEL_TELEPORT)
01016             {
01017                 aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
01018                 aasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
01019                 break;
01020             } //end if
01021         } //end for
01022     } //end for
01023 } //end of the function AAS_RemoveTeleporterPortals
01024 //===========================================================================
01025 //
01026 // Parameter:               -
01027 // Returns:                 -
01028 // Changes Globals:     -
01029 //===========================================================================
01030 void AAS_FloodClusterReachabilities(int clusternum)
01031 {
01032     int i, j, areanum;
01033 
01034     for (i = 1; i < aasworld.numareas; i++)
01035     {
01036         //if this area already has a cluster set
01037         if (aasworld.areasettings[i].cluster) continue;
01038         //if this area is a cluster portal
01039         if (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) continue;
01040         //loop over the reachable areas from this area
01041         for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)
01042         {
01043             //the reachable area
01044             areanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum;
01045             //if this area is a cluster portal
01046             if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;
01047             //if this area has a cluster set
01048             if (aasworld.areasettings[areanum].cluster == clusternum)
01049             {
01050                 AAS_FloodCluster_r(i, clusternum);
01051                 i = 0;
01052                 break;
01053             } //end if
01054         } //end for
01055     } //end for
01056 } //end of the function AAS_FloodClusterReachabilities
01057 
01058 //===========================================================================
01059 //
01060 // Parameter:               -
01061 // Returns:                 -
01062 // Changes Globals:     -
01063 //===========================================================================
01064 void AAS_RemoveNotClusterClosingPortals(void)
01065 {
01066     int i, j, k, facenum, otherareanum, nonclosingportals;
01067     aas_area_t *area;
01068     aas_face_t *face;
01069 
01070     AAS_RemoveTeleporterPortals();
01071     //
01072     nonclosingportals = 0;
01073     for (i = 1; i < aasworld.numareas; i++)
01074     {
01075         if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue;
01076         //find a non-portal area adjacent to the portal area and flood
01077         //the cluster from there
01078         area = &aasworld.areas[i];
01079         for (j = 0; j < area->numfaces; j++)
01080         {
01081             facenum = abs(aasworld.faceindex[area->firstface + j]);
01082             face = &aasworld.faces[facenum];
01083             //
01084             if (face->frontarea != i) otherareanum = face->frontarea;
01085             else otherareanum = face->backarea;
01086             //
01087             if (!otherareanum) continue;
01088             //
01089             if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL)
01090             {
01091                 continue;
01092             } //end if
01093             //reset all cluster fields
01094             AAS_RemoveClusterAreas();
01095             //
01096             AAS_FloodCluster_r(otherareanum, 1);
01097             AAS_FloodClusterReachabilities(1);
01098             //check if all adjacent non-portal areas have a cluster set
01099             for (k = 0; k < area->numfaces; k++)
01100             {
01101                 facenum = abs(aasworld.faceindex[area->firstface + k]);
01102                 face = &aasworld.faces[facenum];
01103                 //
01104                 if (face->frontarea != i) otherareanum = face->frontarea;
01105                 else otherareanum = face->backarea;
01106                 //
01107                 if (!otherareanum) continue;
01108                 //
01109                 if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL)
01110                 {
01111                     continue;
01112                 } //end if
01113                 //
01114                 if (!aasworld.areasettings[otherareanum].cluster) break;
01115             } //end for
01116             //if all adjacent non-portal areas have a cluster set then the portal
01117             //didn't seal a cluster
01118             if (k >= area->numfaces)
01119             {
01120                 aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
01121                 nonclosingportals++;
01122                 //recheck all the other portals again
01123                 i = 0;
01124                 break;
01125             } //end if
01126         } //end for
01127     } //end for
01128     botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals);
01129 } //end of the function AAS_RemoveNotClusterClosingPortals
01130 
01131 //===========================================================================
01132 //
01133 // Parameter:               -
01134 // Returns:                 -
01135 // Changes Globals:     -
01136 //===========================================================================
01137 
01138 void AAS_RemoveNotClusterClosingPortals(void)
01139 {
01140     int i, j, facenum, otherareanum, nonclosingportals, numseperatedclusters;
01141     aas_area_t *area;
01142     aas_face_t *face;
01143 
01144     AAS_RemoveTeleporterPortals();
01145     //
01146     nonclosingportals = 0;
01147     for (i = 1; i < aasworld.numareas; i++)
01148     {
01149         if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue;
01150         //
01151         numseperatedclusters = 0;
01152         //reset all cluster fields
01153         AAS_RemoveClusterAreas();
01154         //find a non-portal area adjacent to the portal area and flood
01155         //the cluster from there
01156         area = &aasworld.areas[i];
01157         for (j = 0; j < area->numfaces; j++)
01158         {
01159             facenum = abs(aasworld.faceindex[area->firstface + j]);
01160             face = &aasworld.faces[facenum];
01161             //
01162             if (face->frontarea != i) otherareanum = face->frontarea;
01163             else otherareanum = face->backarea;
01164             //if not solid at the other side of the face
01165             if (!otherareanum) continue;
01166             //don't flood into other portals
01167             if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;
01168             //if the area already has a cluster set
01169             if (aasworld.areasettings[otherareanum].cluster) continue;
01170             //another cluster is seperated by this portal
01171             numseperatedclusters++;
01172             //flood the cluster
01173             AAS_FloodCluster_r(otherareanum, numseperatedclusters);
01174             AAS_FloodClusterReachabilities(numseperatedclusters);
01175         } //end for
01176         //use the reachabilities to flood into other areas
01177         for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)
01178         {
01179             otherareanum = aasworld.reachability[
01180                         aasworld.areasettings[i].firstreachablearea + j].areanum;
01181             //this should never be qtrue but we check anyway
01182             if (!otherareanum) continue;
01183             //don't flood into other portals
01184             if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;
01185             //if the area already has a cluster set
01186             if (aasworld.areasettings[otherareanum].cluster) continue;
01187             //another cluster is seperated by this portal
01188             numseperatedclusters++;
01189             //flood the cluster
01190             AAS_FloodCluster_r(otherareanum, numseperatedclusters);
01191             AAS_FloodClusterReachabilities(numseperatedclusters);
01192         } //end for
01193         //a portal must seperate no more and no less than 2 clusters
01194         if (numseperatedclusters != 2)
01195         {
01196             aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;
01197             nonclosingportals++;
01198             //recheck all the other portals again
01199             i = 0;
01200         } //end if