hello world

This commit is contained in:
Timothee 'TTimo' Besset
2011-11-22 15:28:15 -06:00
commit fb1609f554
2155 changed files with 1017022 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,499 @@
/*
===========================================================================
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 "../../../idlib/precompiled.h"
#pragma hdrstop
#include "AASBuild_local.h"
#define VERTEX_HASH_BOXSIZE (1<<6) // must be power of 2
#define VERTEX_HASH_SIZE (VERTEX_HASH_BOXSIZE*VERTEX_HASH_BOXSIZE)
#define EDGE_HASH_SIZE (1<<14)
#define INTEGRAL_EPSILON 0.01f
#define VERTEX_EPSILON 0.1f
#define AAS_PLANE_NORMAL_EPSILON 0.00001f
#define AAS_PLANE_DIST_EPSILON 0.01f
idHashIndex *aas_vertexHash;
idHashIndex *aas_edgeHash;
idBounds aas_vertexBounds;
int aas_vertexShift;
/*
================
idAASBuild::SetupHash
================
*/
void idAASBuild::SetupHash( void ) {
aas_vertexHash = new idHashIndex( VERTEX_HASH_SIZE, 1024 );
aas_edgeHash = new idHashIndex( EDGE_HASH_SIZE, 1024 );
}
/*
================
idAASBuild::ShutdownHash
================
*/
void idAASBuild::ShutdownHash( void ) {
delete aas_vertexHash;
delete aas_edgeHash;
}
/*
================
idAASBuild::ClearHash
================
*/
void idAASBuild::ClearHash( const idBounds &bounds ) {
int i;
float f, max;
aas_vertexHash->Clear();
aas_edgeHash->Clear();
aas_vertexBounds = bounds;
max = bounds[1].x - bounds[0].x;
f = bounds[1].y - bounds[0].y;
if ( f > max ) {
max = f;
}
aas_vertexShift = (float) max / VERTEX_HASH_BOXSIZE;
for ( i = 0; (1<<i) < aas_vertexShift; i++ ) {
}
if ( i == 0 ) {
aas_vertexShift = 1;
}
else {
aas_vertexShift = i;
}
}
/*
================
idAASBuild::HashVec
================
*/
ID_INLINE int idAASBuild::HashVec( const idVec3 &vec ) {
int x, y;
x = (((int) (vec[0] - aas_vertexBounds[0].x + 0.5)) + 2) >> 2;
y = (((int) (vec[1] - aas_vertexBounds[0].y + 0.5)) + 2) >> 2;
return (x + y * VERTEX_HASH_BOXSIZE) & (VERTEX_HASH_SIZE-1);
}
/*
================
idAASBuild::GetVertex
================
*/
bool idAASBuild::GetVertex( const idVec3 &v, int *vertexNum ) {
int i, hashKey, vn;
aasVertex_t vert, *p;
for (i = 0; i < 3; i++) {
if ( idMath::Fabs(v[i] - idMath::Rint(v[i])) < INTEGRAL_EPSILON ) {
vert[i] = idMath::Rint(v[i]);
}
else {
vert[i] = v[i];
}
}
hashKey = idAASBuild::HashVec( vert );
for ( vn = aas_vertexHash->First( hashKey ); vn >= 0; vn = aas_vertexHash->Next( vn ) ) {
p = &file->vertices[vn];
// first compare z-axis because hash is based on x-y plane
if (idMath::Fabs( vert.z - p->z ) < VERTEX_EPSILON &&
idMath::Fabs( vert.x - p->x ) < VERTEX_EPSILON &&
idMath::Fabs( vert.y - p->y ) < VERTEX_EPSILON )
{
*vertexNum = vn;
return true;
}
}
*vertexNum = file->vertices.Num();
aas_vertexHash->Add( hashKey, file->vertices.Num() );
file->vertices.Append( vert );
return false;
}
/*
================
idAASBuild::GetEdge
================
*/
bool idAASBuild::GetEdge( const idVec3 &v1, const idVec3 &v2, int *edgeNum, int v1num ) {
int v2num, hashKey, e;
int *vertexNum;
aasEdge_t edge;
bool found;
if ( v1num != -1 ) {
found = true;
}
else {
found = GetVertex( v1, &v1num );
}
found &= GetVertex( v2, &v2num );
// if both vertexes are the same or snapped onto each other
if ( v1num == v2num ) {
*edgeNum = 0;
return true;
}
hashKey = aas_edgeHash->GenerateKey( v1num, v2num );
// if both vertexes where already stored
if ( found ) {
for ( e = aas_edgeHash->First( hashKey ); e >= 0; e = aas_edgeHash->Next( e ) ) {
vertexNum = file->edges[e].vertexNum;
if ( vertexNum[0] == v2num ) {
if ( vertexNum[1] == v1num ) {
// negative for a reversed edge
*edgeNum = -e;
break;
}
}
else if ( vertexNum[0] == v1num ) {
if ( vertexNum[1] == v2num ) {
*edgeNum = e;
break;
}
}
}
// if edge found in hash
if ( e >= 0 ) {
return true;
}
}
*edgeNum = file->edges.Num();
aas_edgeHash->Add( hashKey, file->edges.Num() );
edge.vertexNum[0] = v1num;
edge.vertexNum[1] = v2num;
file->edges.Append( edge );
return false;
}
/*
================
idAASBuild::GetFaceForPortal
================
*/
bool idAASBuild::GetFaceForPortal( idBrushBSPPortal *portal, int side, int *faceNum ) {
int i, j, v1num;
int numFaceEdges, faceEdges[MAX_POINTS_ON_WINDING];
idWinding *w;
aasFace_t face;
if ( portal->GetFaceNum() > 0 ) {
if ( side ) {
*faceNum = -portal->GetFaceNum();
}
else {
*faceNum = portal->GetFaceNum();
}
return true;
}
w = portal->GetWinding();
// turn the winding into a sequence of edges
numFaceEdges = 0;
v1num = -1; // first vertex unknown
for ( i = 0; i < w->GetNumPoints(); i++ ) {
GetEdge( (*w)[i].ToVec3(), (*w)[(i+1)%w->GetNumPoints()].ToVec3(), &faceEdges[numFaceEdges], v1num );
if ( faceEdges[numFaceEdges] ) {
// last vertex of this edge is the first vertex of the next edge
v1num = file->edges[ abs(faceEdges[numFaceEdges]) ].vertexNum[ INTSIGNBITNOTSET(faceEdges[numFaceEdges]) ];
// this edge is valid so keep it
numFaceEdges++;
}
}
// should have at least 3 edges
if ( numFaceEdges < 3 ) {
return false;
}
// the polygon is invalid if some edge is found twice
for ( i = 0; i < numFaceEdges; i++ ) {
for ( j = i+1; j < numFaceEdges; j++ ) {
if ( faceEdges[i] == faceEdges[j] || faceEdges[i] == -faceEdges[j] ) {
return false;
}
}
}
portal->SetFaceNum( file->faces.Num() );
face.planeNum = file->planeList.FindPlane( portal->GetPlane(), AAS_PLANE_NORMAL_EPSILON, AAS_PLANE_DIST_EPSILON );
face.flags = portal->GetFlags();
face.areas[0] = face.areas[1] = 0;
face.firstEdge = file->edgeIndex.Num();
face.numEdges = numFaceEdges;
for ( i = 0; i < numFaceEdges; i++ ) {
file->edgeIndex.Append( faceEdges[i] );
}
if ( side ) {
*faceNum = -file->faces.Num();
}
else {
*faceNum = file->faces.Num();
}
file->faces.Append( face );
return true;
}
/*
================
idAASBuild::GetAreaForLeafNode
================
*/
bool idAASBuild::GetAreaForLeafNode( idBrushBSPNode *node, int *areaNum ) {
int s, faceNum;
idBrushBSPPortal *p;
aasArea_t area;
if ( node->GetAreaNum() ) {
*areaNum = -node->GetAreaNum();
return true;
}
area.flags = node->GetFlags();
area.cluster = area.clusterAreaNum = 0;
area.contents = node->GetContents();
area.firstFace = file->faceIndex.Num();
area.numFaces = 0;
area.reach = NULL;
area.rev_reach = NULL;
for ( p = node->GetPortals(); p; p = p->Next(s) ) {
s = (p->GetNode(1) == node);
if ( !GetFaceForPortal( p, s, &faceNum ) ) {
continue;
}
file->faceIndex.Append( faceNum );
area.numFaces++;
if ( faceNum > 0 ) {
file->faces[abs(faceNum)].areas[0] = file->areas.Num();
}
else {
file->faces[abs(faceNum)].areas[1] = file->areas.Num();
}
}
if ( !area.numFaces ) {
*areaNum = 0;
return false;
}
*areaNum = -file->areas.Num();
node->SetAreaNum( file->areas.Num() );
file->areas.Append( area );
DisplayRealTimeString( "\r%6d", file->areas.Num() );
return true;
}
/*
================
idAASBuild::StoreTree_r
================
*/
int idAASBuild::StoreTree_r( idBrushBSPNode *node ) {
int areaNum, nodeNum, child0, child1;
aasNode_t aasNode;
if ( !node ) {
return 0;
}
if ( node->GetContents() & AREACONTENTS_SOLID ) {
return 0;
}
if ( !node->GetChild(0) && !node->GetChild(1) ) {
if ( GetAreaForLeafNode( node, &areaNum ) ) {
return areaNum;
}
return 0;
}
aasNode.planeNum = file->planeList.FindPlane( node->GetPlane(), AAS_PLANE_NORMAL_EPSILON, AAS_PLANE_DIST_EPSILON );
aasNode.children[0] = aasNode.children[1] = 0;
nodeNum = file->nodes.Num();
file->nodes.Append( aasNode );
// !@#$%^ cause of some bug we cannot set the children directly with the StoreTree_r return value
child0 = StoreTree_r( node->GetChild(0) );
file->nodes[nodeNum].children[0] = child0;
child1 = StoreTree_r( node->GetChild(1) );
file->nodes[nodeNum].children[1] = child1;
if ( !child0 && !child1 ) {
file->nodes.SetNum( file->nodes.Num()-1 );
return 0;
}
return nodeNum;
}
/*
================
idAASBuild::GetSizeEstimate_r
================
*/
typedef struct sizeEstimate_s {
int numEdgeIndexes;
int numFaceIndexes;
int numAreas;
int numNodes;
} sizeEstimate_t;
void idAASBuild::GetSizeEstimate_r( idBrushBSPNode *parent, idBrushBSPNode *node, struct sizeEstimate_s &size ) {
idBrushBSPPortal *p;
int s;
if ( !node ) {
return;
}
if ( node->GetContents() & AREACONTENTS_SOLID ) {
return;
}
if ( !node->GetChild(0) && !node->GetChild(1) ) {
// multiple branches of the bsp tree might point to the same leaf node
if ( node->GetParent() == parent ) {
size.numAreas++;
for ( p = node->GetPortals(); p; p = p->Next(s) ) {
s = (p->GetNode(1) == node);
size.numFaceIndexes++;
size.numEdgeIndexes += p->GetWinding()->GetNumPoints();
}
}
}
else {
size.numNodes++;
}
GetSizeEstimate_r( node, node->GetChild(0), size );
GetSizeEstimate_r( node, node->GetChild(1), size );
}
/*
================
idAASBuild::SetSizeEstimate
================
*/
void idAASBuild::SetSizeEstimate( const idBrushBSP &bsp, idAASFileLocal *file ) {
sizeEstimate_t size;
size.numEdgeIndexes = 1;
size.numFaceIndexes = 1;
size.numAreas = 1;
size.numNodes = 1;
GetSizeEstimate_r( NULL, bsp.GetRootNode(), size );
file->planeList.Resize( size.numNodes / 2, 1024 );
file->vertices.Resize( size.numEdgeIndexes / 3, 1024 );
file->edges.Resize( size.numEdgeIndexes / 2, 1024 );
file->edgeIndex.Resize( size.numEdgeIndexes, 4096 );
file->faces.Resize( size.numFaceIndexes, 1024 );
file->faceIndex.Resize( size.numFaceIndexes, 4096 );
file->areas.Resize( size.numAreas, 1024 );
file->nodes.Resize( size.numNodes, 1024 );
}
/*
================
idAASBuild::StoreFile
================
*/
bool idAASBuild::StoreFile( const idBrushBSP &bsp ) {
aasEdge_t edge;
aasFace_t face;
aasArea_t area;
aasNode_t node;
common->Printf( "[Store AAS]\n" );
SetupHash();
ClearHash( bsp.GetTreeBounds() );
file = new idAASFileLocal();
file->Clear();
SetSizeEstimate( bsp, file );
// the first edge is a dummy
memset( &edge, 0, sizeof( edge ) );
file->edges.Append( edge );
// the first face is a dummy
memset( &face, 0, sizeof( face ) );
file->faces.Append( face );
// the first area is a dummy
memset( &area, 0, sizeof( area ) );
file->areas.Append( area );
// the first node is a dummy
memset( &node, 0, sizeof( node ) );
file->nodes.Append( node );
// store the tree
StoreTree_r( bsp.GetRootNode() );
// calculate area bounds and a reachable point in the area
file->FinishAreas();
ShutdownHash();
common->Printf( "\r%6d areas\n", file->areas.Num() );
return true;
}

View File

