dhewm3/neo/tools/compilers/aas/AASBuild_file.cpp
dhewg 736ec20d4d Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2011-12-19 23:21:47 +01:00

501 lines
12 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "sys/platform.h"
#include "tools/compilers/aas/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;
area.bounds.Zero();
area.center.Zero();
area.travelFlags = 0;
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;
}