mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-12-02 17:22:32 +00:00
736ec20d4d
Don't include the lazy precompiled.h everywhere, only what's required for the compilation unit. platform.h needs to be included instead to provide all essential defines and types. All includes use the relative path to the neo or the game specific root. Move all idlib related includes from idlib/Lib.h to precompiled.h. precompiled.h still exists for the MFC stuff in tools/. Add some missing header guards.
554 lines
14 KiB
C++
554 lines
14 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
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.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "sys/platform.h"
|
|
#include "tools/compilers/aas/AASFile_local.h"
|
|
|
|
#include "tools/compilers/aas/AASCluster.h"
|
|
|
|
/*
|
|
================
|
|
idAASCluster::UpdatePortal
|
|
================
|
|
*/
|
|
bool idAASCluster::UpdatePortal( int areaNum, int clusterNum ) {
|
|
int portalNum;
|
|
aasPortal_t *portal;
|
|
|
|
// find the portal for this area
|
|
for ( portalNum = 1; portalNum < file->portals.Num(); portalNum++ ) {
|
|
if ( file->portals[portalNum].areaNum == areaNum ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( portalNum >= file->portals.Num() ) {
|
|
common->Error( "no portal for area %d", areaNum );
|
|
return true;
|
|
}
|
|
|
|
portal = &file->portals[portalNum];
|
|
|
|
// if the portal is already fully updated
|
|
if ( portal->clusters[0] == clusterNum ) {
|
|
return true;
|
|
}
|
|
if ( portal->clusters[1] == clusterNum ) {
|
|
return true;
|
|
}
|
|
// if the portal has no front cluster yet
|
|
if ( !portal->clusters[0] ) {
|
|
portal->clusters[0] = clusterNum;
|
|
}
|
|
// if the portal has no back cluster yet
|
|
else if ( !portal->clusters[1] )
|
|
{
|
|
portal->clusters[1] = clusterNum;
|
|
}
|
|
else
|
|
{
|
|
// remove the cluster portal flag contents
|
|
file->areas[areaNum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
|
|
return false;
|
|
}
|
|
|
|
// set the area cluster number to the negative portal number
|
|
file->areas[areaNum].cluster = -portalNum;
|
|
|
|
// add the portal to the cluster using the portal index
|
|
file->portalIndex.Append( portalNum );
|
|
file->clusters[clusterNum].numPortals++;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAASCluster::FloodClusterAreas_r
|
|
================
|
|
*/
|
|
bool idAASCluster::FloodClusterAreas_r( int areaNum, int clusterNum ) {
|
|
aasArea_t *area;
|
|
aasFace_t *face;
|
|
int faceNum, i;
|
|
idReachability *reach;
|
|
|
|
area = &file->areas[areaNum];
|
|
|
|
// if the area is already part of a cluster
|
|
if ( area->cluster > 0 ) {
|
|
if ( area->cluster == clusterNum ) {
|
|
return true;
|
|
}
|
|
// there's a reachability going from one cluster to another only in one direction
|
|
common->Error( "cluster %d touched cluster %d at area %d\r\n", clusterNum, file->areas[areaNum].cluster, areaNum );
|
|
return false;
|
|
}
|
|
|
|
// if this area is a cluster portal
|
|
if ( area->contents & AREACONTENTS_CLUSTERPORTAL ) {
|
|
return UpdatePortal( areaNum, clusterNum );
|
|
}
|
|
|
|
// set the area cluster number
|
|
area->cluster = clusterNum;
|
|
|
|
if ( !noFaceFlood ) {
|
|
// use area faces to flood into adjacent areas
|
|
for ( i = 0; i < area->numFaces; i++ ) {
|
|
faceNum = abs(file->faceIndex[area->firstFace + i]);
|
|
face = &file->faces[faceNum];
|
|
if ( face->areas[0] == areaNum ) {
|
|
if ( face->areas[1] ) {
|
|
if ( !FloodClusterAreas_r( face->areas[1], clusterNum ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ( face->areas[0] ) {
|
|
if ( !FloodClusterAreas_r( face->areas[0], clusterNum ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// use the reachabilities to flood into other areas
|
|
for ( reach = file->areas[areaNum].reach; reach; reach = reach->next ) {
|
|
if ( !FloodClusterAreas_r( reach->toAreaNum, clusterNum) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// use the reversed reachabilities to flood into other areas
|
|
for ( reach = file->areas[areaNum].rev_reach; reach; reach = reach->rev_next ) {
|
|
if ( !FloodClusterAreas_r( reach->fromAreaNum, clusterNum) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAASCluster::RemoveAreaClusterNumbers
|
|
================
|
|
*/
|
|
void idAASCluster::RemoveAreaClusterNumbers( void ) {
|
|
int i;
|
|
|
|
for ( i = 1; i < file->areas.Num(); i++ ) {
|
|
file->areas[i].cluster = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAASCluster::NumberClusterAreas
|
|
================
|
|
*/
|
|
void idAASCluster::NumberClusterAreas( int clusterNum ) {
|
|
int i, portalNum;
|
|
aasCluster_t *cluster;
|
|
aasPortal_t *portal;
|
|
|
|
cluster = &file->clusters[clusterNum];
|
|
cluster->numAreas = 0;
|
|
cluster->numReachableAreas = 0;
|
|
|
|
// number all areas in this cluster WITH reachabilities
|
|
for ( i = 1; i < file->areas.Num(); i++ ) {
|
|
|
|
if ( file->areas[i].cluster != clusterNum ) {
|
|
continue;
|
|
}
|
|
|
|
if ( !(file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) {
|
|
continue;
|
|
}
|
|
|
|
file->areas[i].clusterAreaNum = cluster->numAreas++;
|
|
cluster->numReachableAreas++;
|
|
}
|
|
|
|
// number all portals in this cluster WITH reachabilities
|
|
for ( i = 0; i < cluster->numPortals; i++ ) {
|
|
portalNum = file->portalIndex[cluster->firstPortal + i];
|
|
portal = &file->portals[portalNum];
|
|
|
|
if ( !(file->areas[portal->areaNum].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( portal->clusters[0] == clusterNum ) {
|
|
portal->clusterAreaNum[0] = cluster->numAreas++;
|
|
}
|
|
else {
|
|
portal->clusterAreaNum[1] = cluster->numAreas++;
|
|
}
|
|
cluster->numReachableAreas++;
|
|
}
|
|
|
|
// number all areas in this cluster WITHOUT reachabilities
|
|
for ( i = 1; i < file->areas.Num(); i++ ) {
|
|
|
|
if ( file->areas[i].cluster != clusterNum ) {
|
|
continue;
|
|
}
|
|
|
|
if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
|
|
continue;
|
|
}
|
|
|
|
file->areas[i].clusterAreaNum = cluster->numAreas++;
|
|
}
|
|
|
|
// number all portals in this cluster WITHOUT reachabilities
|
|
for ( i = 0; i < cluster->numPortals; i++ ) {
|
|
portalNum = file->portalIndex[cluster->firstPortal + i];
|
|
portal = &file->portals[portalNum];
|
|
|
|
if ( file->areas[portal->areaNum].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( portal->clusters[0] == clusterNum ) {
|
|
portal->clusterAreaNum[0] = cluster->numAreas++;
|
|
}
|
|
else {
|
|
portal->clusterAreaNum[1] = cluster->numAreas++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAASCluster::FindClusters
|
|
================
|
|
*/
|
|
bool idAASCluster::FindClusters( void ) {
|
|
int i, clusterNum;
|
|
aasCluster_t cluster;
|
|
|
|
RemoveAreaClusterNumbers();
|
|
|
|
for ( i = 1; i < file->areas.Num(); i++ ) {
|
|
// if the area is already part of a cluster
|
|
if ( file->areas[i].cluster ) {
|
|
continue;
|
|
}
|
|
|
|
// if not flooding through faces only use areas that have reachabilities
|
|
if ( noFaceFlood ) {
|
|
if ( !(file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// if the area is a cluster portal
|
|
if ( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
|
|
continue;
|
|
}
|
|
|
|
cluster.numAreas = 0;
|
|
cluster.numReachableAreas = 0;
|
|
cluster.firstPortal = file->portalIndex.Num();
|
|
cluster.numPortals = 0;
|
|
clusterNum = file->clusters.Num();
|
|
file->clusters.Append( cluster );
|
|
|
|
// flood the areas in this cluster
|
|
if ( !FloodClusterAreas_r( i, clusterNum ) ) {
|
|
return false;
|
|
}
|
|
|
|
// number the cluster areas
|
|
NumberClusterAreas( clusterNum );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAASCluster::CreatePortals
|
|
================
|
|
*/
|
|
void idAASCluster::CreatePortals( void ) {
|
|
int i;
|
|
aasPortal_t portal;
|
|
|
|
for ( i = 1; i < file->areas.Num(); i++ ) {
|
|
// if the area is a cluster portal
|
|
if ( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
|
|
portal.areaNum = i;
|
|
portal.clusters[0] = portal.clusters[1] = 0;
|
|
portal.maxAreaTravelTime = 0;
|
|
file->portals.Append( portal );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAASCluster::TestPortals
|
|
================
|
|
*/
|
|
bool idAASCluster::TestPortals( void ) {
|
|
int i;
|
|
aasPortal_t *portal, *portal2;
|
|
aasArea_t *area, *area2;
|
|
idReachability *reach;
|
|
bool ok;
|
|
|
|
ok = true;
|
|
for ( i = 1; i < file->portals.Num(); i++ ) {
|
|
portal = &file->portals[i];
|
|
area = &file->areas[portal->areaNum];
|
|
|
|
// if this portal was already removed
|
|
if ( !( area->contents & AREACONTENTS_CLUSTERPORTAL) ) {
|
|
continue;
|
|
}
|
|
|
|
// may not removed this portal if it has a reachability to a removed portal
|
|
for ( reach = area->reach; reach; reach = reach->next ) {
|
|
area2 = &file->areas[ reach->toAreaNum ];
|
|
if ( area2->contents & AREACONTENTS_CLUSTERPORTAL ) {
|
|
continue;
|
|
}
|
|
if ( area2->cluster < 0 ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( reach ) {
|
|
continue;
|
|
}
|
|
|
|
// may not removed this portal if it has a reversed reachability to a removed portal
|
|
for ( reach = area->rev_reach; reach; reach = reach->rev_next ) {
|
|
area2 = &file->areas[ reach->toAreaNum ];
|
|
if ( area2->contents & AREACONTENTS_CLUSTERPORTAL ) {
|
|
continue;
|
|
}
|
|
if ( area2->cluster < 0 ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( reach ) {
|
|
continue;
|
|
}
|
|
|
|
// portal should have two clusters set
|
|
if ( !portal->clusters[0] ) {
|
|
area->contents &= ~AREACONTENTS_CLUSTERPORTAL;
|
|
ok = false;
|
|
continue;
|
|
}
|
|
if ( !portal->clusters[1] ) {
|
|
area->contents &= ~AREACONTENTS_CLUSTERPORTAL;
|
|
ok = false;
|
|
continue;
|
|
}
|
|
|
|
// this portal may not have reachabilities to a portal that doesn't seperate the same clusters
|
|
for ( reach = area->reach; reach; reach = reach->next ) {
|
|
area2 = &file->areas[ reach->toAreaNum ];
|
|
|
|
if ( !(area2->contents & AREACONTENTS_CLUSTERPORTAL) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( area2->cluster > 0 ) {
|
|
area2->contents &= ~AREACONTENTS_CLUSTERPORTAL;
|
|
ok = false;
|
|
continue;
|
|
}
|
|
|
|
portal2 = &file->portals[ -file->areas[ reach->toAreaNum ].cluster ];
|
|
|
|
if ( ( portal2->clusters[0] != portal->clusters[0] && portal2->clusters[0] != portal->clusters[1] ) ||
|
|
( portal2->clusters[1] != portal->clusters[0] && portal2->clusters[1] != portal->clusters[1] ) ) {
|
|
area2->contents &= ~AREACONTENTS_CLUSTERPORTAL;
|
|
ok = false;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAASCluster::RemoveInvalidPortals
|
|
================
|
|
*/
|
|
void idAASCluster::RemoveInvalidPortals( void ) {
|
|
int i, j, k, face1Num, face2Num, otherAreaNum, numOpenAreas, numInvalidPortals;
|
|
aasFace_t *face1, *face2;
|
|
|
|
numInvalidPortals = 0;
|
|
for ( i = 0; i < file->areas.Num(); i++ ) {
|
|
if ( !( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) ) {
|
|
continue;
|
|
}
|
|
|
|
numOpenAreas = 0;
|
|
for ( j = 0; j < file->areas[i].numFaces; j++ ) {
|
|
face1Num = file->faceIndex[ file->areas[i].firstFace + j ];
|
|
face1 = &file->faces[ abs(face1Num) ];
|
|
otherAreaNum = face1->areas[ face1Num < 0 ];
|
|
|
|
if ( !otherAreaNum ) {
|
|
continue;
|
|
}
|
|
|
|
for ( k = 0; k < j; k++ ) {
|
|
face2Num = file->faceIndex[ file->areas[i].firstFace + k ];
|
|
face2 = &file->faces[ abs(face2Num) ];
|
|
if ( otherAreaNum == face2->areas[ face2Num < 0 ] ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( k < j ) {
|
|
continue;
|
|
}
|
|
|
|
if ( !( file->areas[otherAreaNum].contents & AREACONTENTS_CLUSTERPORTAL ) ) {
|
|
numOpenAreas++;
|
|
}
|
|
}
|
|
|
|
if ( numOpenAreas <= 1 ) {
|
|
file->areas[i].contents &= AREACONTENTS_CLUSTERPORTAL;
|
|
numInvalidPortals++;
|
|
}
|
|
}
|
|
|
|
common->Printf( "\r%6d invalid portals removed\n", numInvalidPortals );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAASCluster::Build
|
|
================
|
|
*/
|
|
bool idAASCluster::Build( idAASFileLocal *file ) {
|
|
|
|
common->Printf( "[Clustering]\n" );
|
|
|
|
this->file = file;
|
|
this->noFaceFlood = true;
|
|
|
|
RemoveInvalidPortals();
|
|
|
|
while( 1 ) {
|
|
|
|
// delete all existing clusters
|
|
file->DeleteClusters();
|
|
|
|
// create the portals from the portal areas
|
|
CreatePortals();
|
|
|
|
common->Printf( "\r%6d", file->portals.Num() );
|
|
|
|
// find the clusters
|
|
if ( !FindClusters() ) {
|
|
continue;
|
|
}
|
|
|
|
// test the portals
|
|
if ( !TestPortals() ) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
common->Printf( "\r%6d portals\n", file->portals.Num() );
|
|
common->Printf( "%6d clusters\n", file->clusters.Num() );
|
|
|
|
for ( int i = 0; i < file->clusters.Num(); i++ ) {
|
|
common->Printf( "%6d reachable areas in cluster %d\n", file->clusters[i].numReachableAreas, i );
|
|
}
|
|
|
|
file->ReportRoutingEfficiency();
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAASCluster::BuildSingleCluster
|
|
================
|
|
*/
|
|
bool idAASCluster::BuildSingleCluster( idAASFileLocal *file ) {
|
|
int i, numAreas;
|
|
aasCluster_t cluster;
|
|
|
|
common->Printf( "[Clustering]\n" );
|
|
|
|
this->file = file;
|
|
|
|
// delete all existing clusters
|
|
file->DeleteClusters();
|
|
|
|
cluster.firstPortal = 0;
|
|
cluster.numPortals = 0;
|
|
cluster.numAreas = file->areas.Num();
|
|
cluster.numReachableAreas = 0;
|
|
// give all reachable areas in the cluster a number
|
|
for ( i = 0; i < file->areas.Num(); i++ ) {
|
|
file->areas[i].cluster = file->clusters.Num();
|
|
if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
|
|
file->areas[i].clusterAreaNum = cluster.numReachableAreas++;
|
|
}
|
|
}
|
|
// give the remaining areas a number within the cluster
|
|
numAreas = cluster.numReachableAreas;
|
|
for ( i = 0; i < file->areas.Num(); i++ ) {
|
|
if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
|
|
continue;
|
|
}
|
|
file->areas[i].clusterAreaNum = numAreas++;
|
|
}
|
|
file->clusters.Append( cluster );
|
|
|
|
common->Printf( "%6d portals\n", file->portals.Num() );
|
|
common->Printf( "%6d clusters\n", file->clusters.Num() );
|
|
|
|
for ( i = 0; i < file->clusters.Num(); i++ ) {
|
|
common->Printf( "%6d reachable areas in cluster %d\n", file->clusters[i].numReachableAreas, i );
|
|
}
|
|
|
|
file->ReportRoutingEfficiency();
|
|
|
|
return true;
|
|
}
|