@@ -0,0 +1,359 @@
/*
===========================================================================
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 "../../../idlib/precompiled.h"
#pragma hdrstop
#include "AASBuild_local.h"
/*
============
idAASBuild::SetPortalFlags_r
============
*/
void idAASBuild::SetPortalFlags_r( idBrushBSPNode *node ) {
int s;
idBrushBSPPortal *p;
idVec3 normal;
if ( !node ) {
return;
}
if ( node->GetContents() & AREACONTENTS_SOLID ) {
return;
}
if ( !node->GetChild(0) && !node->GetChild(1) ) {
for ( p = node->GetPortals(); p; p = p->Next(s) ) {
s = (p->GetNode(1) == node);
// if solid at the other side of the portal
if ( p->GetNode(!s)->GetContents() & AREACONTENTS_SOLID ) {
if ( s ) {
normal = -p->GetPlane().Normal();
}
else {
normal = p->GetPlane().Normal();
}
if ( normal * aasSettings->invGravityDir > aasSettings->minFloorCos ) {
p->SetFlag( FACE_FLOOR );
}
else {
p->SetFlag( FACE_SOLID );
}
}
}
return;
}
SetPortalFlags_r( node->GetChild(0) );
SetPortalFlags_r( node->GetChild(1) );
}
/*
============
idAASBuild::PortalIsGap
============
*/
bool idAASBuild::PortalIsGap( idBrushBSPPortal *portal, int side ) {
idVec3 normal;
// if solid at the other side of the portal
if ( portal->GetNode(!side)->GetContents() & AREACONTENTS_SOLID ) {
return false;
}
if ( side ) {
normal = -(portal->GetPlane().Normal());
}
else {
normal = portal->GetPlane().Normal();
}
if ( normal * aasSettings->invGravityDir > aasSettings->minFloorCos ) {
return true;
}
return false;
}
/*
============
idAASBuild::GravSubdivLeafNode
============
*/
#define FACE_CHECKED BIT(31)
#define GRAVSUBDIV_EPSILON 0.1f
void idAASBuild::GravSubdivLeafNode( idBrushBSPNode *node ) {
int s1, s2, i, j, k, side1;
int numSplits, numSplitters;
idBrushBSPPortal *p1, *p2;
idWinding *w1, *w2;
idVec3 normal;
idPlane plane;
idPlaneSet planeList;
float d, min, max;
int *splitterOrder;
int *bestNumSplits;
int floor, gap, numFloorChecked;
// if this leaf node is already classified it cannot have a combination of floor and gap portals
if ( node->GetFlags() & (AREA_FLOOR|AREA_GAP) ) {
return;
}
floor = gap = 0;
// check if the area has a floor
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
s1 = (p1->GetNode(1) == node);
if ( p1->GetFlags() & FACE_FLOOR ) {
floor++;
}
}
// find seperating planes between gap and floor portals
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
s1 = (p1->GetNode(1) == node);
// if the portal is a gap seen from this side
if ( PortalIsGap( p1, s1 ) ) {
gap++;
// if the area doesn't have a floor
if ( !floor ) {
break;
}
}
else {
continue;
}
numFloorChecked = 0;
w1 = p1->GetWinding();
// test all edges of the gap
for ( i = 0; i < w1->GetNumPoints(); i++ ) {
// create a plane through the edge of the gap parallel to the direction of gravity
normal = (*w1)[(i+1)%w1->GetNumPoints()].ToVec3() - (*w1)[i].ToVec3();
normal = normal.Cross( aasSettings->invGravityDir );
if ( normal.Normalize() < 0.2f ) {
continue;
}
plane.SetNormal( normal );
plane.FitThroughPoint( (*w1)[i].ToVec3() );
// get the side of the plane the gap is on
side1 = w1->PlaneSide( plane, GRAVSUBDIV_EPSILON );
if ( side1 == SIDE_ON ) {
break;
}
// test if the plane through the edge of the gap seperates the gap from a floor portal
for ( p2 = node->GetPortals(); p2; p2 = p2->Next(s2) ) {
s2 = (p2->GetNode(1) == node);
if ( !( p2->GetFlags() & FACE_FLOOR ) ) {
continue;
}
if ( p2->GetFlags() & FACE_CHECKED ) {
continue;
}
w2 = p2->GetWinding();
min = 2.0f * GRAVSUBDIV_EPSILON;
max = GRAVSUBDIV_EPSILON;
if ( side1 == SIDE_FRONT ) {
for ( j = 0; j < w2->GetNumPoints(); j++ ) {
d = plane.Distance( (*w2)[j].ToVec3() );
if ( d >= GRAVSUBDIV_EPSILON ) {
break; // point at the same side of the plane as the gap
}
d = idMath::Fabs( d );
if ( d < min ) {
min = d;
}
if ( d > max ) {
max = d;
}
}
}
else {
for ( j = 0; j < w2->GetNumPoints(); j++ ) {
d = plane.Distance( (*w2)[j].ToVec3() );
if ( d <= -GRAVSUBDIV_EPSILON ) {
break; // point at the same side of the plane as the gap
}
d = idMath::Fabs( d );
if ( d < min ) {
min = d;
}
if ( d > max ) {
max = d;
}
}
}
// a point of the floor portal was found to be at the same side of the plane as the gap
if ( j < w2->GetNumPoints() ) {
continue;
}
// if the floor portal touches the plane
if ( min < GRAVSUBDIV_EPSILON && max > GRAVSUBDIV_EPSILON ) {
planeList.FindPlane( plane, 0.00001f, 0.1f );
}
p2->SetFlag( FACE_CHECKED );
numFloorChecked++;
}
if ( numFloorChecked == floor ) {
break;
}
}
for ( p2 = node->GetPortals(); p2; p2 = p2->Next(s2) ) {
s2 = (p2->GetNode(1) == node);
p2->RemoveFlag( FACE_CHECKED );
}
}
// if the leaf node does not have both floor and gap portals
if ( !( gap && floor) ) {
if ( floor ) {
node->SetFlag( AREA_FLOOR );
}
else if ( gap ) {
node->SetFlag( AREA_GAP );
}
return;
}
// if no valid seperators found
if ( planeList.Num() == 0 ) {
// NOTE: this should never happend, if it does the leaf node has degenerate portals
return;
}
splitterOrder = (int *) _alloca( planeList.Num() * sizeof( int ) );
bestNumSplits = (int *) _alloca( planeList.Num() * sizeof( int ) );
numSplitters = 0;
// test all possible seperators and sort them from best to worst
for ( i = 0; i < planeList.Num(); i += 2 ) {
numSplits = 0;
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
s1 = (p1->GetNode(1) == node);
if ( p1->GetWinding()->PlaneSide( planeList[i], 0.1f ) == SIDE_CROSS ) {
numSplits++;
}
}
for ( j = 0; j < numSplitters; j++ ) {
if ( numSplits < bestNumSplits[j] ) {
for ( k = numSplitters; k > j; k-- ) {
bestNumSplits[k] = bestNumSplits[k-1];
splitterOrder[k] = splitterOrder[k-1];
}
bestNumSplits[j] = numSplits;
splitterOrder[j] = i;
numSplitters++;
break;
}
}
if ( j >= numSplitters ) {
bestNumSplits[j] = numSplits;
splitterOrder[j] = i;
numSplitters++;
}
}
// try all seperators in order from best to worst
for ( i = 0; i < numSplitters; i++ ) {
if ( node->Split( planeList[splitterOrder[i]], -1 ) ) {
// we found a seperator that works
break;
}
}
if ( i >= numSplitters) {
return;
}
DisplayRealTimeString( "\r%6d", ++numGravitationalSubdivisions );
// test children for further splits
GravSubdivLeafNode( node->GetChild(0) );
GravSubdivLeafNode( node->GetChild(1) );
}
/*
============
idAASBuild::GravSubdiv_r
============
*/
void idAASBuild::GravSubdiv_r( idBrushBSPNode *node ) {
if ( !node ) {
return;
}
if ( node->GetContents() & AREACONTENTS_SOLID ) {
return;
}
if ( !node->GetChild(0) && !node->GetChild(1) ) {
GravSubdivLeafNode( node );
return;
}
GravSubdiv_r( node->GetChild(0) );
GravSubdiv_r( node->GetChild(1) );
}
/*
============
idAASBuild::GravitationalSubdivision
============
*/
void idAASBuild::GravitationalSubdivision( idBrushBSP &bsp ) {
numGravitationalSubdivisions = 0;
common->Printf( "[Gravitational Subdivision]\n" );
SetPortalFlags_r( bsp.GetRootNode() );
GravSubdiv_r( bsp.GetRootNode() );
common->Printf( "\r%6d subdivisions\n", numGravitationalSubdivisions );
}

View File

@@ -0,0 +1,575 @@
/*
===========================================================================
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 "../../../idlib/precompiled.h"
#pragma hdrstop
#include "AASBuild_local.h"
#define LEDGE_EPSILON 0.1f
//===============================================================
//
// idLedge
//
//===============================================================
/*
============
idLedge::idLedge
============
*/
idLedge::idLedge( void ) {
}
/*
============
idLedge::idLedge
============
*/
idLedge::idLedge( const idVec3 &v1, const idVec3 &v2, const idVec3 &gravityDir, idBrushBSPNode *n ) {
start = v1;
end = v2;
node = n;
numPlanes = 4;
planes[0].SetNormal( (v1 - v2).Cross( gravityDir ) );
planes[0].Normalize();
planes[0].FitThroughPoint( v1 );
planes[1].SetNormal( (v1 - v2).Cross( planes[0].Normal() ) );
planes[1].Normalize();
planes[1].FitThroughPoint( v1 );
planes[2].SetNormal( v1 - v2 );
planes[2].Normalize();
planes[2].FitThroughPoint( v1 );
planes[3].SetNormal( v2 - v1 );
planes[3].Normalize();
planes[3].FitThroughPoint( v2 );
}
/*
============
idLedge::AddPoint
============
*/
void idLedge::AddPoint( const idVec3 &v ) {
if ( planes[2].Distance( v ) > 0.0f ) {
start = v;
planes[2].FitThroughPoint( start );
}
if ( planes[3].Distance( v ) > 0.0f ) {
end = v;
planes[3].FitThroughPoint( end );
}
}
/*
============
idLedge::CreateBevels
NOTE: this assumes the gravity is vertical
============
*/
void idLedge::CreateBevels( const idVec3 &gravityDir ) {
int i, j;
idBounds bounds;
idVec3 size, normal;
bounds.Clear();
bounds.AddPoint( start );
bounds.AddPoint( end );
size = bounds[1] - bounds[0];
// plane through ledge
planes[0].SetNormal( (start - end).Cross( gravityDir ) );
planes[0].Normalize();
planes[0].FitThroughPoint( start );
// axial bevels at start and end point
i = size[1] > size[0];
normal = vec3_origin;
normal[i] = 1.0f;
j = end[i] > start[i];
planes[1+j].SetNormal( normal );
planes[1+!j].SetNormal( -normal );
planes[1].FitThroughPoint( start );
planes[2].FitThroughPoint( end );
numExpandedPlanes = 3;
// if additional bevels are required
if ( idMath::Fabs( size[!i] ) > 0.01f ) {
normal = vec3_origin;
normal[!i] = 1.0f;
j = end[!i] > start[!i];
planes[3+j].SetNormal( normal );
planes[3+!j].SetNormal( -normal );
planes[3].FitThroughPoint( start );
planes[4].FitThroughPoint( end );
numExpandedPlanes = 5;
}
// opposite of first
planes[numExpandedPlanes+0] = -planes[0];
// number of planes used for splitting
numSplitPlanes = numExpandedPlanes + 1;
// top plane
planes[numSplitPlanes+0].SetNormal( (start - end).Cross( planes[0].Normal() ) );
planes[numSplitPlanes+0].Normalize();
planes[numSplitPlanes+0].FitThroughPoint( start );
// bottom plane
planes[numSplitPlanes+1] = -planes[numSplitPlanes+0];
// total number of planes
numPlanes = numSplitPlanes + 2;
}
/*
============
idLedge::Expand
============
*/
void idLedge::Expand( const idBounds &bounds, float maxStepHeight ) {
int i, j;
idVec3 v;
for ( i = 0; i < numExpandedPlanes; i++ ) {
for ( j = 0; j < 3; j++ ) {
if ( planes[i].Normal()[j] > 0.0f ) {
v[j] = bounds[0][j];
}
else {
v[j] = bounds[1][j];
}
}
planes[i].SetDist( planes[i].Dist() + v * -planes[i].Normal() );
}
planes[numSplitPlanes+0].SetDist( planes[numSplitPlanes+0].Dist() + maxStepHeight );
planes[numSplitPlanes+1].SetDist( planes[numSplitPlanes+1].Dist() + 1.0f );
}
/*
============
idLedge::ChopWinding
============
*/
idWinding *idLedge::ChopWinding( const idWinding *winding ) const {
int i;
idWinding *w;
w = winding->Copy();
for ( i = 0; i < numPlanes && w; i++ ) {
w = w->Clip( -planes[i], ON_EPSILON, true );
}
return w;
}
/*
============
idLedge::PointBetweenBounds
============
*/
bool idLedge::PointBetweenBounds( const idVec3 &v ) const {
return ( planes[2].Distance( v ) < LEDGE_EPSILON ) && ( planes[3].Distance( v ) < LEDGE_EPSILON );
}
//===============================================================
//
// idAASBuild
//
//===============================================================
/*
============
idAASBuild::LedgeSubdivFlood_r
============
*/
void idAASBuild::LedgeSubdivFlood_r( idBrushBSPNode *node, const idLedge *ledge ) {
int s1, i;
idBrushBSPPortal *p1;
idWinding *w;
idList<idBrushBSPNode *> nodeList;
if ( node->GetFlags() & NODE_VISITED ) {
return;
}
// if this is not already a ledge area
if ( !( node->GetFlags() & AREA_LEDGE ) ) {
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
s1 = (p1->GetNode(1) == node);
if ( !(p1->GetFlags() & FACE_FLOOR) ) {
continue;
}
// split the area if some part of the floor portal is inside the expanded ledge
w = ledge->ChopWinding( p1->GetWinding() );
if ( !w ) {
continue;
}
delete w;
for ( i = 0; i < ledge->numSplitPlanes; i++ ) {
if ( node->PlaneSide( ledge->planes[i], 0.1f ) != SIDE_CROSS ) {
continue;
}
if ( !node->Split( ledge->planes[i], -1 ) ) {
continue;
}
numLedgeSubdivisions++;
DisplayRealTimeString( "\r%6d", numLedgeSubdivisions );
node->GetChild(0)->SetFlag( NODE_VISITED );
LedgeSubdivFlood_r( node->GetChild(1), ledge );
return;
}
node->SetFlag( AREA_LEDGE );
break;
}
}
node->SetFlag( NODE_VISITED );
// get all nodes we might need to flood into
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
s1 = (p1->GetNode(1) == node);
if ( p1->GetNode( !s1 )->GetContents() & AREACONTENTS_SOLID ) {
continue;
}
// flood through this portal if the portal is partly inside the expanded ledge
w = ledge->ChopWinding( p1->GetWinding() );
if ( !w ) {
continue;
}
delete w;
// add to list, cannot flood directly cause portals might be split on the way
nodeList.Append( p1->GetNode( !s1 ) );
}
// flood into other nodes
for ( i = 0; i < nodeList.Num(); i++ ) {
LedgeSubdivLeafNodes_r( nodeList[i], ledge );
}
}
/*
============
idAASBuild::LedgeSubdivLeafNodes_r
The node the ledge was originally part of might be split by other ledges.
Here we recurse down the tree from the original node to find all the new leaf nodes the ledge might be part of.
============
*/
void idAASBuild::LedgeSubdivLeafNodes_r( idBrushBSPNode *node, const idLedge *ledge ) {
if ( !node ) {
return;
}
if ( !node->GetChild(0) && !node->GetChild(1) ) {
LedgeSubdivFlood_r( node, ledge );
return;
}
LedgeSubdivLeafNodes_r( node->GetChild(0), ledge );
LedgeSubdivLeafNodes_r( node->GetChild(1), ledge );
}
/*
============
idAASBuild::LedgeSubdiv
============
*/
void idAASBuild::LedgeSubdiv( idBrushBSPNode *root ) {
int i, j;
idBrush *brush;
idList<idBrushSide *> sideList;
// create ledge bevels and expand ledges
for ( i = 0; i < ledgeList.Num(); i++ ) {
ledgeList[i].CreateBevels( aasSettings->gravityDir );
ledgeList[i].Expand( aasSettings->boundingBoxes[0], aasSettings->maxStepHeight );
// if we should write out a ledge map
if ( ledgeMap ) {
sideList.SetNum( 0 );
for ( j = 0; j < ledgeList[i].numPlanes; j++ ) {
sideList.Append( new idBrushSide( ledgeList[i].planes[j], -1 ) );
}
brush = new idBrush();
brush->FromSides( sideList );
ledgeMap->WriteBrush( brush );
delete brush;
}
// flood tree from the ledge node and subdivide areas with the ledge
LedgeSubdivLeafNodes_r( ledgeList[i].node, &ledgeList[i] );
// remove the node visited flags
ledgeList[i].node->RemoveFlagRecurseFlood( NODE_VISITED );
}
}
/*
============
idAASBuild::IsLedgeSide_r
============
*/
bool idAASBuild::IsLedgeSide_r( idBrushBSPNode *node, idFixedWinding *w, const idPlane &plane, const idVec3 &normal, const idVec3 &origin, const float radius ) {
int res, i;
idFixedWinding back;
float dist;
if ( !node ) {
return false;
}
while ( node->GetChild(0) && node->GetChild(1) ) {
dist = node->GetPlane().Distance( origin );
if ( dist > radius ) {
res = SIDE_FRONT;
}
else if ( dist < -radius ) {
res = SIDE_BACK;
}
else {
res = w->Split( &back, node->GetPlane(), LEDGE_EPSILON );
}
if ( res == SIDE_FRONT ) {
node = node->GetChild(0);
}
else if ( res == SIDE_BACK ) {
node = node->GetChild(1);
}
else if ( res == SIDE_ON ) {
// continue with the side the winding faces
if ( node->GetPlane().Normal() * normal > 0.0f ) {
node = node->GetChild(0);
}
else {
node = node->GetChild(1);
}
}
else {
if ( IsLedgeSide_r( node->GetChild(1), &back, plane, normal, origin, radius ) ) {
return true;
}
node = node->GetChild(0);
}
}
if ( node->GetContents() & AREACONTENTS_SOLID ) {
return false;
}
for ( i = 0; i < w->GetNumPoints(); i++ ) {
if ( plane.Distance( (*w)[i].ToVec3() ) > 0.0f ) {
return true;
}
}
return false;
}
/*
============
idAASBuild::AddLedge
============
*/
void idAASBuild::AddLedge( const idVec3 &v1, const idVec3 &v2, idBrushBSPNode *node ) {
int i, j, merged;
// first try to merge the ledge with existing ledges
merged = -1;
for ( i = 0; i < ledgeList.Num(); i++ ) {
for ( j = 0; j < 2; j++ ) {
if ( idMath::Fabs( ledgeList[i].planes[j].Distance( v1 ) ) > LEDGE_EPSILON ) {
break;
}
if ( idMath::Fabs( ledgeList[i].planes[j].Distance( v2 ) ) > LEDGE_EPSILON ) {
break;
}
}
if ( j < 2 ) {
continue;
}
if ( !ledgeList[i].PointBetweenBounds( v1 ) &&
!ledgeList[i].PointBetweenBounds( v2 ) ) {
continue;
}
if ( merged == -1 ) {
ledgeList[i].AddPoint( v1 );
ledgeList[i].AddPoint( v2 );
merged = i;
}
else {
ledgeList[merged].AddPoint( ledgeList[i].start );
ledgeList[merged].AddPoint( ledgeList[i].end );
ledgeList.RemoveIndex(i);
break;
}
}
// if the ledge could not be merged
if ( merged == -1 ) {
ledgeList.Append( idLedge( v1, v2, aasSettings->gravityDir, node ) );
}
}
/*
============
idAASBuild::FindLeafNodeLedges
============
*/
void idAASBuild::FindLeafNodeLedges( idBrushBSPNode *root, idBrushBSPNode *node ) {
int s1, i;
idBrushBSPPortal *p1;
idWinding *w;
idVec3 v1, v2, normal, origin;
idFixedWinding winding;
idBounds bounds;
idPlane plane;
float radius;
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
s1 = (p1->GetNode(1) == node);
if ( !(p1->GetFlags() & FACE_FLOOR) ) {
continue;
}
if ( s1 ) {
plane = p1->GetPlane();
w = p1->GetWinding()->Reverse();
}
else {
plane = -p1->GetPlane();
w = p1->GetWinding();
}
for ( i = 0; i < w->GetNumPoints(); i++ ) {
v1 = (*w)[i].ToVec3();
v2 = (*w)[(i+1)%w->GetNumPoints()].ToVec3();
normal = (v2 - v1).Cross( aasSettings->gravityDir );
if ( normal.Normalize() < 0.5f ) {
continue;
}
winding.Clear();
winding += v1 + normal * LEDGE_EPSILON * 0.5f;
winding += v2 + normal * LEDGE_EPSILON * 0.5f;
winding += winding[1].ToVec3() + ( aasSettings->maxStepHeight + 1.0f ) * aasSettings->gravityDir;
winding += winding[0].ToVec3() + ( aasSettings->maxStepHeight + 1.0f ) * aasSettings->gravityDir;
winding.GetBounds( bounds );
origin = (bounds[1] - bounds[0]) * 0.5f;
radius = origin.Length() + LEDGE_EPSILON;
origin = bounds[0] + origin;
plane.FitThroughPoint( v1 + aasSettings->maxStepHeight * aasSettings->gravityDir );
if ( !IsLedgeSide_r( root, &winding, plane, normal, origin, radius ) ) {
continue;
}
AddLedge( v1, v2, node );
}
if ( w != p1->GetWinding() ) {
delete w;
}
}
}
/*
============
idAASBuild::FindLedges_r
============
*/
void idAASBuild::FindLedges_r( idBrushBSPNode *root, idBrushBSPNode *node ) {
if ( !node ) {
return;
}
if ( node->GetContents() & AREACONTENTS_SOLID ) {
return;
}
if ( !node->GetChild(0) && !node->GetChild(1) ) {
if ( node->GetFlags() & NODE_VISITED ) {
return;
}
FindLeafNodeLedges( root, node );
node->SetFlag( NODE_VISITED );
return;
}
FindLedges_r( root, node->GetChild(0) );
FindLedges_r( root, node->GetChild(1) );
}
/*
============
idAASBuild::WriteLedgeMap
============
*/
void idAASBuild::WriteLedgeMap( const idStr &fileName, const idStr &ext ) {
ledgeMap = new idBrushMap( fileName, ext );
ledgeMap->SetTexture( "textures/base_trim/bluetex4q_ed" );
}
/*
============
idAASBuild::LedgeSubdivision
NOTE: this assumes the bounding box is higher than the maximum step height
only ledges with vertical sides are considered
============
*/
void idAASBuild::LedgeSubdivision( idBrushBSP &bsp ) {
numLedgeSubdivisions = 0;
ledgeList.Clear();
common->Printf( "[Ledge Subdivision]\n" );
bsp.GetRootNode()->RemoveFlagRecurse( NODE_VISITED );
FindLedges_r( bsp.GetRootNode(), bsp.GetRootNode() );
bsp.GetRootNode()->RemoveFlagRecurse( NODE_VISITED );
common->Printf( "\r%6d ledges\n", ledgeList.Num() );
LedgeSubdiv( bsp.GetRootNode() );
common->Printf( "\r%6d subdivisions\n", numLedgeSubdivisions );
}

