mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-12-11 13:30:59 +00:00
736ec20d4d
Don't include the lazy precompiled.h everywhere, only what's required for the compilation unit. platform.h needs to be included instead to provide all essential defines and types. All includes use the relative path to the neo or the game specific root. Move all idlib related includes from idlib/Lib.h to precompiled.h. precompiled.h still exists for the MFC stuff in tools/. Add some missing header guards.
501 lines
12 KiB
C++
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;
|
|
}
|