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"
37 idAASBuild::SetPortalFlags_r
40 void idAASBuild::SetPortalFlags_r( idBrushBSPNode *node ) {
49 if ( node->GetContents() & AREACONTENTS_SOLID ) {
53 if ( !node->GetChild(0) && !node->GetChild(1) ) {
54 for ( p = node->GetPortals(); p; p = p->Next(s) ) {
55 s = (p->GetNode(1) == node);
57 // if solid at the other side of the portal
58 if ( p->GetNode(!s)->GetContents() & AREACONTENTS_SOLID ) {
60 normal = -p->GetPlane().Normal();
63 normal = p->GetPlane().Normal();
65 if ( normal * aasSettings->invGravityDir > aasSettings->minFloorCos ) {
66 p->SetFlag( FACE_FLOOR );
69 p->SetFlag( FACE_SOLID );
76 SetPortalFlags_r( node->GetChild(0) );
77 SetPortalFlags_r( node->GetChild(1) );
82 idAASBuild::PortalIsGap
85 bool idAASBuild::PortalIsGap( idBrushBSPPortal *portal, int side ) {
88 // if solid at the other side of the portal
89 if ( portal->GetNode(!side)->GetContents() & AREACONTENTS_SOLID ) {
94 normal = -(portal->GetPlane().Normal());
97 normal = portal->GetPlane().Normal();
99 if ( normal * aasSettings->invGravityDir > aasSettings->minFloorCos ) {
107 idAASBuild::GravSubdivLeafNode
110 #define FACE_CHECKED BIT(31)
111 #define GRAVSUBDIV_EPSILON 0.1f
113 void idAASBuild::GravSubdivLeafNode( idBrushBSPNode *node ) {
114 int s1, s2, i, j, k, side1;
115 int numSplits, numSplitters;
116 idBrushBSPPortal *p1, *p2;
120 idPlaneSet planeList;
124 int floor, gap, numFloorChecked;
126 // if this leaf node is already classified it cannot have a combination of floor and gap portals
127 if ( node->GetFlags() & (AREA_FLOOR|AREA_GAP) ) {
133 // check if the area has a floor
134 for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
135 s1 = (p1->GetNode(1) == node);
137 if ( p1->GetFlags() & FACE_FLOOR ) {
142 // find seperating planes between gap and floor portals
143 for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
144 s1 = (p1->GetNode(1) == node);
146 // if the portal is a gap seen from this side
147 if ( PortalIsGap( p1, s1 ) ) {
149 // if the area doesn't have a floor
160 w1 = p1->GetWinding();
162 // test all edges of the gap
163 for ( i = 0; i < w1->GetNumPoints(); i++ ) {
165 // create a plane through the edge of the gap parallel to the direction of gravity
166 normal = (*w1)[(i+1)%w1->GetNumPoints()].ToVec3() - (*w1)[i].ToVec3();
167 normal = normal.Cross( aasSettings->invGravityDir );
168 if ( normal.Normalize() < 0.2f ) {
171 plane.SetNormal( normal );
172 plane.FitThroughPoint( (*w1)[i].ToVec3() );
174 // get the side of the plane the gap is on
175 side1 = w1->PlaneSide( plane, GRAVSUBDIV_EPSILON );
176 if ( side1 == SIDE_ON ) {
180 // test if the plane through the edge of the gap seperates the gap from a floor portal
181 for ( p2 = node->GetPortals(); p2; p2 = p2->Next(s2) ) {
182 s2 = (p2->GetNode(1) == node);
184 if ( !( p2->GetFlags() & FACE_FLOOR ) ) {
188 if ( p2->GetFlags() & FACE_CHECKED ) {
192 w2 = p2->GetWinding();
194 min = 2.0f * GRAVSUBDIV_EPSILON;
195 max = GRAVSUBDIV_EPSILON;
196 if ( side1 == SIDE_FRONT ) {
197 for ( j = 0; j < w2->GetNumPoints(); j++ ) {
198 d = plane.Distance( (*w2)[j].ToVec3() );
199 if ( d >= GRAVSUBDIV_EPSILON ) {
200 break; // point at the same side of the plane as the gap
202 d = idMath::Fabs( d );
212 for ( j = 0; j < w2->GetNumPoints(); j++ ) {
213 d = plane.Distance( (*w2)[j].ToVec3() );
214 if ( d <= -GRAVSUBDIV_EPSILON ) {
215 break; // point at the same side of the plane as the gap
217 d = idMath::Fabs( d );
227 // a point of the floor portal was found to be at the same side of the plane as the gap
228 if ( j < w2->GetNumPoints() ) {
232 // if the floor portal touches the plane
233 if ( min < GRAVSUBDIV_EPSILON && max > GRAVSUBDIV_EPSILON ) {
234 planeList.FindPlane( plane, 0.00001f, 0.1f );
237 p2->SetFlag( FACE_CHECKED );
241 if ( numFloorChecked == floor ) {
246 for ( p2 = node->GetPortals(); p2; p2 = p2->Next(s2) ) {
247 s2 = (p2->GetNode(1) == node);
248 p2->RemoveFlag( FACE_CHECKED );
252 // if the leaf node does not have both floor and gap portals
253 if ( !( gap && floor) ) {
255 node->SetFlag( AREA_FLOOR );
258 node->SetFlag( AREA_GAP );
263 // if no valid seperators found
264 if ( planeList.Num() == 0 ) {
265 // NOTE: this should never happend, if it does the leaf node has degenerate portals
269 splitterOrder = (int *) _alloca( planeList.Num() * sizeof( int ) );
270 bestNumSplits = (int *) _alloca( planeList.Num() * sizeof( int ) );
273 // test all possible seperators and sort them from best to worst
274 for ( i = 0; i < planeList.Num(); i += 2 ) {
277 for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
278 s1 = (p1->GetNode(1) == node);
279 if ( p1->GetWinding()->PlaneSide( planeList[i], 0.1f ) == SIDE_CROSS ) {
284 for ( j = 0; j < numSplitters; j++ ) {
285 if ( numSplits < bestNumSplits[j] ) {
286 for ( k = numSplitters; k > j; k-- ) {
287 bestNumSplits[k] = bestNumSplits[k-1];
288 splitterOrder[k] = splitterOrder[k-1];
290 bestNumSplits[j] = numSplits;
291 splitterOrder[j] = i;
296 if ( j >= numSplitters ) {
297 bestNumSplits[j] = numSplits;
298 splitterOrder[j] = i;
303 // try all seperators in order from best to worst
304 for ( i = 0; i < numSplitters; i++ ) {
305 if ( node->Split( planeList[splitterOrder[i]], -1 ) ) {
306 // we found a seperator that works
310 if ( i >= numSplitters) {
314 DisplayRealTimeString( "\r%6d", ++numGravitationalSubdivisions );
316 // test children for further splits
317 GravSubdivLeafNode( node->GetChild(0) );
318 GravSubdivLeafNode( node->GetChild(1) );
323 idAASBuild::GravSubdiv_r
326 void idAASBuild::GravSubdiv_r( idBrushBSPNode *node ) {
332 if ( node->GetContents() & AREACONTENTS_SOLID ) {
336 if ( !node->GetChild(0) && !node->GetChild(1) ) {
337 GravSubdivLeafNode( node );
341 GravSubdiv_r( node->GetChild(0) );
342 GravSubdiv_r( node->GetChild(1) );
347 idAASBuild::GravitationalSubdivision
350 void idAASBuild::GravitationalSubdivision( idBrushBSP &bsp ) {
351 numGravitationalSubdivisions = 0;
353 common->Printf( "[Gravitational Subdivision]\n" );
355 SetPortalFlags_r( bsp.GetRootNode() );
356 GravSubdiv_r( bsp.GetRootNode() );
358 common->Printf( "\r%6d subdivisions\n", numGravitationalSubdivisions );