View File

@@ -0,0 +1,149 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __AASBUILD_LOCAL_H__
#define __AASBUILD_LOCAL_H__
#include "AASFile.h"
#include "AASFile_local.h"
#include "Brush.h"
#include "BrushBSP.h"
#include "AASReach.h"
#include "AASCluster.h"
//===============================================================
//
// idAASBuild
//
//===============================================================
typedef struct aasProcNode_s {
idPlane plane;
int children[2]; // negative numbers are (-1 - areaNumber), 0 = solid
} aasProcNode_t;
class idLedge {
public:
idVec3 start;
idVec3 end;
idBrushBSPNode * node;
int numExpandedPlanes;
int numSplitPlanes;
int numPlanes;
idPlane planes[8];
public:
idLedge( void );
idLedge( const idVec3 &v1, const idVec3 &v2, const idVec3 &gravityDir, idBrushBSPNode *n );
void AddPoint( const idVec3 &v );
void CreateBevels( const idVec3 &gravityDir );
void Expand( const idBounds &bounds, float maxStepHeight );
idWinding * ChopWinding( const idWinding *winding ) const;
bool PointBetweenBounds( const idVec3 &v ) const;
};
class idAASBuild {
public:
idAASBuild( void );
~idAASBuild( void );
bool Build( const idStr &fileName, const idAASSettings *settings );
bool BuildReachability( const idStr &fileName, const idAASSettings *settings );
void Shutdown( void );
private:
const idAASSettings * aasSettings;
idAASFileLocal * file;
aasProcNode_t * procNodes;
int numProcNodes;
int numGravitationalSubdivisions;
int numMergedLeafNodes;
int numLedgeSubdivisions;
idList<idLedge> ledgeList;
idBrushMap * ledgeMap;
private: // map loading
void ParseProcNodes( idLexer *src );
bool LoadProcBSP( const char *name, ID_TIME_T minFileTime );
void DeleteProcBSP( void );
bool ChoppedAwayByProcBSP( int nodeNum, idFixedWinding *w, const idVec3 &normal, const idVec3 &origin, const float radius );
void ClipBrushSidesWithProcBSP( idBrushList &brushList );
int ContentsForAAS( int contents );
idBrushList AddBrushesForMapBrush( const idMapBrush *mapBrush, const idVec3 &origin, const idMat3 &axis, int entityNum, int primitiveNum, idBrushList brushList );
idBrushList AddBrushesForMapPatch( const idMapPatch *mapPatch, const idVec3 &origin, const idMat3 &axis, int entityNum, int primitiveNum, idBrushList brushList );
idBrushList AddBrushesForMapEntity( const idMapEntity *mapEnt, int entityNum, idBrushList brushList );
idBrushList AddBrushesForMapFile( const idMapFile * mapFile, idBrushList brushList );
bool CheckForEntities( const idMapFile *mapFile, idStrList &entityClassNames ) const;
void ChangeMultipleBoundingBoxContents_r( idBrushBSPNode *node, int mask );
private: // gravitational subdivision
void SetPortalFlags_r( idBrushBSPNode *node );
bool PortalIsGap( idBrushBSPPortal *portal, int side );
void GravSubdivLeafNode( idBrushBSPNode *node );
void GravSubdiv_r( idBrushBSPNode *node );
void GravitationalSubdivision( idBrushBSP &bsp );
private: // ledge subdivision
void LedgeSubdivFlood_r( idBrushBSPNode *node, const idLedge *ledge );
void LedgeSubdivLeafNodes_r( idBrushBSPNode *node, const idLedge *ledge );
void LedgeSubdiv( idBrushBSPNode *root );
bool IsLedgeSide_r( idBrushBSPNode *node, idFixedWinding *w, const idPlane &plane, const idVec3 &normal, const idVec3 &origin, const float radius );
void AddLedge( const idVec3 &v1, const idVec3 &v2, idBrushBSPNode *node );
void FindLeafNodeLedges( idBrushBSPNode *root, idBrushBSPNode *node );
void FindLedges_r( idBrushBSPNode *root, idBrushBSPNode *node );
void LedgeSubdivision( idBrushBSP &bsp );
void WriteLedgeMap( const idStr &fileName, const idStr &ext );
private: // merging
bool AllGapsLeadToOtherNode( idBrushBSPNode *nodeWithGaps, idBrushBSPNode *otherNode );
bool MergeWithAdjacentLeafNodes( idBrushBSP &bsp, idBrushBSPNode *node );
void MergeLeafNodes_r( idBrushBSP &bsp, idBrushBSPNode *node );
void MergeLeafNodes( idBrushBSP &bsp );
private: // storing file
void SetupHash( void );
void ShutdownHash( void );
void ClearHash( const idBounds &bounds );
int HashVec( const idVec3 &vec );
bool GetVertex( const idVec3 &v, int *vertexNum );
bool GetEdge( const idVec3 &v1, const idVec3 &v2, int *edgeNum, int v1num );
bool GetFaceForPortal( idBrushBSPPortal *portal, int side, int *faceNum );
bool GetAreaForLeafNode( idBrushBSPNode *node, int *areaNum );
int StoreTree_r( idBrushBSPNode *node );
void GetSizeEstimate_r( idBrushBSPNode *parent, idBrushBSPNode *node, struct sizeEstimate_s &size );
void SetSizeEstimate( const idBrushBSP &bsp, idAASFileLocal *file );
bool StoreFile( const idBrushBSP &bsp );
};
#endif /* !__AASBUILD_LOCAL_H__ */

View File

@@ -0,0 +1,163 @@
/*
===========================================================================
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 "../../../idlib/precompiled.h"
#pragma hdrstop
#include "AASBuild_local.h"
/*
============
idAASBuild::AllGapsLeadToOtherNode
============
*/
bool idAASBuild::AllGapsLeadToOtherNode( idBrushBSPNode *nodeWithGaps, idBrushBSPNode *otherNode ) {
int s;
idBrushBSPPortal *p;
for ( p = nodeWithGaps->GetPortals(); p; p = p->Next(s) ) {
s = (p->GetNode(1) == nodeWithGaps);
if ( !PortalIsGap( p, s ) ) {
continue;
}
if ( p->GetNode(!s) != otherNode ) {
return false;
}
}
return true;
}
/*
============
idAASBuild::MergeWithAdjacentLeafNodes
============
*/
bool idAASBuild::MergeWithAdjacentLeafNodes( idBrushBSP &bsp, idBrushBSPNode *node ) {
int s, numMerges = 0, otherNodeFlags;
idBrushBSPPortal *p;
do {
for ( p = node->GetPortals(); p; p = p->Next(s) ) {
s = (p->GetNode(1) == node);
// both leaf nodes must have the same contents
if ( node->GetContents() != p->GetNode(!s)->GetContents() ) {
continue;
}
// cannot merge leaf nodes if one is near a ledge and the other is not
if ( (node->GetFlags() & AREA_LEDGE) != (p->GetNode(!s)->GetFlags() & AREA_LEDGE) ) {
continue;
}
// cannot merge leaf nodes if one has a floor portal and the other a gap portal
if ( node->GetFlags() & AREA_FLOOR ) {
if ( p->GetNode(!s)->GetFlags() & AREA_GAP ) {
if ( !AllGapsLeadToOtherNode( p->GetNode(!s), node ) ) {
continue;
}
}
}
else if ( node->GetFlags() & AREA_GAP ) {
if ( p->GetNode(!s)->GetFlags() & AREA_FLOOR ) {
if ( !AllGapsLeadToOtherNode( node, p->GetNode(!s) ) ) {
continue;
}
}
}
otherNodeFlags = p->GetNode(!s)->GetFlags();
// try to merge the leaf nodes
if ( bsp.TryMergeLeafNodes( p, s ) ) {
node->SetFlag( otherNodeFlags );
if ( node->GetFlags() & AREA_FLOOR ) {
node->RemoveFlag( AREA_GAP );
}
numMerges++;
DisplayRealTimeString( "\r%6d", ++numMergedLeafNodes );
break;
}
}
} while( p );
if ( numMerges ) {
return true;
}
return false;
}
/*
============
idAASBuild::MergeLeafNodes_r
============
*/
void idAASBuild::MergeLeafNodes_r( idBrushBSP &bsp, idBrushBSPNode *node ) {
if ( !node ) {
return;
}
if ( node->GetContents() & AREACONTENTS_SOLID ) {
return;
}
if ( node->GetFlags() & NODE_DONE ) {
return;
}
if ( !node->GetChild(0) && !node->GetChild(1) ) {
MergeWithAdjacentLeafNodes( bsp, node );
node->SetFlag( NODE_DONE );
return;
}
MergeLeafNodes_r( bsp, node->GetChild(0) );
MergeLeafNodes_r( bsp, node->GetChild(1) );
return;
}
/*
============
idAASBuild::MergeLeafNodes
============
*/
void idAASBuild::MergeLeafNodes( idBrushBSP &bsp ) {
numMergedLeafNodes = 0;
common->Printf( "[Merge Leaf Nodes]\n" );
MergeLeafNodes_r( bsp, bsp.GetRootNode() );
bsp.GetRootNode()->RemoveFlagRecurse( NODE_DONE );
bsp.PruneMergedTree_r( bsp.GetRootNode() );
common->Printf( "\r%6d leaf nodes merged\n", numMergedLeafNodes );
}

View File

