123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- /*
- ===========================================================================
- 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;
- }
|