mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-02-28 22:51:25 +00:00
421 lines
9 KiB
C++
421 lines
9 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 "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "AASBuild_local.h"
|
|
|
|
|
|
/*
|
|
============
|
|
idAASBuild::SetPortalFlags_r
|
|
============
|
|
*/
|
|
void idAASBuild::SetPortalFlags_r( idBrushBSPNode* node )
|
|
{
|
|
int s;
|
|
idBrushBSPPortal* p;
|
|
idVec3 normal;
|
|
|
|
if( !node )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( node->GetContents() & AREACONTENTS_SOLID )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( !node->GetChild( 0 ) && !node->GetChild( 1 ) )
|
|
{
|
|
for( p = node->GetPortals(); p; p = p->Next( s ) )
|
|
{
|
|
s = ( p->GetNode( 1 ) == node );
|
|
|
|
// if solid at the other side of the portal
|
|
if( p->GetNode( !s )->GetContents() & AREACONTENTS_SOLID )
|
|
{
|
|
if( s )
|
|
{
|
|
normal = -p->GetPlane().Normal();
|
|
}
|
|
else
|
|
{
|
|
normal = p->GetPlane().Normal();
|
|
}
|
|
if( normal * aasSettings->invGravityDir > aasSettings->minFloorCos )
|
|
{
|
|
p->SetFlag( FACE_FLOOR );
|
|
}
|
|
else
|
|
{
|
|
p->SetFlag( FACE_SOLID );
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
SetPortalFlags_r( node->GetChild( 0 ) );
|
|
SetPortalFlags_r( node->GetChild( 1 ) );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASBuild::PortalIsGap
|
|
============
|
|
*/
|
|
bool idAASBuild::PortalIsGap( idBrushBSPPortal* portal, int side )
|
|
{
|
|
idVec3 normal;
|
|
|
|
// if solid at the other side of the portal
|
|
if( portal->GetNode( !side )->GetContents() & AREACONTENTS_SOLID )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( side )
|
|
{
|
|
normal = -( portal->GetPlane().Normal() );
|
|
}
|
|
else
|
|
{
|
|
normal = portal->GetPlane().Normal();
|
|
}
|
|
if( normal * aasSettings->invGravityDir > aasSettings->minFloorCos )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASBuild::GravSubdivLeafNode
|
|
============
|
|
*/
|
|
#define FACE_CHECKED BIT(31)
|
|
#define GRAVSUBDIV_EPSILON 0.1f
|
|
|
|
void idAASBuild::GravSubdivLeafNode( idBrushBSPNode* node )
|
|
{
|
|
int s1, s2, i, j, k, side1;
|
|
int numSplits, numSplitters;
|
|
idBrushBSPPortal* p1, *p2;
|
|
idWinding* w1, *w2;
|
|
idVec3 normal;
|
|
idPlane plane;
|
|
idPlaneSet planeList;
|
|
float d, min, max;
|
|
int* splitterOrder;
|
|
int* bestNumSplits;
|
|
int floor, gap, numFloorChecked;
|
|
|
|
// if this leaf node is already classified it cannot have a combination of floor and gap portals
|
|
if( node->GetFlags() & ( AREA_FLOOR | AREA_GAP ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
floor = gap = 0;
|
|
|
|
// check if the area has a floor
|
|
for( p1 = node->GetPortals(); p1; p1 = p1->Next( s1 ) )
|
|
{
|
|
s1 = ( p1->GetNode( 1 ) == node );
|
|
|
|
if( p1->GetFlags() & FACE_FLOOR )
|
|
{
|
|
floor++;
|
|
}
|
|
}
|
|
|
|
// find seperating planes between gap and floor portals
|
|
for( p1 = node->GetPortals(); p1; p1 = p1->Next( s1 ) )
|
|
{
|
|
s1 = ( p1->GetNode( 1 ) == node );
|
|
|
|
// if the portal is a gap seen from this side
|
|
if( PortalIsGap( p1, s1 ) )
|
|
{
|
|
gap++;
|
|
// if the area doesn't have a floor
|
|
if( !floor )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
numFloorChecked = 0;
|
|
|
|
w1 = p1->GetWinding();
|
|
|
|
// test all edges of the gap
|
|
for( i = 0; i < w1->GetNumPoints(); i++ )
|
|
{
|
|
|
|
// create a plane through the edge of the gap parallel to the direction of gravity
|
|
normal = ( *w1 )[( i + 1 ) % w1->GetNumPoints()].ToVec3() - ( *w1 )[i].ToVec3();
|
|
normal = normal.Cross( aasSettings->invGravityDir );
|
|
if( normal.Normalize() < 0.2f )
|
|
{
|
|
continue;
|
|
}
|
|
plane.SetNormal( normal );
|
|
plane.FitThroughPoint( ( *w1 )[i].ToVec3() );
|
|
|
|
// get the side of the plane the gap is on
|
|
side1 = w1->PlaneSide( plane, GRAVSUBDIV_EPSILON );
|
|
if( side1 == SIDE_ON )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// test if the plane through the edge of the gap seperates the gap from a floor portal
|
|
for( p2 = node->GetPortals(); p2; p2 = p2->Next( s2 ) )
|
|
{
|
|
s2 = ( p2->GetNode( 1 ) == node );
|
|
|
|
if( !( p2->GetFlags() & FACE_FLOOR ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( p2->GetFlags() & FACE_CHECKED )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
w2 = p2->GetWinding();
|
|
|
|
min = 2.0f * GRAVSUBDIV_EPSILON;
|
|
max = GRAVSUBDIV_EPSILON;
|
|
if( side1 == SIDE_FRONT )
|
|
{
|
|
for( j = 0; j < w2->GetNumPoints(); j++ )
|
|
{
|
|
d = plane.Distance( ( *w2 )[j].ToVec3() );
|
|
if( d >= GRAVSUBDIV_EPSILON )
|
|
{
|
|
break; // point at the same side of the plane as the gap
|
|
}
|
|
d = idMath::Fabs( d );
|
|
if( d < min )
|
|
{
|
|
min = d;
|
|
}
|
|
if( d > max )
|
|
{
|
|
max = d;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( j = 0; j < w2->GetNumPoints(); j++ )
|
|
{
|
|
d = plane.Distance( ( *w2 )[j].ToVec3() );
|
|
if( d <= -GRAVSUBDIV_EPSILON )
|
|
{
|
|
break; // point at the same side of the plane as the gap
|
|
}
|
|
d = idMath::Fabs( d );
|
|
if( d < min )
|
|
{
|
|
min = d;
|
|
}
|
|
if( d > max )
|
|
{
|
|
max = d;
|
|
}
|
|
}
|
|
}
|
|
|
|
// a point of the floor portal was found to be at the same side of the plane as the gap
|
|
if( j < w2->GetNumPoints() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// if the floor portal touches the plane
|
|
if( min < GRAVSUBDIV_EPSILON && max > GRAVSUBDIV_EPSILON )
|
|
{
|
|
planeList.FindPlane( plane, 0.00001f, 0.1f );
|
|
}
|
|
|
|
p2->SetFlag( FACE_CHECKED );
|
|
numFloorChecked++;
|
|
|
|
}
|
|
if( numFloorChecked == floor )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
for( p2 = node->GetPortals(); p2; p2 = p2->Next( s2 ) )
|
|
{
|
|
s2 = ( p2->GetNode( 1 ) == node );
|
|
p2->RemoveFlag( FACE_CHECKED );
|
|
}
|
|
}
|
|
|
|
// if the leaf node does not have both floor and gap portals
|
|
if( !( gap && floor ) )
|
|
{
|
|
if( floor )
|
|
{
|
|
node->SetFlag( AREA_FLOOR );
|
|
}
|
|
else if( gap )
|
|
{
|
|
node->SetFlag( AREA_GAP );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// if no valid seperators found
|
|
if( planeList.Num() == 0 )
|
|
{
|
|
// NOTE: this should never happend, if it does the leaf node has degenerate portals
|
|
return;
|
|
}
|
|
|
|
splitterOrder = ( int* ) _alloca( planeList.Num() * sizeof( int ) );
|
|
bestNumSplits = ( int* ) _alloca( planeList.Num() * sizeof( int ) );
|
|
numSplitters = 0;
|
|
|
|
// test all possible seperators and sort them from best to worst
|
|
for( i = 0; i < planeList.Num(); i += 2 )
|
|
{
|
|
numSplits = 0;
|
|
|
|
for( p1 = node->GetPortals(); p1; p1 = p1->Next( s1 ) )
|
|
{
|
|
s1 = ( p1->GetNode( 1 ) == node );
|
|
if( p1->GetWinding()->PlaneSide( planeList[i], 0.1f ) == SIDE_CROSS )
|
|
{
|
|
numSplits++;
|
|
}
|
|
}
|
|
|
|
for( j = 0; j < numSplitters; j++ )
|
|
{
|
|
if( numSplits < bestNumSplits[j] )
|
|
{
|
|
for( k = numSplitters; k > j; k-- )
|
|
{
|
|
bestNumSplits[k] = bestNumSplits[k - 1];
|
|
splitterOrder[k] = splitterOrder[k - 1];
|
|
}
|
|
bestNumSplits[j] = numSplits;
|
|
splitterOrder[j] = i;
|
|
numSplitters++;
|
|
break;
|
|
}
|
|
}
|
|
if( j >= numSplitters )
|
|
{
|
|
bestNumSplits[j] = numSplits;
|
|
splitterOrder[j] = i;
|
|
numSplitters++;
|
|
}
|
|
}
|
|
|
|
// try all seperators in order from best to worst
|
|
for( i = 0; i < numSplitters; i++ )
|
|
{
|
|
if( node->Split( planeList[splitterOrder[i]], -1 ) )
|
|
{
|
|
// we found a seperator that works
|
|
break;
|
|
}
|
|
}
|
|
if( i >= numSplitters )
|
|
{
|
|
return;
|
|
}
|
|
|
|
DisplayRealTimeString( "\r%6d", ++numGravitationalSubdivisions );
|
|
|
|
// test children for further splits
|
|
GravSubdivLeafNode( node->GetChild( 0 ) );
|
|
GravSubdivLeafNode( node->GetChild( 1 ) );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASBuild::GravSubdiv_r
|
|
============
|
|
*/
|
|
void idAASBuild::GravSubdiv_r( idBrushBSPNode* node )
|
|
{
|
|
|
|
if( !node )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( node->GetContents() & AREACONTENTS_SOLID )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( !node->GetChild( 0 ) && !node->GetChild( 1 ) )
|
|
{
|
|
GravSubdivLeafNode( node );
|
|
return;
|
|
}
|
|
|
|
GravSubdiv_r( node->GetChild( 0 ) );
|
|
GravSubdiv_r( node->GetChild( 1 ) );
|
|
}
|
|
|
|
/*
|
|
============
|
|
idAASBuild::GravitationalSubdivision
|
|
============
|
|
*/
|
|
void idAASBuild::GravitationalSubdivision( idBrushBSP& bsp )
|
|
{
|
|
numGravitationalSubdivisions = 0;
|
|
|
|
common->Printf( "[Gravitational Subdivision]\n" );
|
|
|
|
SetPortalFlags_r( bsp.GetRootNode() );
|
|
GravSubdiv_r( bsp.GetRootNode() );
|
|
|
|
common->Printf( "\r%6d subdivisions\n", numGravitationalSubdivisions );
|
|
}
|