@@ -0,0 +1,557 @@
/*
===========================================================================
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 "../../../idlib/precompiled.h"
#pragma hdrstop
#include "AASFile.h"
#include "AASFile_local.h"
#include "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;
}

View File

@@ -0,0 +1,62 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __AASCLUSTER_H__
#define __AASCLUSTER_H__
/*
===============================================================================
Area Clustering
===============================================================================
*/
class idAASCluster {
public:
bool Build( idAASFileLocal *file );
bool BuildSingleCluster( idAASFileLocal *file );
private:
idAASFileLocal * file;
bool noFaceFlood;
private:
bool UpdatePortal( int areaNum, int clusterNum );
bool FloodClusterAreas_r( int areaNum, int clusterNum );
void RemoveAreaClusterNumbers( void );
void NumberClusterAreas( int clusterNum );
bool FindClusters( void );
void CreatePortals( void );
bool TestPortals( void );
void ReportEfficiency( void );
void RemoveInvalidPortals( void );
};
#endif /* !__AASCLUSTER_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,351 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __AASFILE_H__
#define __AASFILE_H__
/*
===============================================================================
AAS File
===============================================================================
*/
#define AAS_FILEID "DewmAAS"
#define AAS_FILEVERSION "1.07"
// travel flags
#define TFL_INVALID BIT(0) // not valid
#define TFL_WALK BIT(1) // walking
#define TFL_CROUCH BIT(2) // crouching
#define TFL_WALKOFFLEDGE BIT(3) // walking of a ledge
#define TFL_BARRIERJUMP BIT(4) // jumping onto a barrier
#define TFL_JUMP BIT(5) // jumping
#define TFL_LADDER BIT(6) // climbing a ladder
#define TFL_SWIM BIT(7) // swimming
#define TFL_WATERJUMP BIT(8) // jump out of the water
#define TFL_TELEPORT BIT(9) // teleportation
#define TFL_ELEVATOR BIT(10) // travel by elevator
#define TFL_FLY BIT(11) // fly
#define TFL_SPECIAL BIT(12) // special
#define TFL_WATER BIT(21) // travel through water
#define TFL_AIR BIT(22) // travel through air
// face flags
#define FACE_SOLID BIT(0) // solid at the other side
#define FACE_LADDER BIT(1) // ladder surface
#define FACE_FLOOR BIT(2) // standing on floor when on this face
#define FACE_LIQUID BIT(3) // face seperating two areas with liquid
#define FACE_LIQUIDSURFACE BIT(4) // face seperating liquid and air
// area flags
#define AREA_FLOOR BIT(0) // AI can stand on the floor in this area
#define AREA_GAP BIT(1) // area has a gap
#define AREA_LEDGE BIT(2) // if entered the AI bbox partly floats above a ledge
#define AREA_LADDER BIT(3) // area contains one or more ladder faces
#define AREA_LIQUID BIT(4) // area contains a liquid
#define AREA_CROUCH BIT(5) // AI cannot walk but can only crouch in this area
#define AREA_REACHABLE_WALK BIT(6) // area is reachable by walking or swimming
#define AREA_REACHABLE_FLY BIT(7) // area is reachable by flying
// area contents flags
#define AREACONTENTS_SOLID BIT(0) // solid, not a valid area
#define AREACONTENTS_WATER BIT(1) // area contains water
#define AREACONTENTS_CLUSTERPORTAL BIT(2) // area is a cluster portal
#define AREACONTENTS_OBSTACLE BIT(3) // area contains (part of) a dynamic obstacle
#define AREACONTENTS_TELEPORTER BIT(4) // area contains (part of) a teleporter trigger
// bits for different bboxes
#define AREACONTENTS_BBOX_BIT 24
#define MAX_REACH_PER_AREA 256
#define MAX_AAS_TREE_DEPTH 128
#define MAX_AAS_BOUNDING_BOXES 4
// reachability to another area
class idReachability {
public:
int travelType; // type of travel required to get to the area
short toAreaNum; // number of the reachable area
short fromAreaNum; // number of area the reachability starts
idVec3 start; // start point of inter area movement
idVec3 end; // end point of inter area movement
int edgeNum; // edge crossed by this reachability
unsigned short travelTime; // travel time of the inter area movement
byte number; // reachability number within the fromAreaNum (must be < 256)
byte disableCount; // number of times this reachability has been disabled
idReachability * next; // next reachability in list
idReachability * rev_next; // next reachability in reversed list
unsigned short * areaTravelTimes; // travel times within the fromAreaNum from reachabilities that lead towards this area
public:
void CopyBase( idReachability &reach );
};
class idReachability_Walk : public idReachability {
};
class idReachability_BarrierJump : public idReachability {
};
class idReachability_WaterJump : public idReachability {
};
class idReachability_WalkOffLedge : public idReachability {
};
class idReachability_Swim : public idReachability {
};
class idReachability_Fly : public idReachability {
};
class idReachability_Special : public idReachability {
public:
idDict dict;
};
// index
typedef int aasIndex_t;
// vertex
typedef idVec3 aasVertex_t;
// edge
typedef struct aasEdge_s {
int vertexNum[2]; // numbers of the vertexes of this edge
} aasEdge_t;
// area boundary face
typedef struct aasFace_s {
unsigned short planeNum; // number of the plane this face is on
unsigned short flags; // face flags
int numEdges; // number of edges in the boundary of the face
int firstEdge; // first edge in the edge index
short areas[2]; // area at the front and back of this face
} aasFace_t;
// area with a boundary of faces
typedef struct aasArea_s {
int numFaces; // number of faces used for the boundary of the area
int firstFace; // first face in the face index used for the boundary of the area
idBounds bounds; // bounds of the area
idVec3 center; // center of the area an AI can move towards
unsigned short flags; // several area flags
unsigned short contents; // contents of the area
short cluster; // cluster the area belongs to, if negative it's a portal
short clusterAreaNum; // number of the area in the cluster
int travelFlags; // travel flags for traveling through this area
idReachability * reach; // reachabilities that start from this area
idReachability * rev_reach; // reachabilities that lead to this area
} aasArea_t;
// nodes of the bsp tree
typedef struct aasNode_s {
unsigned short planeNum; // number of the plane that splits the subspace at this node
int children[2]; // child nodes, zero is solid, negative is -(area number)
} aasNode_t;
// cluster portal
typedef struct aasPortal_s {
short areaNum; // number of the area that is the actual portal
short clusters[2]; // number of cluster at the front and back of the portal
short clusterAreaNum[2]; // number of this portal area in the front and back cluster
unsigned short maxAreaTravelTime; // maximum travel time through the portal area
} aasPortal_t;
// cluster
typedef struct aasCluster_s {
int numAreas; // number of areas in the cluster
int numReachableAreas; // number of areas with reachabilities
int numPortals; // number of cluster portals
int firstPortal; // first cluster portal in the index
} aasCluster_t;
// trace through the world
typedef struct aasTrace_s {
// parameters
int flags; // areas with these flags block the trace
int travelFlags; // areas with these travel flags block the trace
int maxAreas; // size of the 'areas' array
int getOutOfSolid; // trace out of solid if the trace starts in solid
// output
float fraction; // fraction of trace completed
idVec3 endpos; // end position of trace
int planeNum; // plane hit
int lastAreaNum; // number of last area the trace went through
int blockingAreaNum; // area that could not be entered
int numAreas; // number of areas the trace went through
int * areas; // array to store areas the trace went through
idVec3 * points; // points where the trace entered each new area
aasTrace_s( void ) { areas = NULL; points = NULL; getOutOfSolid = false; flags = travelFlags = maxAreas = 0; }
} aasTrace_t;
// settings
class idAASSettings {
public:
// collision settings
int numBoundingBoxes;
idBounds boundingBoxes[MAX_AAS_BOUNDING_BOXES];
bool usePatches;
bool writeBrushMap;
bool playerFlood;
bool noOptimize;
bool allowSwimReachabilities;
bool allowFlyReachabilities;
idStr fileExtension;
// physics settings
idVec3 gravity;
idVec3 gravityDir;
idVec3 invGravityDir;
float gravityValue;
float maxStepHeight;
float maxBarrierHeight;
float maxWaterJumpHeight;
float maxFallHeight;
float minFloorCos;
// fixed travel times
int tt_barrierJump;
int tt_startCrouching;
int tt_waterJump;
int tt_startWalkOffLedge;
public:
idAASSettings( void );
bool FromFile( const idStr &fileName );
bool FromParser( idLexer &src );
bool FromDict( const char *name, const idDict *dict );
bool WriteToFile( idFile *fp ) const;
bool ValidForBounds( const idBounds &bounds ) const;
bool ValidEntity( const char *classname ) const;
private:
bool ParseBool( idLexer &src, bool &b );
bool ParseInt( idLexer &src, int &i );
bool ParseFloat( idLexer &src, float &f );
bool ParseVector( idLexer &src, idVec3 &vec );
bool ParseBBoxes( idLexer &src );
};
/*
- when a node child is a solid leaf the node child number is zero
- two adjacent areas (sharing a plane at opposite sides) share a face
this face is a portal between the areas
- when an area uses a face from the faceindex with a positive index
then the face plane normal points into the area
- the face edges are stored counter clockwise using the edgeindex
- two adjacent convex areas (sharing a face) only share One face
this is a simple result of the areas being convex
- the areas can't have a mixture of ground and gap faces
other mixtures of faces in one area are allowed
- areas with the AREACONTENTS_CLUSTERPORTAL in the settings have
the cluster number set to the negative portal number
- edge zero is a dummy
- face zero is a dummy
- area zero is a dummy
- node zero is a dummy
- portal zero is a dummy
- cluster zero is a dummy
*/
class idAASFile {
public:
virtual ~idAASFile( void ) {}
const char * GetName( void ) const { return name.c_str(); }
unsigned int GetCRC( void ) const { return crc; }
int GetNumPlanes( void ) const { return planeList.Num(); }
const idPlane & GetPlane( int index ) const { return planeList[index]; }
int GetNumVertices( void ) const { return vertices.Num(); }
const aasVertex_t & GetVertex( int index ) const { return vertices[index]; }
int GetNumEdges( void ) const { return edges.Num(); }
const aasEdge_t & GetEdge( int index ) const { return edges[index]; }
int GetNumEdgeIndexes( void ) const { return edgeIndex.Num(); }
const aasIndex_t & GetEdgeIndex( int index ) const { return edgeIndex[index]; }
int GetNumFaces( void ) const { return faces.Num(); }
const aasFace_t & GetFace( int index ) const { return faces[index]; }
int GetNumFaceIndexes( void ) const { return faceIndex.Num(); }
const aasIndex_t & GetFaceIndex( int index ) const { return faceIndex[index]; }
int GetNumAreas( void ) const { return areas.Num(); }
const aasArea_t & GetArea( int index ) { return areas[index]; }
int GetNumNodes( void ) const { return nodes.Num(); }
const aasNode_t & GetNode( int index ) const { return nodes[index]; }
int GetNumPortals( void ) const { return portals.Num(); }
const aasPortal_t & GetPortal( int index ) { return portals[index]; }
int GetNumPortalIndexes( void ) const { return portalIndex.Num(); }
const aasIndex_t & GetPortalIndex( int index ) const { return portalIndex[index]; }
int GetNumClusters( void ) const { return clusters.Num(); }
const aasCluster_t & GetCluster( int index ) const { return clusters[index]; }
const idAASSettings & GetSettings( void ) const { return settings; }
void SetPortalMaxTravelTime( int index, int time ) { portals[index].maxAreaTravelTime = time; }
void SetAreaTravelFlag( int index, int flag ) { areas[index].travelFlags |= flag; }
void RemoveAreaTravelFlag( int index, int flag ) { areas[index].travelFlags &= ~flag; }
virtual idVec3 EdgeCenter( int edgeNum ) const = 0;
virtual idVec3 FaceCenter( int faceNum ) const = 0;
virtual idVec3 AreaCenter( int areaNum ) const = 0;
virtual idBounds EdgeBounds( int edgeNum ) const = 0;
virtual idBounds FaceBounds( int faceNum ) const = 0;
virtual idBounds AreaBounds( int areaNum ) const = 0;
virtual int PointAreaNum( const idVec3 &origin ) const = 0;
virtual int PointReachableAreaNum( const idVec3 &origin, const idBounds &searchBounds, const int areaFlags, const int excludeTravelFlags ) const = 0;
virtual int BoundsReachableAreaNum( const idBounds &bounds, const int areaFlags, const int excludeTravelFlags ) const = 0;
virtual void PushPointIntoAreaNum( int areaNum, idVec3 &point ) const = 0;
virtual bool Trace( aasTrace_t &trace, const idVec3 &start, const idVec3 &end ) const = 0;
virtual void PrintInfo( void ) const = 0;
protected:
idStr name;
unsigned int crc;
idPlaneSet planeList;
idList<aasVertex_t> vertices;
idList<aasEdge_t> edges;
idList<aasIndex_t> edgeIndex;
idList<aasFace_t> faces;
idList<aasIndex_t> faceIndex;
idList<aasArea_t> areas;
idList<aasNode_t> nodes;
idList<aasPortal_t> portals;
idList<aasIndex_t> portalIndex;
idList<aasCluster_t> clusters;
idAASSettings settings;
};
#endif /* !__AASFILE_H__ */

View File

@@ -0,0 +1,76 @@
/*
===========================================================================
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 "../../../idlib/precompiled.h"
#pragma hdrstop
#include "AASFile.h"
#include "AASFile_local.h"
/*
===============================================================================
AAS File Manager
===============================================================================
*/
class idAASFileManagerLocal : public idAASFileManager {
public:
virtual ~idAASFileManagerLocal( void ) {}
virtual idAASFile * LoadAAS( const char *fileName, unsigned int mapFileCRC );
virtual void FreeAAS( idAASFile *file );
};
idAASFileManagerLocal AASFileManagerLocal;
idAASFileManager * AASFileManager = &AASFileManagerLocal;
/*
================
idAASFileManagerLocal::LoadAAS
================
*/
idAASFile *idAASFileManagerLocal::LoadAAS( const char *fileName, unsigned int mapFileCRC ) {
idAASFileLocal *file = new idAASFileLocal();
if ( !file->Load( fileName, mapFileCRC ) ) {
delete file;
return NULL;
}
return file;
}
/*
================
idAASFileManagerLocal::FreeAAS
================
*/
void idAASFileManagerLocal::FreeAAS( idAASFile *file ) {
delete file;
}

View File

@@ -0,0 +1,50 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __AASFILEMANAGER_H__
#define __AASFILEMANAGER_H__
/*
===============================================================================
AAS File Manager
===============================================================================
*/
class idAASFileManager {
public:
virtual ~idAASFileManager( void ) {}
virtual idAASFile * LoadAAS( const char *fileName, unsigned int mapFileCRC ) = 0;
virtual void FreeAAS( idAASFile *file ) = 0;
};
extern idAASFileManager * AASFileManager;
#endif /* !__AASFILEMANAGER_H__ */

View File

@@ -0,0 +1,99 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __AASFILELOCAL_H__
#define __AASFILELOCAL_H__
/*
===============================================================================
AAS File Local
===============================================================================
*/
class idAASFileLocal : public idAASFile {
friend class idAASBuild;
friend class idAASReach;
friend class idAASCluster;
public:
idAASFileLocal( void );
virtual ~idAASFileLocal( void );
public:
virtual idVec3 EdgeCenter( int edgeNum ) const;
virtual idVec3 FaceCenter( int faceNum ) const;
virtual idVec3 AreaCenter( int areaNum ) const;
virtual idBounds EdgeBounds( int edgeNum ) const;
virtual idBounds FaceBounds( int faceNum ) const;
virtual idBounds AreaBounds( int areaNum ) const;
virtual int PointAreaNum( const idVec3 &origin ) const;
virtual int PointReachableAreaNum( const idVec3 &origin, const idBounds &searchBounds, const int areaFlags, const int excludeTravelFlags ) const;
virtual int BoundsReachableAreaNum( const idBounds &bounds, const int areaFlags, const int excludeTravelFlags ) const;
virtual void PushPointIntoAreaNum( int areaNum, idVec3 &point ) const;
virtual bool Trace( aasTrace_t &trace, const idVec3 &start, const idVec3 &end ) const;
virtual void PrintInfo( void ) const;
public:
bool Load( const idStr &fileName, unsigned int mapFileCRC );
bool Write( const idStr &fileName, unsigned int mapFileCRC );
int MemorySize( void ) const;
void ReportRoutingEfficiency( void ) const;
void Optimize( void );
void LinkReversedReachability( void );
void FinishAreas( void );
void Clear( void );
void DeleteReachabilities( void );
void DeleteClusters( void );
private:
bool ParseIndex( idLexer &src, idList<aasIndex_t> &indexes );
bool ParsePlanes( idLexer &src );
bool ParseVertices( idLexer &src );
bool ParseEdges( idLexer &src );
bool ParseFaces( idLexer &src );
bool ParseReachabilities( idLexer &src, int areaNum );
bool ParseAreas( idLexer &src );
bool ParseNodes( idLexer &src );
bool ParsePortals( idLexer &src );
bool ParseClusters( idLexer &src );
private:
int BoundsReachableAreaNum_r( int nodeNum, const idBounds &bounds, const int areaFlags, const int excludeTravelFlags ) const;
void MaxTreeDepth_r( int nodeNum, int &depth, int &maxDepth ) const;
int MaxTreeDepth( void ) const;
int AreaContentsTravelFlags( int areaNum ) const;
idVec3 AreaReachableGoal( int areaNum ) const;
int NumReachabilities( void ) const;
};
#endif /* !__AASFILELOCAL_H__ */

View File

