2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 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.
24 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.
26 ===========================================================================
29 #include "../../../idlib/precompiled.h"
32 #include "AASBuild_local.h"
34 #define VERTEX_HASH_BOXSIZE (1<<6) // must be power of 2
35 #define VERTEX_HASH_SIZE (VERTEX_HASH_BOXSIZE*VERTEX_HASH_BOXSIZE)
36 #define EDGE_HASH_SIZE (1<<14)
38 #define INTEGRAL_EPSILON 0.01f
39 #define VERTEX_EPSILON 0.1f
41 #define AAS_PLANE_NORMAL_EPSILON 0.00001f
42 #define AAS_PLANE_DIST_EPSILON 0.01f
45 idHashIndex *aas_vertexHash;
46 idHashIndex *aas_edgeHash;
47 idBounds aas_vertexBounds;
55 void idAASBuild::SetupHash( void ) {
56 aas_vertexHash = new idHashIndex( VERTEX_HASH_SIZE, 1024 );
57 aas_edgeHash = new idHashIndex( EDGE_HASH_SIZE, 1024 );
62 idAASBuild::ShutdownHash
65 void idAASBuild::ShutdownHash( void ) {
66 delete aas_vertexHash;
75 void idAASBuild::ClearHash( const idBounds &bounds ) {
79 aas_vertexHash->Clear();
80 aas_edgeHash->Clear();
81 aas_vertexBounds = bounds;
83 max = bounds[1].x - bounds[0].x;
84 f = bounds[1].y - bounds[0].y;
88 aas_vertexShift = (float) max / VERTEX_HASH_BOXSIZE;
89 for ( i = 0; (1<<i) < aas_vertexShift; i++ ) {
104 ID_INLINE int idAASBuild::HashVec( const idVec3 &vec ) {
107 x = (((int) (vec[0] - aas_vertexBounds[0].x + 0.5)) + 2) >> 2;
108 y = (((int) (vec[1] - aas_vertexBounds[0].y + 0.5)) + 2) >> 2;
109 return (x + y * VERTEX_HASH_BOXSIZE) & (VERTEX_HASH_SIZE-1);
114 idAASBuild::GetVertex
117 bool idAASBuild::GetVertex( const idVec3 &v, int *vertexNum ) {
119 aasVertex_t vert, *p;
121 for (i = 0; i < 3; i++) {
122 if ( idMath::Fabs(v[i] - idMath::Rint(v[i])) < INTEGRAL_EPSILON ) {
123 vert[i] = idMath::Rint(v[i]);
130 hashKey = idAASBuild::HashVec( vert );
132 for ( vn = aas_vertexHash->First( hashKey ); vn >= 0; vn = aas_vertexHash->Next( vn ) ) {
133 p = &file->vertices[vn];
134 // first compare z-axis because hash is based on x-y plane
135 if (idMath::Fabs( vert.z - p->z ) < VERTEX_EPSILON &&
136 idMath::Fabs( vert.x - p->x ) < VERTEX_EPSILON &&
137 idMath::Fabs( vert.y - p->y ) < VERTEX_EPSILON )
144 *vertexNum = file->vertices.Num();
145 aas_vertexHash->Add( hashKey, file->vertices.Num() );
146 file->vertices.Append( vert );
156 bool idAASBuild::GetEdge( const idVec3 &v1, const idVec3 &v2, int *edgeNum, int v1num ) {
157 int v2num, hashKey, e;
166 found = GetVertex( v1, &v1num );
168 found &= GetVertex( v2, &v2num );
169 // if both vertexes are the same or snapped onto each other
170 if ( v1num == v2num ) {
174 hashKey = aas_edgeHash->GenerateKey( v1num, v2num );
175 // if both vertexes where already stored
177 for ( e = aas_edgeHash->First( hashKey ); e >= 0; e = aas_edgeHash->Next( e ) ) {
179 vertexNum = file->edges[e].vertexNum;
180 if ( vertexNum[0] == v2num ) {
181 if ( vertexNum[1] == v1num ) {
182 // negative for a reversed edge
187 else if ( vertexNum[0] == v1num ) {
188 if ( vertexNum[1] == v2num ) {
194 // if edge found in hash
200 *edgeNum = file->edges.Num();
201 aas_edgeHash->Add( hashKey, file->edges.Num() );
203 edge.vertexNum[0] = v1num;
204 edge.vertexNum[1] = v2num;
206 file->edges.Append( edge );
213 idAASBuild::GetFaceForPortal
216 bool idAASBuild::GetFaceForPortal( idBrushBSPPortal *portal, int side, int *faceNum ) {
218 int numFaceEdges, faceEdges[MAX_POINTS_ON_WINDING];
222 if ( portal->GetFaceNum() > 0 ) {
224 *faceNum = -portal->GetFaceNum();
227 *faceNum = portal->GetFaceNum();
232 w = portal->GetWinding();
233 // turn the winding into a sequence of edges
235 v1num = -1; // first vertex unknown
236 for ( i = 0; i < w->GetNumPoints(); i++ ) {
238 GetEdge( (*w)[i].ToVec3(), (*w)[(i+1)%w->GetNumPoints()].ToVec3(), &faceEdges[numFaceEdges], v1num );
240 if ( faceEdges[numFaceEdges] ) {
241 // last vertex of this edge is the first vertex of the next edge
242 v1num = file->edges[ abs(faceEdges[numFaceEdges]) ].vertexNum[ INTSIGNBITNOTSET(faceEdges[numFaceEdges]) ];
244 // this edge is valid so keep it
249 // should have at least 3 edges
250 if ( numFaceEdges < 3 ) {
254 // the polygon is invalid if some edge is found twice
255 for ( i = 0; i < numFaceEdges; i++ ) {
256 for ( j = i+1; j < numFaceEdges; j++ ) {
257 if ( faceEdges[i] == faceEdges[j] || faceEdges[i] == -faceEdges[j] ) {
263 portal->SetFaceNum( file->faces.Num() );
265 face.planeNum = file->planeList.FindPlane( portal->GetPlane(), AAS_PLANE_NORMAL_EPSILON, AAS_PLANE_DIST_EPSILON );
266 face.flags = portal->GetFlags();
267 face.areas[0] = face.areas[1] = 0;
268 face.firstEdge = file->edgeIndex.Num();
269 face.numEdges = numFaceEdges;
270 for ( i = 0; i < numFaceEdges; i++ ) {
271 file->edgeIndex.Append( faceEdges[i] );
274 *faceNum = -file->faces.Num();
277 *faceNum = file->faces.Num();
279 file->faces.Append( face );
286 idAASBuild::GetAreaForLeafNode
289 bool idAASBuild::GetAreaForLeafNode( idBrushBSPNode *node, int *areaNum ) {
294 if ( node->GetAreaNum() ) {
295 *areaNum = -node->GetAreaNum();
299 area.flags = node->GetFlags();
300 area.cluster = area.clusterAreaNum = 0;
301 area.contents = node->GetContents();
302 area.firstFace = file->faceIndex.Num();
305 area.rev_reach = NULL;
307 for ( p = node->GetPortals(); p; p = p->Next(s) ) {
308 s = (p->GetNode(1) == node);
310 if ( !GetFaceForPortal( p, s, &faceNum ) ) {
314 file->faceIndex.Append( faceNum );
318 file->faces[abs(faceNum)].areas[0] = file->areas.Num();
321 file->faces[abs(faceNum)].areas[1] = file->areas.Num();
325 if ( !area.numFaces ) {
330 *areaNum = -file->areas.Num();
331 node->SetAreaNum( file->areas.Num() );
332 file->areas.Append( area );
334 DisplayRealTimeString( "\r%6d", file->areas.Num() );
341 idAASBuild::StoreTree_r
344 int idAASBuild::StoreTree_r( idBrushBSPNode *node ) {
345 int areaNum, nodeNum, child0, child1;
352 if ( node->GetContents() & AREACONTENTS_SOLID ) {
356 if ( !node->GetChild(0) && !node->GetChild(1) ) {
357 if ( GetAreaForLeafNode( node, &areaNum ) ) {
363 aasNode.planeNum = file->planeList.FindPlane( node->GetPlane(), AAS_PLANE_NORMAL_EPSILON, AAS_PLANE_DIST_EPSILON );
364 aasNode.children[0] = aasNode.children[1] = 0;
365 nodeNum = file->nodes.Num();
366 file->nodes.Append( aasNode );
368 // !@#$%^ cause of some bug we cannot set the children directly with the StoreTree_r return value
369 child0 = StoreTree_r( node->GetChild(0) );
370 file->nodes[nodeNum].children[0] = child0;
371 child1 = StoreTree_r( node->GetChild(1) );
372 file->nodes[nodeNum].children[1] = child1;
374 if ( !child0 && !child1 ) {
375 file->nodes.SetNum( file->nodes.Num()-1 );
384 idAASBuild::GetSizeEstimate_r
387 typedef struct sizeEstimate_s {
394 void idAASBuild::GetSizeEstimate_r( idBrushBSPNode *parent, idBrushBSPNode *node, struct sizeEstimate_s &size ) {
402 if ( node->GetContents() & AREACONTENTS_SOLID ) {
406 if ( !node->GetChild(0) && !node->GetChild(1) ) {
407 // multiple branches of the bsp tree might point to the same leaf node
408 if ( node->GetParent() == parent ) {
410 for ( p = node->GetPortals(); p; p = p->Next(s) ) {
411 s = (p->GetNode(1) == node);
412 size.numFaceIndexes++;
413 size.numEdgeIndexes += p->GetWinding()->GetNumPoints();
421 GetSizeEstimate_r( node, node->GetChild(0), size );
422 GetSizeEstimate_r( node, node->GetChild(1), size );
427 idAASBuild::SetSizeEstimate
430 void idAASBuild::SetSizeEstimate( const idBrushBSP &bsp, idAASFileLocal *file ) {
433 size.numEdgeIndexes = 1;
434 size.numFaceIndexes = 1;
438 GetSizeEstimate_r( NULL, bsp.GetRootNode(), size );
440 file->planeList.Resize( size.numNodes / 2, 1024 );
441 file->vertices.Resize( size.numEdgeIndexes / 3, 1024 );
442 file->edges.Resize( size.numEdgeIndexes / 2, 1024 );
443 file->edgeIndex.Resize( size.numEdgeIndexes, 4096 );
444 file->faces.Resize( size.numFaceIndexes, 1024 );
445 file->faceIndex.Resize( size.numFaceIndexes, 4096 );
446 file->areas.Resize( size.numAreas, 1024 );
447 file->nodes.Resize( size.numNodes, 1024 );
452 idAASBuild::StoreFile
455 bool idAASBuild::StoreFile( const idBrushBSP &bsp ) {
461 common->Printf( "[Store AAS]\n" );
464 ClearHash( bsp.GetTreeBounds() );
466 file = new idAASFileLocal();
470 SetSizeEstimate( bsp, file );
472 // the first edge is a dummy
473 memset( &edge, 0, sizeof( edge ) );
474 file->edges.Append( edge );
476 // the first face is a dummy
477 memset( &face, 0, sizeof( face ) );
478 file->faces.Append( face );
480 // the first area is a dummy
481 memset( &area, 0, sizeof( area ) );
482 file->areas.Append( area );
484 // the first node is a dummy
485 memset( &node, 0, sizeof( node ) );
486 file->nodes.Append( node );
489 StoreTree_r( bsp.GetRootNode() );
491 // calculate area bounds and a reachable point in the area
496 common->Printf( "\r%6d areas\n", file->areas.Num() );