448 lines
11 KiB
C++
448 lines
11 KiB
C++
/* -------------------------------------------------------------------------------
|
|
|
|
Copyright (C) 1999-2007 id Software, Inc. and contributors.
|
|
For a list of contributors, see the accompanying CONTRIBUTORS file.
|
|
|
|
This file is part of GtkRadiant.
|
|
|
|
GtkRadiant 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 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
----------------------------------------------------------------------------------
|
|
|
|
This code has been altered significantly from its original form, to support
|
|
several games based on the Quake III Arena engine, in the form of "Q3Map2."
|
|
|
|
------------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/* dependencies */
|
|
#include "q3map2.h"
|
|
|
|
|
|
|
|
int c_faceLeafs;
|
|
|
|
|
|
|
|
/*
|
|
SelectSplitPlaneNum()
|
|
finds the best split plane for this node
|
|
*/
|
|
|
|
static void SelectSplitPlaneNum( node_t *node, const facelist_t& list, int *splitPlaneNum, int *compileFlags ){
|
|
//int frontC,backC,splitsC,facingC;
|
|
|
|
|
|
/* ydnar: set some defaults */
|
|
*splitPlaneNum = -1; /* leaf */
|
|
*compileFlags = 0;
|
|
|
|
/* ydnar 2002-06-24: changed this to split on z-axis as well */
|
|
/* ydnar 2002-09-21: changed blocksize to be a vector, so mappers can specify a 3 element value */
|
|
|
|
/* if it is crossing a block boundary, force a split */
|
|
for ( int i = 0; i < 3; i++ )
|
|
{
|
|
if ( blockSize[ i ] <= 0 ) {
|
|
continue;
|
|
}
|
|
const float dist = blockSize[ i ] * ( floor( node->minmax.mins[ i ] / blockSize[ i ] ) + 1 );
|
|
if ( node->minmax.maxs[ i ] > dist ) {
|
|
*splitPlaneNum = FindFloatPlane( g_vector3_axes[i], dist, 0, NULL );
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* pick one of the face planes */
|
|
int bestValue = -99999;
|
|
const face_t *bestSplit = nullptr;
|
|
|
|
|
|
// div0: this check causes detail/structural mixes
|
|
//for( face_t& split : list )
|
|
// split.checked = false;
|
|
|
|
for ( const face_t& split : list )
|
|
{
|
|
//if ( split->checked )
|
|
// continue;
|
|
|
|
const plane_t& plane = mapplanes[ split.planenum ];
|
|
int splits = 0;
|
|
int facing = 0;
|
|
int front = 0;
|
|
int back = 0;
|
|
for ( const face_t& check : list ) {
|
|
if ( check.planenum == split.planenum ) {
|
|
facing++;
|
|
//check->checked = true; // won't need to test this plane again
|
|
continue;
|
|
}
|
|
const EPlaneSide side = WindingOnPlaneSide( check.w, plane.plane );
|
|
if ( side == eSideCross ) {
|
|
splits++;
|
|
}
|
|
else if ( side == eSideFront ) {
|
|
front++;
|
|
}
|
|
else if ( side == eSideBack ) {
|
|
back++;
|
|
}
|
|
}
|
|
|
|
int value;
|
|
if ( bspAlternateSplitWeights ) {
|
|
// from 27
|
|
|
|
//Bigger is better
|
|
const float sizeBias = WindingArea( split.w );
|
|
|
|
//Base score = 20000 perfectly balanced
|
|
value = 20000 - ( abs( front - back ) );
|
|
value -= plane.counter; // If we've already used this plane sometime in the past try not to use it again
|
|
value -= facing ; // if we're going to have alot of other surfs use this plane, we want to get it in quickly.
|
|
value -= splits * 5; //more splits = bad
|
|
value += sizeBias * 10; //We want a huge score bias based on plane size
|
|
}
|
|
else
|
|
{
|
|
value = 5 * facing - 5 * splits; // - abs(front-back);
|
|
if ( plane.type < ePlaneNonAxial ) {
|
|
value += 5; // axial is better
|
|
}
|
|
}
|
|
|
|
value += split.priority; // prioritize hints higher
|
|
|
|
if ( value > bestValue ) {
|
|
bestValue = value;
|
|
bestSplit = &split;
|
|
//frontC=front;
|
|
//backC=back;
|
|
//splitsC=splits;
|
|
//facingC=facing;
|
|
}
|
|
}
|
|
|
|
/* nothing, we have a leaf */
|
|
if ( bestValue == -99999 ) {
|
|
return;
|
|
}
|
|
|
|
//Sys_FPrintf( SYS_VRB, "F: %d B:%d S:%d FA:%ds\n", frontC, backC, splitsC, facingC );
|
|
|
|
/* set best split data */
|
|
*splitPlaneNum = bestSplit->planenum;
|
|
*compileFlags = bestSplit->compileFlags;
|
|
|
|
#if 0
|
|
if ( bestSplit->compileFlags & C_DETAIL ) {
|
|
for ( const face_t& split : list )
|
|
if ( !( split.compileFlags & C_DETAIL ) ) {
|
|
Sys_FPrintf( SYS_ERR, "DON'T DO SUCH SPLITS (1)\n" );
|
|
}
|
|
}
|
|
if ( ( node->compileFlags & C_DETAIL ) && !( bestSplit->compileFlags & C_DETAIL ) ) {
|
|
Sys_FPrintf( SYS_ERR, "DON'T DO SUCH SPLITS (2)\n" );
|
|
}
|
|
#endif
|
|
|
|
if ( *splitPlaneNum > -1 ) {
|
|
mapplanes[ *splitPlaneNum ].counter++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
BuildFaceTree_r()
|
|
recursively builds the bsp, splitting on face planes
|
|
*/
|
|
|
|
void BuildFaceTree_r( node_t *node, facelist_t& list ){
|
|
facelist_t childLists[2];
|
|
int splitPlaneNum, compileFlags;
|
|
#if 0
|
|
bool isstruct = false;
|
|
#endif
|
|
|
|
|
|
/* select the best split plane */
|
|
SelectSplitPlaneNum( node, list, &splitPlaneNum, &compileFlags );
|
|
|
|
/* if we don't have any more faces, this is a node */
|
|
if ( splitPlaneNum == -1 ) {
|
|
node->planenum = PLANENUM_LEAF;
|
|
node->has_structural_children = false;
|
|
c_faceLeafs++;
|
|
return;
|
|
}
|
|
|
|
/* partition the list */
|
|
node->planenum = splitPlaneNum;
|
|
node->compileFlags = compileFlags;
|
|
node->has_structural_children = !( compileFlags & C_DETAIL ) && !node->opaque;
|
|
const plane_t& plane = mapplanes[ splitPlaneNum ];
|
|
|
|
while ( !list.empty() )
|
|
{
|
|
const face_t& split = list.front();
|
|
/* don't split by identical plane */
|
|
if ( split.planenum == node->planenum ) {
|
|
list.pop_front();
|
|
continue;
|
|
}
|
|
|
|
#if 0
|
|
if ( !( split.compileFlags & C_DETAIL ) ) {
|
|
isstruct = true;
|
|
}
|
|
#endif
|
|
|
|
/* determine which side the face falls on */
|
|
const EPlaneSide side = WindingOnPlaneSide( split.w, plane.plane );
|
|
|
|
/* switch on side */
|
|
if ( side == eSideCross ) {
|
|
winding_t *frontWinding, *backWinding;
|
|
ClipWindingEpsilonStrict( split.w, plane.plane, CLIP_EPSILON * 2,
|
|
frontWinding, backWinding ); /* strict; if no winding is left, we have a "virtually identical" plane and don't want to split by it */
|
|
if ( frontWinding ) {
|
|
face_t& newFace = childLists[0].emplace_front();
|
|
newFace.w.swap( *frontWinding );
|
|
FreeWinding( frontWinding );
|
|
newFace.planenum = split.planenum;
|
|
newFace.priority = split.priority;
|
|
newFace.compileFlags = split.compileFlags;
|
|
}
|
|
if ( backWinding ) {
|
|
face_t& newFace = childLists[1].emplace_front();
|
|
newFace.w.swap( *backWinding );
|
|
FreeWinding( backWinding );
|
|
newFace.planenum = split.planenum;
|
|
newFace.priority = split.priority;
|
|
newFace.compileFlags = split.compileFlags;
|
|
}
|
|
list.pop_front();
|
|
}
|
|
else if ( side == eSideFront ) {
|
|
childLists[0].splice_after( childLists[0].cbefore_begin(), list, list.cbefore_begin() );
|
|
}
|
|
else if ( side == eSideBack ) {
|
|
childLists[1].splice_after( childLists[1].cbefore_begin(), list, list.cbefore_begin() );
|
|
}
|
|
else{ // eSideOn
|
|
list.pop_front();
|
|
}
|
|
}
|
|
|
|
|
|
// recursively process children
|
|
for ( int i = 0; i < 2; i++ ) {
|
|
node->children[i] = AllocNode();
|
|
node->children[i]->parent = node;
|
|
node->children[i]->minmax = node->minmax;
|
|
}
|
|
|
|
for ( int i = 0; i < 3; i++ ) {
|
|
if ( plane.normal()[i] == 1 ) {
|
|
node->children[0]->minmax.mins[i] = plane.dist();
|
|
node->children[1]->minmax.maxs[i] = plane.dist();
|
|
break;
|
|
}
|
|
if ( plane.normal()[i] == -1 ) {
|
|
node->children[0]->minmax.maxs[i] = -plane.dist();
|
|
node->children[1]->minmax.mins[i] = -plane.dist();
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
if ( ( node->compileFlags & C_DETAIL ) && isstruct ) {
|
|
Sys_FPrintf( SYS_ERR, "I am detail, my child is structural, this is a wtf1\n", node->has_structural_children );
|
|
}
|
|
#endif
|
|
|
|
for ( int i = 0; i < 2; i++ ) {
|
|
BuildFaceTree_r( node->children[i], childLists[i] );
|
|
node->has_structural_children |= node->children[i]->has_structural_children;
|
|
}
|
|
|
|
#if 0
|
|
if ( ( node->compileFlags & C_DETAIL ) && !( node->children[0]->compileFlags & C_DETAIL ) && node->children[0]->planenum != PLANENUM_LEAF ) {
|
|
Sys_FPrintf( SYS_ERR, "I am detail, my child is structural\n", node->has_structural_children );
|
|
}
|
|
if ( ( node->compileFlags & C_DETAIL ) && isstruct ) {
|
|
Sys_FPrintf( SYS_ERR, "I am detail, my child is structural, this is a wtf2\n", node->has_structural_children );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
FaceBSP
|
|
|
|
List will be freed before returning
|
|
================
|
|
*/
|
|
tree_t *FaceBSP( facelist_t& list ) {
|
|
Sys_FPrintf( SYS_VRB, "--- FaceBSP ---\n" );
|
|
|
|
tree_t *tree = AllocTree();
|
|
|
|
int count = 0;
|
|
for ( const face_t& face : list )
|
|
{
|
|
WindingExtendBounds( face.w, tree->minmax );
|
|
count++;
|
|
}
|
|
Sys_FPrintf( SYS_VRB, "%9d faces\n", count );
|
|
|
|
for ( plane_t& plane : mapplanes )
|
|
{
|
|
plane.counter = 0;
|
|
}
|
|
|
|
tree->headnode = AllocNode();
|
|
tree->headnode->minmax = tree->minmax;
|
|
c_faceLeafs = 0;
|
|
|
|
BuildFaceTree_r( tree->headnode, list );
|
|
|
|
Sys_FPrintf( SYS_VRB, "%9d leafs\n", c_faceLeafs );
|
|
|
|
return tree;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
MakeStructuralBSPFaceList()
|
|
get structural brush faces
|
|
*/
|
|
|
|
facelist_t MakeStructuralBSPFaceList( const brush_t *list ){
|
|
facelist_t flist;
|
|
|
|
for ( const brush_t *b = list; b != NULL; b = b->next )
|
|
{
|
|
if ( !deepBSP && b->detail ) {
|
|
continue;
|
|
}
|
|
|
|
for ( int i = 0; i < b->numsides; i++ )
|
|
{
|
|
/* get side and winding */
|
|
const side_t& s = b->sides[ i ];
|
|
const winding_t *w = s.winding;
|
|
if ( w == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
/* ydnar: skip certain faces */
|
|
if ( s.compileFlags & C_SKIP ) {
|
|
continue;
|
|
}
|
|
|
|
/* allocate a face */
|
|
face_t& f = flist.emplace_front();
|
|
f.w = *w;
|
|
f.planenum = s.planenum & ~1;
|
|
f.compileFlags = s.compileFlags; /* ydnar */
|
|
if ( b->detail ) {
|
|
f.compileFlags |= C_DETAIL;
|
|
}
|
|
|
|
/* ydnar: set priority */
|
|
f.priority = 0;
|
|
if ( f.compileFlags & C_HINT ) {
|
|
f.priority += HINT_PRIORITY;
|
|
}
|
|
if ( f.compileFlags & C_ANTIPORTAL ) {
|
|
f.priority += ANTIPORTAL_PRIORITY;
|
|
}
|
|
if ( f.compileFlags & C_AREAPORTAL ) {
|
|
f.priority += AREAPORTAL_PRIORITY;
|
|
}
|
|
if ( f.compileFlags & C_DETAIL ) {
|
|
f.priority += DETAIL_PRIORITY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return flist;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
MakeVisibleBSPFaceList()
|
|
get visible brush faces
|
|
*/
|
|
|
|
facelist_t MakeVisibleBSPFaceList( const brush_t *list ){
|
|
facelist_t flist;
|
|
|
|
for ( const brush_t *b = list; b != NULL; b = b->next )
|
|
{
|
|
if ( !deepBSP && b->detail ) {
|
|
continue;
|
|
}
|
|
|
|
for ( int i = 0; i < b->numsides; i++ )
|
|
{
|
|
/* get side and winding */
|
|
const side_t& s = b->sides[ i ];
|
|
const winding_t *w = s.visibleHull;
|
|
if ( w == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
/* ydnar: skip certain faces */
|
|
if ( s.compileFlags & C_SKIP ) {
|
|
continue;
|
|
}
|
|
|
|
/* allocate a face */
|
|
face_t& f = flist.emplace_front();
|
|
f.w = *w;
|
|
f.planenum = s.planenum & ~1;
|
|
f.compileFlags = s.compileFlags; /* ydnar */
|
|
if ( b->detail ) {
|
|
f.compileFlags |= C_DETAIL;
|
|
}
|
|
|
|
/* ydnar: set priority */
|
|
f.priority = 0;
|
|
if ( f.compileFlags & C_HINT ) {
|
|
f.priority += HINT_PRIORITY;
|
|
}
|
|
if ( f.compileFlags & C_ANTIPORTAL ) {
|
|
f.priority += ANTIPORTAL_PRIORITY;
|
|
}
|
|
if ( f.compileFlags & C_AREAPORTAL ) {
|
|
f.priority += AREAPORTAL_PRIORITY;
|
|
}
|
|
if ( f.compileFlags & C_DETAIL ) {
|
|
f.priority += DETAIL_PRIORITY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return flist;
|
|
}
|