@@ -0,0 +1,154 @@
/*
===========================================================================
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 "../../../idlib/precompiled.h"
#pragma hdrstop
#include "AASFile.h"
#include "AASFile_local.h"
//===============================================================
//
// optimize file
//
//===============================================================
/*
================
idAASFileLocal::Optimize
================
*/
void idAASFileLocal::Optimize( void ) {
int i, j, k, faceNum, edgeNum, areaFirstFace, faceFirstEdge;
aasArea_t *area;
aasFace_t *face;
aasEdge_t *edge;
idReachability *reach;
idList<int> vertexRemap;
idList<int> edgeRemap;
idList<int> faceRemap;
idList<aasVertex_t> newVertices;
idList<aasEdge_t> newEdges;
idList<aasIndex_t> newEdgeIndex;
idList<aasFace_t> newFaces;
idList<aasIndex_t> newFaceIndex;
vertexRemap.AssureSize( vertices.Num(), -1 );
edgeRemap.AssureSize( edges.Num(), 0 );
faceRemap.AssureSize( faces.Num(), 0 );
newVertices.Resize( vertices.Num() );
newEdges.Resize( edges.Num() );
newEdges.SetNum( 1, false );
newEdgeIndex.Resize( edgeIndex.Num() );
newFaces.Resize( faces.Num() );
newFaces.SetNum( 1, false );
newFaceIndex.Resize( faceIndex.Num() );
for ( i = 0; i < areas.Num(); i++ ) {
area = &areas[i];
areaFirstFace = newFaceIndex.Num();
for ( j = 0; j < area->numFaces; j++ ) {
faceNum = faceIndex[area->firstFace + j];
face = &faces[ abs(faceNum) ];
// store face
if ( !faceRemap[ abs(faceNum) ] ) {
faceRemap[ abs(faceNum) ] = newFaces.Num();
newFaces.Append( *face );
// don't store edges for faces we don't care about
if ( !( face->flags & ( FACE_FLOOR|FACE_LADDER ) ) ) {
newFaces[ newFaces.Num()-1 ].firstEdge = 0;
newFaces[ newFaces.Num()-1 ].numEdges = 0;
} else {
// store edges
faceFirstEdge = newEdgeIndex.Num();
for ( k = 0; k < face->numEdges; k++ ) {
edgeNum = edgeIndex[ face->firstEdge + k ];
edge = &edges[ abs(edgeNum) ];
if ( !edgeRemap[ abs(edgeNum) ] ) {
if ( edgeNum < 0 ) {
edgeRemap[ abs(edgeNum) ] = -newEdges.Num();
}
else {
edgeRemap[ abs(edgeNum) ] = newEdges.Num();
}
// remap vertices if not yet remapped
if ( vertexRemap[ edge->vertexNum[0] ] == -1 ) {
vertexRemap[ edge->vertexNum[0] ] = newVertices.Num();
newVertices.Append( vertices[ edge->vertexNum[0] ] );
}
if ( vertexRemap[ edge->vertexNum[1] ] == -1 ) {
vertexRemap[ edge->vertexNum[1] ] = newVertices.Num();
newVertices.Append( vertices[ edge->vertexNum[1] ] );
}
newEdges.Append( *edge );
newEdges[ newEdges.Num()-1 ].vertexNum[0] = vertexRemap[ edge->vertexNum[0] ];
newEdges[ newEdges.Num()-1 ].vertexNum[1] = vertexRemap[ edge->vertexNum[1] ];
}
newEdgeIndex.Append( edgeRemap[ abs(edgeNum) ] );
}
newFaces[ newFaces.Num()-1 ].firstEdge = faceFirstEdge;
newFaces[ newFaces.Num()-1 ].numEdges = newEdgeIndex.Num() - faceFirstEdge;
}
}
if ( faceNum < 0 ) {
newFaceIndex.Append( -faceRemap[ abs(faceNum) ] );
} else {
newFaceIndex.Append( faceRemap[ abs(faceNum) ] );
}
}
area->firstFace = areaFirstFace;
area->numFaces = newFaceIndex.Num() - areaFirstFace;
// remap the reachability edges
for ( reach = area->reach; reach; reach = reach->next ) {
reach->edgeNum = abs( edgeRemap[reach->edgeNum] );
}
}
// store new list
vertices = newVertices;
edges = newEdges;
edgeIndex = newEdgeIndex;
faces = newFaces;
faceIndex = newFaceIndex;
}

View File

@@ -0,0 +1,608 @@
/*
===========================================================================
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 "../../../idlib/precompiled.h"
#pragma hdrstop
#include "AASFile.h"
#include "AASFile_local.h"
//===============================================================
//
// Environment Sampling
//
//===============================================================
/*
================
idAASFileLocal::EdgeCenter
================
*/
idVec3 idAASFileLocal::EdgeCenter( int edgeNum ) const {
const aasEdge_t *edge;
edge = &edges[edgeNum];
return ( vertices[edge->vertexNum[0]] + vertices[edge->vertexNum[1]] ) * 0.5f;
}
/*
================
idAASFileLocal::FaceCenter
================
*/
idVec3 idAASFileLocal::FaceCenter( int faceNum ) const {
int i, edgeNum;
const aasFace_t *face;
const aasEdge_t *edge;
idVec3 center;
center = vec3_origin;
face = &faces[faceNum];
if ( face->numEdges > 0 ) {
for ( i = 0; i < face->numEdges; i++ ) {
edgeNum = edgeIndex[ face->firstEdge + i ];
edge = &edges[ abs( edgeNum ) ];
center += vertices[ edge->vertexNum[ INTSIGNBITSET(edgeNum) ] ];
}
center /= face->numEdges;
}
return center;
}
/*
================
idAASFileLocal::AreaCenter
================
*/
idVec3 idAASFileLocal::AreaCenter( int areaNum ) const {
int i, faceNum;
const aasArea_t *area;
idVec3 center;
center = vec3_origin;
area = &areas[areaNum];
if ( area->numFaces > 0 ) {
for ( i = 0; i < area->numFaces; i++ ) {
faceNum = faceIndex[area->firstFace + i];
center += FaceCenter( abs(faceNum) );
}
center /= area->numFaces;
}
return center;
}
/*
============
idAASFileLocal::AreaReachableGoal
============
*/
idVec3 idAASFileLocal::AreaReachableGoal( int areaNum ) const {
int i, faceNum, numFaces;
const aasArea_t *area;
idVec3 center;
idVec3 start, end;
aasTrace_t trace;
area = &areas[areaNum];
if ( !(area->flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) || (area->flags & AREA_LIQUID) ) {
return AreaCenter( areaNum );
}
center = vec3_origin;
numFaces = 0;
for ( i = 0; i < area->numFaces; i++ ) {
faceNum = faceIndex[area->firstFace + i];
if ( !(faces[abs(faceNum)].flags & FACE_FLOOR) ) {
continue;
}
center += FaceCenter( abs(faceNum) );
numFaces++;
}
if ( numFaces > 0 ) {
center /= numFaces;
}
center[2] += 1.0f;
end = center;
end[2] -= 1024;
Trace( trace, center, end );
return trace.endpos;
}
/*
================
idAASFileLocal::EdgeBounds
================
*/
idBounds idAASFileLocal::EdgeBounds( int edgeNum ) const {
const aasEdge_t *edge;
idBounds bounds;
edge = &edges[ abs( edgeNum ) ];
bounds[0] = bounds[1] = vertices[ edge->vertexNum[0] ];
bounds += vertices[ edge->vertexNum[1] ];
return bounds;
}
/*
================
idAASFileLocal::FaceBounds
================
*/
idBounds idAASFileLocal::FaceBounds( int faceNum ) const {
int i, edgeNum;
const aasFace_t *face;
const aasEdge_t *edge;
idBounds bounds;
face = &faces[faceNum];
bounds.Clear();
for ( i = 0; i < face->numEdges; i++ ) {
edgeNum = edgeIndex[ face->firstEdge + i ];
edge = &edges[ abs( edgeNum ) ];
bounds.AddPoint( vertices[ edge->vertexNum[ INTSIGNBITSET(edgeNum) ] ] );
}
return bounds;
}
/*
================
idAASFileLocal::AreaBounds
================
*/
idBounds idAASFileLocal::AreaBounds( int areaNum ) const {
int i, faceNum;
const aasArea_t *area;
idBounds bounds;
area = &areas[areaNum];
bounds.Clear();
for ( i = 0; i < area->numFaces; i++ ) {
faceNum = faceIndex[area->firstFace + i];
bounds += FaceBounds( abs(faceNum) );
}
return bounds;
}
/*
============
idAASFileLocal::PointAreaNum
============
*/
int idAASFileLocal::PointAreaNum( const idVec3 &origin ) const {
int nodeNum;
const aasNode_t *node;
nodeNum = 1;
do {
node = &nodes[nodeNum];
if ( planeList[node->planeNum].Side( origin ) == PLANESIDE_BACK ) {
nodeNum = node->children[1];
}
else {
nodeNum = node->children[0];
}
if ( nodeNum < 0 ) {
return -nodeNum;
}
} while( nodeNum );
return 0;
}
/*
============
idAASFileLocal::PointReachableAreaNum
============
*/
int idAASFileLocal::PointReachableAreaNum( const idVec3 &origin, const idBounds &searchBounds, const int areaFlags, const int excludeTravelFlags ) const {
int areaList[32], areaNum, i;
idVec3 start, end, pointList[32];
aasTrace_t trace;
idBounds bounds;
float frac;
start = origin;
trace.areas = areaList;
trace.points = pointList;
trace.maxAreas = sizeof( areaList ) / sizeof( int );
trace.getOutOfSolid = true;
areaNum = PointAreaNum( start );
if ( areaNum ) {
if ( ( areas[areaNum].flags & areaFlags ) && ( ( areas[areaNum].travelFlags & excludeTravelFlags ) == 0 ) ) {
return areaNum;
}
}
else {
// trace up
end = start;
end[2] += 32.0f;
Trace( trace, start, end );
if ( trace.numAreas >= 1 ) {
if ( ( areas[0].flags & areaFlags ) && ( ( areas[0].travelFlags & excludeTravelFlags ) == 0 ) ) {
return areaList[0];
}
start = pointList[0];
start[2] += 1.0f;
}
}
// trace down
end = start;
end[2] -= 32.0f;
Trace( trace, start, end );
if ( trace.lastAreaNum ) {
if ( ( areas[trace.lastAreaNum].flags & areaFlags ) && ( ( areas[trace.lastAreaNum].travelFlags & excludeTravelFlags ) == 0 ) ) {
return trace.lastAreaNum;
}
start = trace.endpos;
}
// expand bounds until an area is found
for ( i = 1; i <= 12; i++ ) {
frac = i * ( 1.0f / 12.0f );
bounds[0] = origin + searchBounds[0] * frac;
bounds[1] = origin + searchBounds[1] * frac;
areaNum = BoundsReachableAreaNum( bounds, areaFlags, excludeTravelFlags );
if ( areaNum && ( areas[areaNum].flags & areaFlags ) && ( ( areas[areaNum].travelFlags & excludeTravelFlags ) == 0 ) ) {
return areaNum;
}
}
return 0;
}
/*
============
idAASFileLocal::BoundsReachableAreaNum_r
============
*/
int idAASFileLocal::BoundsReachableAreaNum_r( int nodeNum, const idBounds &bounds, const int areaFlags, const int excludeTravelFlags ) const {
int res;
const aasNode_t *node;
while( nodeNum ) {
if ( nodeNum < 0 ) {
if ( ( areas[-nodeNum].flags & areaFlags ) && ( ( areas[-nodeNum].travelFlags & excludeTravelFlags ) == 0 ) ) {
return -nodeNum;
}
return 0;
}
node = &nodes[nodeNum];
res = bounds.PlaneSide( planeList[node->planeNum] );
if ( res == PLANESIDE_BACK ) {
nodeNum = node->children[1];
}
else if ( res == PLANESIDE_FRONT ) {
nodeNum = node->children[0];
}
else {
nodeNum = BoundsReachableAreaNum_r( node->children[1], bounds, areaFlags, excludeTravelFlags );
if ( nodeNum ) {
return nodeNum;
}
nodeNum = node->children[0];
}
}
return 0;
}
/*
============
idAASFileLocal::BoundsReachableAreaNum
============
*/
int idAASFileLocal::BoundsReachableAreaNum( const idBounds &bounds, const int areaFlags, const int excludeTravelFlags ) const {
return BoundsReachableAreaNum_r( 1, bounds, areaFlags, excludeTravelFlags );
}
/*
============
idAASFileLocal::PushPointIntoAreaNum
============
*/
void idAASFileLocal::PushPointIntoAreaNum( int areaNum, idVec3 &point ) const {
int i, faceNum;
const aasArea_t *area;
const aasFace_t *face;
area = &areas[areaNum];
// push the point to the right side of all area face planes
for ( i = 0; i < area->numFaces; i++ ) {
faceNum = faceIndex[area->firstFace + i];
face = &faces[abs( faceNum )];
const idPlane &plane = planeList[face->planeNum ^ INTSIGNBITSET( faceNum )];
float dist = plane.Distance( point );
// project the point onto the face plane if it is on the wrong side
if ( dist < 0.0f ) {
point -= dist * plane.Normal();
}
}
}
/*
============
idAASFileLocal::Trace
============
*/
#define TRACEPLANE_EPSILON 0.125f
typedef struct aasTraceStack_s
{
idVec3 start;
idVec3 end;
int planeNum;
int nodeNum;
} aasTraceStack_t;
bool idAASFileLocal::Trace( aasTrace_t &trace, const idVec3 &start, const idVec3 &end ) const {
int side, nodeNum, tmpPlaneNum;
double front, back, frac;
idVec3 cur_start, cur_end, cur_mid, v1, v2;
aasTraceStack_t tracestack[MAX_AAS_TREE_DEPTH];
aasTraceStack_t *tstack_p;
const aasNode_t *node;
const idPlane *plane;
trace.numAreas = 0;
trace.lastAreaNum = 0;
trace.blockingAreaNum = 0;
tstack_p = tracestack;
tstack_p->start = start;
tstack_p->end = end;
tstack_p->planeNum = 0;
tstack_p->nodeNum = 1; //start with the root of the tree
tstack_p++;
while( 1 ) {
tstack_p--;
// if the trace stack is empty
if ( tstack_p < tracestack ) {
if ( !trace.lastAreaNum ) {
// completely in solid
trace.fraction = 0.0f;
trace.endpos = start;
}
else {
// nothing was hit
trace.fraction = 1.0f;
trace.endpos = end;
}
trace.planeNum = 0;
return false;
}
// number of the current node to test the line against
nodeNum = tstack_p->nodeNum;
// if it is an area
if ( nodeNum < 0) {
// if can't enter the area
if ( ( areas[-nodeNum].flags & trace.flags ) || ( areas[-nodeNum].travelFlags & trace.travelFlags ) ) {
if ( !trace.lastAreaNum ) {
trace.fraction = 0.0f;
v1 = vec3_origin;
} else {
v1 = end - start;
v2 = tstack_p->start - start;
trace.fraction = v2.Length() / v1.Length();
}
trace.endpos = tstack_p->start;
trace.blockingAreaNum = -nodeNum;
trace.planeNum = tstack_p->planeNum;
// always take the plane with normal facing towards the trace start
plane = &planeList[trace.planeNum];
if ( v1 * plane->Normal() > 0.0f ) {
trace.planeNum ^= 1;
}
return true;
}
trace.lastAreaNum = -nodeNum;
if ( trace.numAreas < trace.maxAreas ) {
if ( trace.areas ) {
trace.areas[trace.numAreas] = -nodeNum;
}
if ( trace.points ) {
trace.points[trace.numAreas] = tstack_p->start;
}
trace.numAreas++;
}
continue;
}
// if it is a solid leaf
if ( !nodeNum ) {
if ( !trace.lastAreaNum ) {
trace.fraction = 0.0f;
v1 = vec3_origin;
} else {
v1 = end - start;
v2 = tstack_p->start - start;
trace.fraction = v2.Length() / v1.Length();
}
trace.endpos = tstack_p->start;
trace.blockingAreaNum = 0; // hit solid leaf
trace.planeNum = tstack_p->planeNum;
// always take the plane with normal facing towards the trace start
plane = &planeList[trace.planeNum];
if ( v1 * plane->Normal() > 0.0f ) {
trace.planeNum ^= 1;
}
if ( !trace.lastAreaNum && trace.getOutOfSolid ) {
continue;
}
else {
return true;
}
}
// the node to test against
node = &nodes[nodeNum];
// start point of current line to test against node
cur_start = tstack_p->start;
// end point of the current line to test against node
cur_end = tstack_p->end;
// the current node plane
plane = &planeList[node->planeNum];
front = plane->Distance( cur_start );
back = plane->Distance( cur_end );
// if the whole to be traced line is totally at the front of this node
// only go down the tree with the front child
if ( front >= -ON_EPSILON && back >= -ON_EPSILON ) {
// keep the current start and end point on the stack and go down the tree with the front child
tstack_p->nodeNum = node->children[0];
tstack_p++;
if ( tstack_p >= &tracestack[MAX_AAS_TREE_DEPTH] ) {
common->Error("idAASFileLocal::Trace: stack overflow\n" );
return false;
}
}
// if the whole to be traced line is totally at the back of this node
// only go down the tree with the back child
else if ( front < ON_EPSILON && back < ON_EPSILON ) {
// keep the current start and end point on the stack and go down the tree with the back child
tstack_p->nodeNum = node->children[1];
tstack_p++;
if ( tstack_p >= &tracestack[MAX_AAS_TREE_DEPTH] ) {
common->Error("idAASFileLocal::Trace: stack overflow\n" );
return false;
}
}
// go down the tree both at the front and back of the node
else {
tmpPlaneNum = tstack_p->planeNum;
// calculate the hit point with the node plane
// put the cross point TRACEPLANE_EPSILON on the near side
if (front < 0) {
frac = (front + TRACEPLANE_EPSILON) / ( front - back );
}
else {
frac = (front - TRACEPLANE_EPSILON) / ( front - back );
}
if (frac < 0) {
frac = 0.001f; //0
}
else if (frac > 1) {
frac = 0.999f; //1
}
cur_mid = cur_start + ( cur_end - cur_start ) * frac;
// side the front part of the line is on
side = front < 0;
// first put the end part of the line on the stack (back side)
tstack_p->start = cur_mid;
tstack_p->planeNum = node->planeNum;
tstack_p->nodeNum = node->children[!side];
tstack_p++;
if ( tstack_p >= &tracestack[MAX_AAS_TREE_DEPTH] ) {
common->Error("idAASFileLocal::Trace: stack overflow\n" );
return false;
}
// now put the part near the start of the line on the stack so we will
// continue with that part first.
tstack_p->start = cur_start;
tstack_p->end = cur_mid;
tstack_p->planeNum = tmpPlaneNum;
tstack_p->nodeNum = node->children[side];
tstack_p++;
if ( tstack_p >= &tracestack[MAX_AAS_TREE_DEPTH] ) {
common->Error("idAASFileLocal::Trace: stack overflow\n" );
return false;
}
}
}
return false;
}
/*
============
idAASLocal::AreaContentsTravelFlags
============
*/
int idAASFileLocal::AreaContentsTravelFlags( int areaNum ) const {
if ( areas[areaNum].contents & AREACONTENTS_WATER ) {
return TFL_WATER;
}
return TFL_AIR;
}
/*
============
idAASFileLocal::MaxTreeDepth_r
============
*/
void idAASFileLocal::MaxTreeDepth_r( int nodeNum, int &depth, int &maxDepth ) const {
const aasNode_t *node;
if ( nodeNum <= 0 ) {
return;
}
depth++;
if ( depth > maxDepth ) {
maxDepth = depth;
}
node = &nodes[nodeNum];
MaxTreeDepth_r( node->children[0], depth, maxDepth );
MaxTreeDepth_r( node->children[1], depth, maxDepth );
depth--;
}
/*
============
idAASFileLocal::MaxTreeDepth
============
*/
int idAASFileLocal::MaxTreeDepth( void ) const {
int depth, maxDepth;
depth = maxDepth = 0;
MaxTreeDepth_r( 1, depth, maxDepth );
return maxDepth;
}

View File

@@ -0,0 +1,945 @@
/*
===========================================================================
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 "../../../idlib/precompiled.h"
#pragma hdrstop
#include "AASFile.h"
#include "AASFile_local.h"
#include "AASReach.h"
#define INSIDEUNITS 2.0f
#define INSIDEUNITS_WALKEND 0.5f
#define INSIDEUNITS_WALKSTART 0.1f
#define INSIDEUNITS_SWIMEND 0.5f
#define INSIDEUNITS_FLYEND 0.5f
#define INSIDEUNITS_WATERJUMP 15.0f
/*
================
idAASReach::ReachabilityExists
================
*/
bool idAASReach::ReachabilityExists( int fromAreaNum, int toAreaNum ) {
aasArea_t *area;
idReachability *reach;
area = &file->areas[fromAreaNum];
for ( reach = area->reach; reach; reach = reach->next ) {
if ( reach->toAreaNum == toAreaNum ) {
return true;
}
}
return false;
}
/*
================
idAASReach::CanSwimInArea
================
*/
ID_INLINE bool idAASReach::CanSwimInArea( int areaNum ) {
return ( file->areas[areaNum].contents & AREACONTENTS_WATER ) != 0;
}
/*
================
idAASReach::AreaHasFloor
================
*/
ID_INLINE bool idAASReach::AreaHasFloor( int areaNum ) {
return ( file->areas[areaNum].flags & AREA_FLOOR ) != 0;
}
/*
================
idAASReach::AreaIsClusterPortal
================
*/
ID_INLINE bool idAASReach::AreaIsClusterPortal( int areaNum ) {
return ( file->areas[areaNum].contents & AREACONTENTS_CLUSTERPORTAL ) != 0;
}
/*
================
idAASReach::AddReachabilityToArea
================
*/
void idAASReach::AddReachabilityToArea( idReachability *reach, int areaNum ) {
aasArea_t *area;
area = &file->areas[areaNum];
reach->next = area->reach;
area->reach = reach;
numReachabilities++;
}
/*
================
idAASReach::Reachability_Fly
================
*/
void idAASReach::Reachability_Fly( int areaNum ) {
int i, faceNum, otherAreaNum;
aasArea_t *area;
aasFace_t *face;
idReachability_Fly *reach;
area = &file->areas[areaNum];
for ( i = 0; i < area->numFaces; i++ ) {
faceNum = file->faceIndex[area->firstFace + i];
face = &file->faces[abs(faceNum)];
otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)];
if ( otherAreaNum == 0 ) {
continue;
}
if ( ReachabilityExists( areaNum, otherAreaNum ) ) {
continue;
}
// create reachability going through this face
reach = new idReachability_Fly();
reach->travelType = TFL_FLY;
reach->toAreaNum = otherAreaNum;
reach->fromAreaNum = areaNum;
reach->edgeNum = 0;
reach->travelTime = 1;
reach->start = file->FaceCenter( abs(faceNum) );
if ( faceNum < 0 ) {
reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_FLYEND;
} else {
reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_FLYEND;
}
AddReachabilityToArea( reach, areaNum );
}
}
/*
================
idAASReach::Reachability_Swim
================
*/
void idAASReach::Reachability_Swim( int areaNum ) {
int i, faceNum, otherAreaNum;
aasArea_t *area;
aasFace_t *face;
idReachability_Swim *reach;
if ( !CanSwimInArea( areaNum ) ) {
return;
}
area = &file->areas[areaNum];
for ( i = 0; i < area->numFaces; i++ ) {
faceNum = file->faceIndex[area->firstFace + i];
face = &file->faces[abs(faceNum)];
otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)];
if ( otherAreaNum == 0 ) {
continue;
}
if ( !CanSwimInArea( otherAreaNum ) ) {
continue;
}
if ( ReachabilityExists( areaNum, otherAreaNum ) ) {
continue;
}
// create reachability going through this face
reach = new idReachability_Swim();
reach->travelType = TFL_SWIM;
reach->toAreaNum = otherAreaNum;
reach->fromAreaNum = areaNum;
reach->edgeNum = 0;
reach->travelTime = 1;
reach->start = file->FaceCenter( abs(faceNum) );
if ( faceNum < 0 ) {
reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_SWIMEND;
} else {
reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_SWIMEND;
}
AddReachabilityToArea( reach, areaNum );
}
}
/*
================
idAASReach::Reachability_EqualFloorHeight
================
*/
void idAASReach::Reachability_EqualFloorHeight( int areaNum ) {
int i, k, l, m, n, faceNum, face1Num, face2Num, otherAreaNum, edge1Num, edge2Num;
aasArea_t *area, *otherArea;
aasFace_t *face, *face1, *face2;
idReachability_Walk *reach;
if ( !AreaHasFloor( areaNum ) ) {
return;
}
area = &file->areas[areaNum];
for ( i = 0; i < area->numFaces; i++ ) {
faceNum = file->faceIndex[area->firstFace + i];
face = &file->faces[abs(faceNum)];
otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)];
if ( !AreaHasFloor( otherAreaNum ) ) {
continue;
}
otherArea = &file->areas[otherAreaNum];
for ( k = 0; k < area->numFaces; k++ ) {
face1Num = file->faceIndex[area->firstFace + k];
face1 = &file->faces[abs(face1Num)];
if ( !( face1->flags & FACE_FLOOR ) ) {
continue;
}
for ( l = 0; l < otherArea->numFaces; l++ ) {
face2Num = file->faceIndex[otherArea->firstFace + l];
face2 = &file->faces[abs(face2Num)];
if ( !( face2->flags & FACE_FLOOR ) ) {
continue;
}
for ( m = 0; m < face1->numEdges; m++ ) {
edge1Num = abs(file->edgeIndex[face1->firstEdge + m]);
for ( n = 0; n < face2->numEdges; n++ ) {
edge2Num = abs(file->edgeIndex[face2->firstEdge + n]);
if ( edge1Num == edge2Num ) {
break;
}
}
if ( n < face2->numEdges ) {
break;
}
}
if ( m < face1->numEdges ) {
break;
}
}
if ( l < otherArea->numFaces ) {
break;
}
}
if ( k < area->numFaces ) {
// create reachability
reach = new idReachability_Walk();
reach->travelType = TFL_WALK;
reach->toAreaNum = otherAreaNum;
reach->fromAreaNum = areaNum;
reach->edgeNum = abs( edge1Num );
reach->travelTime = 1;
reach->start = file->EdgeCenter( edge1Num );
if ( faceNum < 0 ) {
reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_WALKEND;
}
else {
reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_WALKEND;
}
AddReachabilityToArea( reach, areaNum );
}
}
}
/*
================
idAASReach::Reachability_Step_Barrier_WaterJump_WalkOffLedge
================
*/
bool idAASReach::Reachability_Step_Barrier_WaterJump_WalkOffLedge( int area1num, int area2num ) {
int i, j, k, l, edge1Num, edge2Num, areas[10];
int floor_bestArea1FloorEdgeNum, floor_bestArea2FloorEdgeNum, floor_foundReach;
int water_bestArea1FloorEdgeNum, water_bestArea2FloorEdgeNum, water_foundReach;
int side1, faceSide1, floorFace1Num;
float dist, dist1, dist2, diff, invGravityDot, orthogonalDot;
float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y;
float length, floor_bestLength, water_bestLength, floor_bestDist, water_bestDist;
idVec3 v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2;
idVec3 normal, orthogonal, edgeVec, start, end;
idVec3 floor_bestStart, floor_bestEnd, floor_bestNormal;
idVec3 water_bestStart, water_bestEnd, water_bestNormal;
idVec3 testPoint;
idPlane *plane;
aasArea_t *area1, *area2;
aasFace_t *floorFace1, *floorFace2, *floor_bestFace1, *water_bestFace1;
aasEdge_t *edge1, *edge2;
idReachability_Walk *walkReach;
idReachability_BarrierJump *barrierJumpReach;
idReachability_WaterJump *waterJumpReach;
idReachability_WalkOffLedge *walkOffLedgeReach;
aasTrace_t trace;
// must be able to walk or swim in the first area
if ( !AreaHasFloor( area1num ) && !CanSwimInArea( area1num ) ) {
return false;
}
if ( !AreaHasFloor( area2num ) && !CanSwimInArea( area2num ) ) {
return false;
}
area1 = &file->areas[area1num];
area2 = &file->areas[area2num];
// if the areas are not near anough in the x-y direction
for ( i = 0; i < 2; i++ ) {
if ( area1->bounds[0][i] > area2->bounds[1][i] + 2.0f ) {
return false;
}
if ( area1->bounds[1][i] < area2->bounds[0][i] - 2.0f ) {
return false;
}
}
floor_foundReach = false;
floor_bestDist = 99999;
floor_bestLength = 0;
floor_bestArea2FloorEdgeNum = 0;
water_foundReach = false;
water_bestDist = 99999;
water_bestLength = 0;
water_bestArea2FloorEdgeNum = 0;
for ( i = 0; i < area1->numFaces; i++ ) {
floorFace1Num = file->faceIndex[area1->firstFace + i];
faceSide1 = floorFace1Num < 0;
floorFace1 = &file->faces[abs(floorFace1Num)];
// if this isn't a floor face
if ( !(floorFace1->flags & FACE_FLOOR) ) {
// if we can swim in the first area
if ( CanSwimInArea( area1num ) ) {
// face plane must be more or less horizontal
plane = &file->planeList[ floorFace1->planeNum ^ (!faceSide1) ];
if ( plane->Normal() * file->settings.invGravityDir < file->settings.minFloorCos ) {
continue;
}
}
else {
// if we can't swim in the area it must be a ground face
continue;
}
}
for ( k = 0; k < floorFace1->numEdges; k++ ) {
edge1Num = file->edgeIndex[floorFace1->firstEdge + k];
side1 = (edge1Num < 0);
// NOTE: for water faces we must take the side area 1 is on into
// account because the face is shared and doesn't have to be oriented correctly
if ( !(floorFace1->flags & FACE_FLOOR) ) {
side1 = (side1 == faceSide1);
}
edge1Num = abs(edge1Num);
edge1 = &file->edges[edge1Num];
// vertices of the edge
v1 = file->vertices[edge1->vertexNum[!side1]];
v2 = file->vertices[edge1->vertexNum[side1]];
// get a vertical plane through the edge
// NOTE: normal is pointing into area 2 because the face edges are stored counter clockwise
edgeVec = v2 - v1;
normal = edgeVec.Cross( file->settings.invGravityDir );
normal.Normalize();
dist = normal * v1;
// check the faces from the second area
for ( j = 0; j < area2->numFaces; j++ ) {
floorFace2 = &file->faces[abs(file->faceIndex[area2->firstFace + j])];
// must be a ground face
if ( !(floorFace2->flags & FACE_FLOOR) ) {
continue;
}
// check the edges of this ground face
for ( l = 0; l < floorFace2->numEdges; l++ ) {
edge2Num = abs(file->edgeIndex[floorFace2->firstEdge + l]);
edge2 = &file->edges[edge2Num];
// vertices of the edge
v3 = file->vertices[edge2->vertexNum[0]];
v4 = file->vertices[edge2->vertexNum[1]];
// check the distance between the two points and the vertical plane through the edge of area1
diff = normal * v3 - dist;
if ( diff < -0.2f || diff > 0.2f ) {
continue;
}
diff = normal * v4 - dist;
if ( diff < -0.2f || diff > 0.2f ) {
continue;
}
// project the two ground edges into the step side plane
// and calculate the shortest distance between the two
// edges if they overlap in the direction orthogonal to
// the gravity direction
orthogonal = file->settings.invGravityDir.Cross( normal );
invGravityDot = file->settings.invGravityDir * file->settings.invGravityDir;
orthogonalDot = orthogonal * orthogonal;
// projection into the step plane
// NOTE: since gravity is vertical this is just the z coordinate
y1 = v1[2];//(v1 * file->settings.invGravity) / invGravityDot;
y2 = v2[2];//(v2 * file->settings.invGravity) / invGravityDot;
y3 = v3[2];//(v3 * file->settings.invGravity) / invGravityDot;
y4 = v4[2];//(v4 * file->settings.invGravity) / invGravityDot;
x1 = (v1 * orthogonal) / orthogonalDot;
x2 = (v2 * orthogonal) / orthogonalDot;
x3 = (v3 * orthogonal) / orthogonalDot;
x4 = (v4 * orthogonal) / orthogonalDot;
if ( x1 > x2 ) {
tmp = x1; x1 = x2; x2 = tmp;
tmp = y1; y1 = y2; y2 = tmp;
tmpv = v1; v1 = v2; v2 = tmpv;
}
if ( x3 > x4 ) {
tmp = x3; x3 = x4; x4 = tmp;
tmp = y3; y3 = y4; y4 = tmp;
tmpv = v3; v3 = v4; v4 = tmpv;
}
// if the two projected edge lines have no overlap
if ( x2 <= x3 || x4 <= x1 ) {
continue;
}
// if the two lines fully overlap
if ( (x1 - 0.5f < x3 && x4 < x2 + 0.5f) && (x3 - 0.5f < x1 && x2 < x4 + 0.5f) ) {
dist1 = y3 - y1;
dist2 = y4 - y2;
p1area1 = v1;
p2area1 = v2;
p1area2 = v3;
p2area2 = v4;
}
else {
// if the points are equal
if ( x1 > x3 - 0.1f && x1 < x3 + 0.1f ) {
dist1 = y3 - y1;
p1area1 = v1;
p1area2 = v3;
}
else if ( x1 < x3 ) {
y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1);
dist1 = y3 - y;
p1area1 = v3;
p1area1[2] = y;
p1area2 = v3;
}
else {
y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3);
dist1 = y - y1;
p1area1 = v1;
p1area2 = v1;
p1area2[2] = y;
}
// if the points are equal
if ( x2 > x4 - 0.1f && x2 < x4 + 0.1f ) {
dist2 = y4 - y2;
p2area1 = v2;
p2area2 = v4;
}
else if ( x2 < x4 ) {
y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3);
dist2 = y - y2;
p2area1 = v2;
p2area2 = v2;
p2area2[2] = y;
}
else {
y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1);
dist2 = y4 - y;
p2area1 = v4;
p2area1[2] = y;
p2area2 = v4;
}
}
// if both distances are pretty much equal then we take the middle of the points
if ( dist1 > dist2 - 1.0f && dist1 < dist2 + 1.0f ) {
dist = dist1;
start = ( p1area1 + p2area1 ) * 0.5f;
end = ( p1area2 + p2area2 ) * 0.5f;
}
else if (dist1 < dist2) {
dist = dist1;
start = p1area1;
end = p1area2;
}
else {
dist = dist2;
start = p2area1;
end = p2area2;
}
// get the length of the overlapping part of the edges of the two areas
length = (p2area2 - p1area2).Length();
if ( floorFace1->flags & FACE_FLOOR ) {
// if the vertical distance is smaller
if ( dist < floor_bestDist ||
// or the vertical distance is pretty much the same
// but the overlapping part of the edges is longer
(dist < floor_bestDist + 1.0f && length > floor_bestLength) ) {
floor_bestDist = dist;
floor_bestLength = length;
floor_foundReach = true;
floor_bestArea1FloorEdgeNum = edge1Num;
floor_bestArea2FloorEdgeNum = edge2Num;
floor_bestFace1 = floorFace1;
floor_bestStart = start;
floor_bestNormal = normal;
floor_bestEnd = end;
}
}
else {
// if the vertical distance is smaller
if ( dist < water_bestDist ||
//or the vertical distance is pretty much the same
//but the overlapping part of the edges is longer
(dist < water_bestDist + 1.0f && length > water_bestLength) ) {
water_bestDist = dist;
water_bestLength = length;
water_foundReach = true;
water_bestArea1FloorEdgeNum = edge1Num;
water_bestArea2FloorEdgeNum = edge2Num;
water_bestFace1 = floorFace1;
water_bestStart = start; // best start point in area1
water_bestNormal = normal; // normal is pointing into area2
water_bestEnd = end; // best point towards area2
}
}
}
}
}
}
//
// NOTE: swim reachabilities should already be filtered out
//
// Steps
//
// ---------
// | step height -> TFL_WALK
// --------|
//
// ---------
// ~~~~~~~~| step height and low water -> TFL_WALK
// --------|
//
// ~~~~~~~~~~~~~~~~~~
// ---------
// | step height and low water up to the step -> TFL_WALK
// --------|
//
// check for a step reachability
if ( floor_foundReach ) {
// if area2 is higher but lower than the maximum step height
// NOTE: floor_bestDist >= 0 also catches equal floor reachabilities
if ( floor_bestDist >= 0 && floor_bestDist < file->settings.maxStepHeight ) {
// create walk reachability from area1 to area2
walkReach = new idReachability_Walk();
walkReach->travelType = TFL_WALK;
walkReach->toAreaNum = area2num;
walkReach->fromAreaNum = area1num;
walkReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal;
walkReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal;
walkReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
walkReach->travelTime = 0;
if ( area2->flags & AREA_CROUCH ) {
walkReach->travelTime += file->settings.tt_startCrouching;
}
AddReachabilityToArea( walkReach, area1num );
return true;
}
}
//
// Water Jumps
//
// ---------
// |
// ~~~~~~~~|
// |
// | higher than step height and water up to waterjump height -> TFL_WATERJUMP
// --------|
//
// ~~~~~~~~~~~~~~~~~~
// ---------
// |
// |
// |
// | higher than step height and low water up to the step -> TFL_WATERJUMP
// --------|
//
// check for a waterjump reachability
if ( water_foundReach ) {
// get a test point a little bit towards area1
testPoint = water_bestEnd - INSIDEUNITS * water_bestNormal;
// go down the maximum waterjump height
testPoint[2] -= file->settings.maxWaterJumpHeight;
// if there IS water the sv_maxwaterjump height below the bestend point
if ( area1->flags & AREA_LIQUID ) {
// don't create rediculous water jump reachabilities from areas very far below the water surface
if ( water_bestDist < file->settings.maxWaterJumpHeight + 24 ) {
// water jumping from or towards a crouch only areas is not possible
if ( !(area1->flags & AREA_CROUCH) && !(area2->flags & AREA_CROUCH) ) {
// create water jump reachability from area1 to area2
waterJumpReach = new idReachability_WaterJump();
waterJumpReach->travelType = TFL_WATERJUMP;
waterJumpReach->toAreaNum = area2num;
waterJumpReach->fromAreaNum = area1num;
waterJumpReach->start = water_bestStart;
waterJumpReach->end = water_bestEnd + INSIDEUNITS_WATERJUMP * water_bestNormal;
waterJumpReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
waterJumpReach->travelTime = file->settings.tt_waterJump;
AddReachabilityToArea( waterJumpReach, area1num );
return true;
}
}
}
}
//
// Barrier Jumps
//
// ---------
// |
// |
// |
// | higher than max step height lower than max barrier height -> TFL_BARRIERJUMP
// --------|
//
// ---------
// |
// |
// |
// ~~~~~~~~| higher than max step height lower than max barrier height
// --------| and a thin layer of water in the area to jump from -> TFL_BARRIERJUMP
//
// check for a barrier jump reachability
if ( floor_foundReach ) {
//if area2 is higher but lower than the maximum barrier jump height
if ( floor_bestDist > 0 && floor_bestDist < file->settings.maxBarrierHeight ) {
//if no water in area1 or a very thin layer of water on the ground
if ( !water_foundReach || (floor_bestDist - water_bestDist < 16) ) {
// cannot perform a barrier jump towards or from a crouch area
if ( !(area1->flags & AREA_CROUCH) && !(area2->flags & AREA_CROUCH) ) {
// create barrier jump reachability from area1 to area2
barrierJumpReach = new idReachability_BarrierJump();
barrierJumpReach->travelType = TFL_BARRIERJUMP;
barrierJumpReach->toAreaNum = area2num;
barrierJumpReach->fromAreaNum = area1num;
barrierJumpReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal;
barrierJumpReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal;
barrierJumpReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
barrierJumpReach->travelTime = file->settings.tt_barrierJump;
AddReachabilityToArea( barrierJumpReach, area1num );
return true;
}
}
}
}
//
// Walk and Walk Off Ledge
//
// --------|
// | can walk or step back -> TFL_WALK
// ---------
//
// --------|
// |
// |
// |
// | cannot walk/step back -> TFL_WALKOFFLEDGE
// ---------
//
// --------|
// |
// |~~~~~~~~
// |
// | cannot step back but can waterjump back -> TFL_WALKOFFLEDGE
// --------- FIXME: create TFL_WALK reach??
//
// check for a walk or walk off ledge reachability
if ( floor_foundReach ) {
if ( floor_bestDist < 0 ) {
if ( floor_bestDist > -file->settings.maxStepHeight ) {
// create walk reachability from area1 to area2
walkReach = new idReachability_Walk();
walkReach->travelType = TFL_WALK;
walkReach->toAreaNum = area2num;
walkReach->fromAreaNum = area1num;
walkReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal;
walkReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal;
walkReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
walkReach->travelTime = 1;
AddReachabilityToArea( walkReach, area1num );
return true;
}
// if no maximum fall height set or less than the max
if ( !file->settings.maxFallHeight || idMath::Fabs(floor_bestDist) < file->settings.maxFallHeight ) {
// trace a bounding box vertically to check for solids
floor_bestEnd += INSIDEUNITS * floor_bestNormal;
start = floor_bestEnd;
start[2] = floor_bestStart[2];
end = floor_bestEnd;
end[2] += 4;
trace.areas = areas;
trace.maxAreas = sizeof(areas) / sizeof(int);
file->Trace( trace, start, end );
// if the trace didn't start in solid and nothing was hit
if ( trace.lastAreaNum && trace.fraction >= 1.0f ) {
// the trace end point must be in the goal area
if ( trace.lastAreaNum == area2num ) {
// don't create reachability if going through a cluster portal
for (i = 0; i < trace.numAreas; i++) {
if ( AreaIsClusterPortal( trace.areas[i] ) ) {
break;
}
}
if ( i >= trace.numAreas ) {
// create a walk off ledge reachability from area1 to area2
walkOffLedgeReach = new idReachability_WalkOffLedge();
walkOffLedgeReach->travelType = TFL_WALKOFFLEDGE;
walkOffLedgeReach->toAreaNum = area2num;
walkOffLedgeReach->fromAreaNum = area1num;
walkOffLedgeReach->start = floor_bestStart;
walkOffLedgeReach->end = floor_bestEnd;
walkOffLedgeReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
walkOffLedgeReach->travelTime = file->settings.tt_startWalkOffLedge + idMath::Fabs(floor_bestDist) * 50 / file->settings.gravityValue;
AddReachabilityToArea( walkOffLedgeReach, area1num );
return true;
}
}
}
}
}
}
return false;
}
/*
================
idAASReach::Reachability_WalkOffLedge
================
*/
void idAASReach::Reachability_WalkOffLedge( int areaNum ) {
int i, j, faceNum, edgeNum, side, reachAreaNum, p, areas[10];
aasArea_t *area;
aasFace_t *face;
aasEdge_t *edge;
idPlane *plane;
idVec3 v1, v2, mid, dir, testEnd;
idReachability_WalkOffLedge *reach;
aasTrace_t trace;
if ( !AreaHasFloor( areaNum ) || CanSwimInArea( areaNum ) ) {
return;
}
area = &file->areas[areaNum];
for ( i = 0; i < area->numFaces; i++ ) {
faceNum = file->faceIndex[area->firstFace + i];
face = &file->faces[abs(faceNum)];
// face must be a floor face
if ( !(face->flags & FACE_FLOOR) ) {
continue;
}
for ( j = 0; j < face->numEdges; j++ ) {
edgeNum = file->edgeIndex[face->firstEdge + j];
edge = &file->edges[abs(edgeNum)];
//if ( !(edge->flags & EDGE_LEDGE) ) {
// continue;
//}
side = edgeNum < 0;
v1 = file->vertices[edge->vertexNum[side]];
v2 = file->vertices[edge->vertexNum[!side]];
plane = &file->planeList[face->planeNum ^ INTSIGNBITSET(faceNum) ];
// get the direction into the other area
dir = plane->Normal().Cross( v2 - v1 );
dir.Normalize();
mid = ( v1 + v2 ) * 0.5f;
testEnd = mid + INSIDEUNITS_WALKEND * dir;
testEnd[2] -= file->settings.maxFallHeight + 1.0f;
trace.areas = areas;
trace.maxAreas = sizeof(areas) / sizeof(int);
file->Trace( trace, mid, testEnd );
reachAreaNum = trace.lastAreaNum;
if ( !reachAreaNum || reachAreaNum == areaNum ) {
continue;
}
if ( idMath::Fabs( mid[2] - trace.endpos[2] ) > file->settings.maxFallHeight ) {
continue;
}
if ( !AreaHasFloor( reachAreaNum ) && !CanSwimInArea( reachAreaNum ) ) {
continue;
}
if ( ReachabilityExists( areaNum, reachAreaNum) ) {
continue;
}
// if not going through a cluster portal
for ( p = 0; p < trace.numAreas; p++ ) {
if ( AreaIsClusterPortal( trace.areas[p] ) ) {
break;
}
}
if ( p < trace.numAreas ) {
continue;
}
reach = new idReachability_WalkOffLedge();
reach->travelType = TFL_WALKOFFLEDGE;
reach->toAreaNum = reachAreaNum;
reach->fromAreaNum = areaNum;
reach->start = mid;
reach->end = trace.endpos;
reach->edgeNum = abs( edgeNum );
reach->travelTime = file->settings.tt_startWalkOffLedge + idMath::Fabs(mid[2] - trace.endpos[2]) * 50 / file->settings.gravityValue;
AddReachabilityToArea( reach, areaNum );
}
}
}
/*
================
idAASReach::FlagReachableAreas
================
*/
void idAASReach::FlagReachableAreas( idAASFileLocal *file ) {
int i, numReachableAreas;
numReachableAreas = 0;
for ( i = 1; i < file->areas.Num(); i++ ) {
if ( ( file->areas[i].flags & ( AREA_FLOOR | AREA_LADDER ) ) ||
( file->areas[i].contents & AREACONTENTS_WATER ) ) {
file->areas[i].flags |= AREA_REACHABLE_WALK;
}
if ( file->GetSettings().allowFlyReachabilities ) {
file->areas[i].flags |= AREA_REACHABLE_FLY;
}
numReachableAreas++;
}
common->Printf( "%6d reachable areas\n", numReachableAreas );
}
/*
================
idAASReach::Build
================
*/
bool idAASReach::Build( const idMapFile *mapFile, idAASFileLocal *file ) {
int i, j, lastPercent, percent;
this->mapFile = mapFile;
this->file = file;
numReachabilities = 0;
common->Printf( "[Reachability]\n" );
// delete all existing reachabilities
file->DeleteReachabilities();
FlagReachableAreas( file );
for ( i = 1; i < file->areas.Num(); i++ ) {
if ( !( file->areas[i].flags & AREA_REACHABLE_WALK ) ) {
continue;
}
if ( file->GetSettings().allowSwimReachabilities ) {
Reachability_Swim( i );
}
Reachability_EqualFloorHeight( i );
}
lastPercent = -1;
for ( i = 1; i < file->areas.Num(); i++ ) {
if ( !( file->areas[i].flags & AREA_REACHABLE_WALK ) ) {
continue;
}
for ( j = 0; j < file->areas.Num(); j++ ) {
if ( i == j ) {
continue;
}
if ( !( file->areas[j].flags & AREA_REACHABLE_WALK ) ) {
continue;
}
if ( ReachabilityExists( i, j ) ) {
continue;
}
if ( Reachability_Step_Barrier_WaterJump_WalkOffLedge( i, j ) ) {
continue;
}
}
//Reachability_WalkOffLedge( i );
percent = 100 * i / file->areas.Num();
if ( percent > lastPercent ) {
common->Printf( "\r%6d%%", percent );
lastPercent = percent;
}
}
if ( file->GetSettings().allowFlyReachabilities ) {
for ( i = 1; i < file->areas.Num(); i++ ) {
Reachability_Fly( i );
}
}
file->LinkReversedReachability();
common->Printf( "\r%6d reachabilities\n", numReachabilities );
return true;
}

View File

@@ -0,0 +1,67 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __AASREACH_H__
#define __AASREACH_H__
/*
===============================================================================
Reachabilities
===============================================================================
*/
class idAASReach {
public:
bool Build( const idMapFile *mapFile, idAASFileLocal *file );
private:
const idMapFile * mapFile;
idAASFileLocal * file;
int numReachabilities;
bool allowSwimReachabilities;
bool allowFlyReachabilities;
private: // reachability
void FlagReachableAreas( idAASFileLocal *file );
bool ReachabilityExists( int fromAreaNum, int toAreaNum );
bool CanSwimInArea( int areaNum );
bool AreaHasFloor( int areaNum );
bool AreaIsClusterPortal( int areaNum );
void AddReachabilityToArea( idReachability *reach, int areaNum );
void Reachability_Fly( int areaNum );
void Reachability_Swim( int areaNum );
void Reachability_EqualFloorHeight( int areaNum );
bool Reachability_Step_Barrier_WaterJump_WalkOffLedge( int fromAreaNum, int toAreaNum );
void Reachability_WalkOffLedge( int areaNum );
};
#endif /* !__AASREACH_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,231 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __BRUSH_H__
#define __BRUSH_H__
/*
===============================================================================
Brushes
===============================================================================
*/
#define BRUSH_PLANESIDE_FRONT 1
#define BRUSH_PLANESIDE_BACK 2
#define BRUSH_PLANESIDE_BOTH ( BRUSH_PLANESIDE_FRONT | BRUSH_PLANESIDE_BACK )
#define BRUSH_PLANESIDE_FACING 4
class idBrush;
class idBrushList;
void DisplayRealTimeString( char *string, ... ) id_attribute((format(printf,1,2)));
//===============================================================
//
// idBrushSide
//
//===============================================================
#define SFL_SPLIT 0x0001
#define SFL_BEVEL 0x0002
#define SFL_USED_SPLITTER 0x0004
#define SFL_TESTED_SPLITTER 0x0008
class idBrushSide {
friend class idBrush;
public:
idBrushSide( void );
idBrushSide( const idPlane &plane, int planeNum );
~idBrushSide( void );
int GetFlags( void ) const { return flags; }
void SetFlag( int flag ) { flags |= flag; }
void RemoveFlag( int flag ) { flags &= ~flag; }
const idPlane & GetPlane( void ) const { return plane; }
void SetPlaneNum( int num ) { planeNum = num; }
int GetPlaneNum( void ) { return planeNum; }
const idWinding * GetWinding( void ) const { return winding; }
idBrushSide * Copy( void ) const;
int Split( const idPlane &splitPlane, idBrushSide **front, idBrushSide **back ) const;
private:
int flags;
int planeNum;
idPlane plane;
idWinding * winding;
};
//===============================================================
//
// idBrush
//
//===============================================================
#define BFL_NO_VALID_SPLITTERS 0x0001
class idBrush {
friend class idBrushList;
public:
idBrush( void );
~idBrush( void );
int GetFlags( void ) const { return flags; }
void SetFlag( int flag ) { flags |= flag; }
void RemoveFlag( int flag ) { flags &= ~flag; }
void SetEntityNum( int num ) { entityNum = num; }
void SetPrimitiveNum( int num ) { primitiveNum = num; }
void SetContents( int contents ) { this->contents = contents; }
int GetContents( void ) const { return contents; }
const idBounds & GetBounds( void ) const { return bounds; }
float GetVolume( void ) const;
int GetNumSides( void ) const { return sides.Num(); }
idBrushSide * GetSide( int i ) const { return sides[i]; }
void SetPlaneSide( int s ) { planeSide = s; }
void SavePlaneSide( void ) { savedPlaneSide = planeSide; }
int GetSavedPlaneSide( void ) const { return savedPlaneSide; }
bool FromSides( idList<idBrushSide *> &sideList );
bool FromWinding( const idWinding &w, const idPlane &windingPlane );
bool FromBounds( const idBounds &bounds );
void Transform( const idVec3 &origin, const idMat3 &axis );
idBrush * Copy( void ) const;
bool TryMerge( const idBrush *brush, const idPlaneSet &planeList );
// returns true if the brushes did intersect
bool Subtract( const idBrush *b, idBrushList &list ) const;
// split the brush into a front and back brush
int Split( const idPlane &plane, int planeNum, idBrush **front, idBrush **back ) const;
// expand the brush for an axial bounding box
void ExpandForAxialBox( const idBounds &bounds );
// next brush in list
idBrush * Next( void ) const { return next; }
private:
mutable idBrush * next; // next brush in list
int entityNum; // entity number in editor
int primitiveNum; // primitive number in editor
int flags; // brush flags
bool windingsValid; // set when side windings are valid
int contents; // contents of brush
int planeSide; // side of a plane this brush is on
int savedPlaneSide; // saved plane side
idBounds bounds; // brush bounds
idList<idBrushSide *> sides; // list with sides
private:
bool CreateWindings( void );
void BoundBrush( const idBrush *original = NULL );
void AddBevelsForAxialBox( void );
bool RemoveSidesWithoutWinding( void );
};
//===============================================================
//
// idBrushList
//
//===============================================================
class idBrushList {
public:
idBrushList( void );
~idBrushList( void );
int Num( void ) const { return numBrushes; }
int NumSides( void ) const { return numBrushSides; }
idBrush * Head( void ) const { return head; }
idBrush * Tail( void ) const { return tail; }
void Clear( void ) { head = tail = NULL; numBrushes = 0; }
bool IsEmpty( void ) const { return (numBrushes == 0); }
idBounds GetBounds( void ) const;
// add brush to the tail of the list
void AddToTail( idBrush *brush );
// add list to the tail of the list
void AddToTail( idBrushList &list );
// add brush to the front of the list
void AddToFront( idBrush *brush );
// add list to the front of the list
void AddToFront( idBrushList &list );
// remove the brush from the list
void Remove( idBrush *brush );
// remove the brush from the list and delete the brush
void Delete( idBrush *brush);
// returns a copy of the brush list
idBrushList * Copy( void ) const;
// delete all brushes in the list
void Free( void );
// split the brushes in the list into two lists
void Split( const idPlane &plane, int planeNum, idBrushList &frontList, idBrushList &backList, bool useBrushSavedPlaneSide = false );
// chop away all brush overlap
void Chop( bool (*ChopAllowed)( idBrush *b1, idBrush *b2 ) );
// merge brushes
void Merge( bool (*MergeAllowed)( idBrush *b1, idBrush *b2 ) );
// set the given flag on all brush sides facing the plane
void SetFlagOnFacingBrushSides( const idPlane &plane, int flag );
// get a list with planes for all brushes in the list
void CreatePlaneList( idPlaneSet &planeList ) const;
// write a brush map with the brushes in the list
void WriteBrushMap( const idStr &fileName, const idStr &ext ) const;
private:
idBrush * head;
idBrush * tail;
int numBrushes;
int numBrushSides;
};
//===============================================================
//
// idBrushMap
//
//===============================================================
class idBrushMap {
public:
idBrushMap( const idStr &fileName, const idStr &ext );
~idBrushMap( void );
void SetTexture( const idStr &textureName ) { texture = textureName; }
void WriteBrush( const idBrush *brush );
void WriteBrushList( const idBrushList &brushList );
private:
idFile * fp;
idStr texture;
int brushCount;
};
#endif /* !__BRUSH_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,233 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __BRUSHBSP_H__
#define __BRUSHBSP_H__
/*
===============================================================================
BrushBSP
===============================================================================
*/
class idBrushBSP;
class idBrushBSPNode;
class idBrushBSPPortal;
//===============================================================
//
// idBrushBSPPortal
//
//===============================================================
class idBrushBSPPortal {
friend class idBrushBSP;
friend class idBrushBSPNode;
public:
idBrushBSPPortal( void );
~idBrushBSPPortal( void );
void AddToNodes( idBrushBSPNode *front, idBrushBSPNode *back );
void RemoveFromNode( idBrushBSPNode *l );
void Flip( void );
int Split( const idPlane &splitPlane, idBrushBSPPortal **front, idBrushBSPPortal **back );
idWinding * GetWinding( void ) const { return winding; }
const idPlane & GetPlane( void ) const { return plane; }
void SetFaceNum( int num ) { faceNum = num; }
int GetFaceNum( void ) const { return faceNum; }
int GetFlags( void ) const { return flags; }
void SetFlag( int flag ) { flags |= flag; }
void RemoveFlag( int flag ) { flags &= ~flag; }
idBrushBSPPortal * Next( int side ) const { return next[side]; }
idBrushBSPNode * GetNode( int side ) const { return nodes[side]; }
private:
idPlane plane; // portal plane
int planeNum; // number of plane this portal is on
idWinding * winding; // portal winding
idBrushBSPNode * nodes[2]; // nodes this portal seperates
idBrushBSPPortal * next[2]; // next portal in list for both nodes
int flags; // portal flags
int faceNum; // number of the face created for this portal
};
//===============================================================
//
// idBrushBSPNode
//
//===============================================================
#define NODE_VISITED BIT(30)
#define NODE_DONE BIT(31)
class idBrushBSPNode {
friend class idBrushBSP;
friend class idBrushBSPPortal;
public:
idBrushBSPNode( void );
~idBrushBSPNode( void );
void SetContentsFromBrushes( void );
idBounds GetPortalBounds( void );
idBrushBSPNode * GetChild( int index ) const { return children[index]; }
idBrushBSPNode * GetParent( void ) const { return parent; }
void SetContents( int contents ) { this->contents = contents; }
int GetContents( void ) const { return contents; }
const idPlane & GetPlane( void ) const { return plane; }
idBrushBSPPortal * GetPortals( void ) const { return portals; }
void SetAreaNum( int num ) { areaNum = num; }
int GetAreaNum( void ) const { return areaNum; }
int GetFlags( void ) const { return flags; }
void SetFlag( int flag ) { flags |= flag; }
void RemoveFlag( int flag ) { flags &= ~flag; }
bool TestLeafNode( void );
// remove the flag from nodes found by flooding through portals to nodes with the flag set
void RemoveFlagFlood( int flag );
// recurse down the tree and remove the flag from all visited nodes
void RemoveFlagRecurse( int flag );
// first recurse down the tree and flood from there
void RemoveFlagRecurseFlood( int flag );
// returns side of the plane the node is on
int PlaneSide( const idPlane &plane, float epsilon = ON_EPSILON ) const;
// split the leaf node with a plane
bool Split( const idPlane &splitPlane, int splitPlaneNum );
private:
idPlane plane; // split plane if this is not a leaf node
idBrush * volume; // node volume
int contents; // node contents
idBrushList brushList; // list with brushes for this node
idBrushBSPNode * parent; // parent of this node
idBrushBSPNode * children[2]; // both are NULL if this is a leaf node
idBrushBSPPortal * portals; // portals of this node
int flags; // node flags
int areaNum; // number of the area created for this node
int occupied; // true when portal is occupied
};
//===============================================================
//
// idBrushBSP
//
//===============================================================
class idBrushBSP {
public:
idBrushBSP( void );
~idBrushBSP( void );
// build a bsp tree from a set of brushes
void Build( idBrushList brushList, int skipContents,
bool (*ChopAllowed)( idBrush *b1, idBrush *b2 ),
bool (*MergeAllowed)( idBrush *b1, idBrush *b2 ) );
// remove splits in subspaces with the given contents
void PruneTree( int contents );
// portalize the bsp tree
void Portalize( void );
// remove subspaces outside the map not reachable by entities
bool RemoveOutside( const idMapFile *mapFile, int contents, const idStrList &classNames );
// write file with a trace going through a leak
void LeakFile( const idStr &fileName );
// try to merge portals
void MergePortals( int skipContents );
// try to merge the two leaf nodes at either side of the portal
bool TryMergeLeafNodes( idBrushBSPPortal *portal, int side );
void PruneMergedTree_r( idBrushBSPNode *node );
// melt portal windings
void MeltPortals( int skipContents );
// write a map file with a brush for every leaf node that has the given contents
void WriteBrushMap( const idStr &fileName, const idStr &ext, int contents );
// bounds for the whole tree
const idBounds & GetTreeBounds( void ) const { return treeBounds; }
// root node of the tree
idBrushBSPNode * GetRootNode( void ) const { return root; }
private:
idBrushBSPNode * root;
idBrushBSPNode * outside;
idBounds treeBounds;
idPlaneSet portalPlanes;
int numGridCells;
int numSplits;
int numGridCellSplits;
int numPrunedSplits;
int numPortals;
int solidLeafNodes;
int outsideLeafNodes;
int insideLeafNodes;
int numMergedPortals;
int numInsertedPoints;
idVec3 leakOrigin;
int brushMapContents;
idBrushMap * brushMap;
bool (*BrushChopAllowed)( idBrush *b1, idBrush *b2 );
bool (*BrushMergeAllowed)( idBrush *b1, idBrush *b2 );
private:
void RemoveMultipleLeafNodeReferences_r( idBrushBSPNode *node );
void Free_r( idBrushBSPNode *node );
void IncreaseNumSplits( void );
bool IsValidSplitter( const idBrushSide *side );
int BrushSplitterStats( const idBrush *brush, int planeNum, const idPlaneSet &planeList, bool *testedPlanes, struct splitterStats_s &stats );
int FindSplitter( idBrushBSPNode *node, const idPlaneSet &planeList, bool *testedPlanes, struct splitterStats_s &bestStats );
void SetSplitterUsed( idBrushBSPNode *node, int planeNum );
idBrushBSPNode * BuildBrushBSP_r( idBrushBSPNode *node, const idPlaneSet &planeList, bool *testedPlanes, int skipContents );
idBrushBSPNode * ProcessGridCell( idBrushBSPNode *node, int skipContents );
void BuildGrid_r( idList<idBrushBSPNode *> &gridCells, idBrushBSPNode *node );
void PruneTree_r( idBrushBSPNode *node, int contents );
void MakeOutsidePortals( void );
idWinding * BaseWindingForNode( idBrushBSPNode *node );
void MakeNodePortal( idBrushBSPNode *node );
void SplitNodePortals( idBrushBSPNode *node );
void MakeTreePortals_r( idBrushBSPNode *node );
void FloodThroughPortals_r( idBrushBSPNode *node, int contents, int depth );
bool FloodFromOrigin( const idVec3 &origin, int contents );
bool FloodFromEntities( const idMapFile *mapFile, int contents, const idStrList &classNames );
void RemoveOutside_r( idBrushBSPNode *node, int contents );
void SetPortalPlanes_r( idBrushBSPNode *node, idPlaneSet &planeList );
void SetPortalPlanes( void );
void MergePortals_r( idBrushBSPNode *node, int skipContents );
void MergeLeafNodePortals( idBrushBSPNode *node, int skipContents );
void UpdateTreeAfterMerge_r( idBrushBSPNode *node, const idBounds &bounds, idBrushBSPNode *oldNode, idBrushBSPNode *newNode );
void RemoveLeafNodeColinearPoints( idBrushBSPNode *node );
void RemoveColinearPoints_r( idBrushBSPNode *node, int skipContents );
void MeltFlood_r( idBrushBSPNode *node, int skipContents, idBounds &bounds, idVectorSet<idVec3,3> &vertexList );
void MeltLeafNodePortals( idBrushBSPNode *node, int skipContents, idVectorSet<idVec3,3> &vertexList );
void MeltPortals_r( idBrushBSPNode *node, int skipContents, idVectorSet<idVec3,3> &vertexList );
};
#endif /* !__BRUSHBSP_H__ */