380 lines
8 KiB
C
380 lines
8 KiB
C
|
/*
|
||
|
===========================================================================
|
||
|
Copyright (C) 1999-2005 Id Software, Inc.
|
||
|
|
||
|
This file is part of Quake III Arena source code.
|
||
|
|
||
|
Quake III Arena 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 2 of the License,
|
||
|
or (at your option) any later version.
|
||
|
|
||
|
Quake III Arena 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 Foobar; if not, write to the Free Software
|
||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
===========================================================================
|
||
|
*/
|
||
|
|
||
|
#include "qbsp.h"
|
||
|
|
||
|
|
||
|
int c_faceLeafs;
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
AllocBspFace
|
||
|
================
|
||
|
*/
|
||
|
bspface_t *AllocBspFace( void ) {
|
||
|
bspface_t *f;
|
||
|
|
||
|
f = malloc(sizeof(*f));
|
||
|
memset( f, 0, sizeof(*f) );
|
||
|
|
||
|
return f;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
FreeBspFace
|
||
|
================
|
||
|
*/
|
||
|
void FreeBspFace( bspface_t *f ) {
|
||
|
if ( f->w ) {
|
||
|
FreeWinding( f->w );
|
||
|
}
|
||
|
free( f );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
SelectSplitPlaneNum
|
||
|
================
|
||
|
*/
|
||
|
int hintsplit;
|
||
|
|
||
|
#define BLOCK_SIZE 1024
|
||
|
int SelectSplitPlaneNum( node_t *node, bspface_t *list ) {
|
||
|
bspface_t *split;
|
||
|
bspface_t *check;
|
||
|
bspface_t *bestSplit;
|
||
|
int splits, facing, front, back;
|
||
|
int side;
|
||
|
plane_t *plane;
|
||
|
int value, bestValue;
|
||
|
int i;
|
||
|
vec3_t normal;
|
||
|
float dist;
|
||
|
int planenum;
|
||
|
|
||
|
hintsplit = qfalse;
|
||
|
// if it is crossing a 1k block boundary, force a split
|
||
|
for ( i = 0 ; i < 2 ; i++ ) {
|
||
|
dist = BLOCK_SIZE * ( floor( node->mins[i] / BLOCK_SIZE ) + 1 );
|
||
|
if ( node->maxs[i] > dist ) {
|
||
|
VectorClear( normal );
|
||
|
normal[i] = 1;
|
||
|
planenum = FindFloatPlane( normal, dist );
|
||
|
return planenum;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// pick one of the face planes
|
||
|
bestValue = -99999;
|
||
|
bestSplit = list;
|
||
|
|
||
|
for ( split = list ; split ; split = split->next ) {
|
||
|
split->checked = qfalse;
|
||
|
}
|
||
|
|
||
|
for ( split = list ; split ; split = split->next ) {
|
||
|
if ( split->checked ) {
|
||
|
continue;
|
||
|
}
|
||
|
plane = &mapplanes[ split->planenum ];
|
||
|
splits = 0;
|
||
|
facing = 0;
|
||
|
front = 0;
|
||
|
back = 0;
|
||
|
for ( check = list ; check ; check = check->next ) {
|
||
|
if ( check->planenum == split->planenum ) {
|
||
|
facing++;
|
||
|
check->checked = qtrue; // won't need to test this plane again
|
||
|
continue;
|
||
|
}
|
||
|
side = WindingOnPlaneSide( check->w, plane->normal, plane->dist );
|
||
|
if ( side == SIDE_CROSS ) {
|
||
|
splits++;
|
||
|
} else if ( side == SIDE_FRONT ) {
|
||
|
front++;
|
||
|
} else if ( side == SIDE_BACK ) {
|
||
|
back++;
|
||
|
}
|
||
|
}
|
||
|
value = 5*facing - 5*splits; // - abs(front-back);
|
||
|
if ( plane->type < 3 ) {
|
||
|
value+=5; // axial is better
|
||
|
}
|
||
|
value += split->priority; // prioritize hints higher
|
||
|
|
||
|
if ( value > bestValue ) {
|
||
|
bestValue = value;
|
||
|
bestSplit = split;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( bestValue == -99999 ) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (bestSplit->hint)
|
||
|
hintsplit = qtrue;
|
||
|
|
||
|
return bestSplit->planenum;
|
||
|
}
|
||
|
|
||
|
int CountFaceList( bspface_t *list ) {
|
||
|
int c;
|
||
|
c = 0;
|
||
|
for ( ; list ; list = list->next ) {
|
||
|
c++;
|
||
|
}
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
BuildFaceTree_r
|
||
|
================
|
||
|
*/
|
||
|
void BuildFaceTree_r( node_t *node, bspface_t *list ) {
|
||
|
bspface_t *split;
|
||
|
bspface_t *next;
|
||
|
int side;
|
||
|
plane_t *plane;
|
||
|
bspface_t *newFace;
|
||
|
bspface_t *childLists[2];
|
||
|
winding_t *frontWinding, *backWinding;
|
||
|
int i;
|
||
|
int splitPlaneNum;
|
||
|
|
||
|
i = CountFaceList( list );
|
||
|
|
||
|
splitPlaneNum = SelectSplitPlaneNum( node, list );
|
||
|
// if we don't have any more faces, this is a node
|
||
|
if ( splitPlaneNum == -1 ) {
|
||
|
node->planenum = PLANENUM_LEAF;
|
||
|
c_faceLeafs++;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// partition the list
|
||
|
node->planenum = splitPlaneNum;
|
||
|
node->hint = hintsplit;
|
||
|
plane = &mapplanes[ splitPlaneNum ];
|
||
|
childLists[0] = NULL;
|
||
|
childLists[1] = NULL;
|
||
|
for ( split = list ; split ; split = next ) {
|
||
|
next = split->next;
|
||
|
|
||
|
if ( split->planenum == node->planenum ) {
|
||
|
FreeBspFace( split );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
side = WindingOnPlaneSide( split->w, plane->normal, plane->dist );
|
||
|
|
||
|
if ( side == SIDE_CROSS ) {
|
||
|
ClipWindingEpsilon( split->w, plane->normal, plane->dist, CLIP_EPSILON * 2,
|
||
|
&frontWinding, &backWinding );
|
||
|
if ( frontWinding ) {
|
||
|
newFace = AllocBspFace();
|
||
|
newFace->w = frontWinding;
|
||
|
newFace->next = childLists[0];
|
||
|
newFace->planenum = split->planenum;
|
||
|
newFace->priority = split->priority;
|
||
|
newFace->hint = split->hint;
|
||
|
childLists[0] = newFace;
|
||
|
}
|
||
|
if ( backWinding ) {
|
||
|
newFace = AllocBspFace();
|
||
|
newFace->w = backWinding;
|
||
|
newFace->next = childLists[1];
|
||
|
newFace->planenum = split->planenum;
|
||
|
newFace->priority = split->priority;
|
||
|
newFace->hint = split->hint;
|
||
|
childLists[1] = newFace;
|
||
|
}
|
||
|
FreeBspFace( split );
|
||
|
} else if ( side == SIDE_FRONT ) {
|
||
|
split->next = childLists[0];
|
||
|
childLists[0] = split;
|
||
|
} else if ( side == SIDE_BACK ) {
|
||
|
split->next = childLists[1];
|
||
|
childLists[1] = split;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// recursively process children
|
||
|
for ( i = 0 ; i < 2 ; i++ ) {
|
||
|
node->children[i] = AllocNode();
|
||
|
node->children[i]->parent = node;
|
||
|
VectorCopy( node->mins, node->children[i]->mins );
|
||
|
VectorCopy( node->maxs, node->children[i]->maxs );
|
||
|
}
|
||
|
|
||
|
for ( i = 0 ; i < 3 ; i++ ) {
|
||
|
if ( plane->normal[i] == 1 ) {
|
||
|
node->children[0]->mins[i] = plane->dist;
|
||
|
node->children[1]->maxs[i] = plane->dist;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ( i = 0 ; i < 2 ; i++ ) {
|
||
|
BuildFaceTree_r ( node->children[i], childLists[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
FaceBSP
|
||
|
|
||
|
List will be freed before returning
|
||
|
================
|
||
|
*/
|
||
|
tree_t *FaceBSP( bspface_t *list ) {
|
||
|
tree_t *tree;
|
||
|
bspface_t *face;
|
||
|
int i;
|
||
|
int count;
|
||
|
|
||
|
qprintf( "--- FaceBSP ---\n" );
|
||
|
|
||
|
tree = AllocTree ();
|
||
|
|
||
|
count = 0;
|
||
|
for ( face = list ; face ; face = face->next ) {
|
||
|
count++;
|
||
|
for ( i = 0 ; i < face->w->numpoints ; i++ ) {
|
||
|
AddPointToBounds( face->w->p[i], tree->mins, tree->maxs);
|
||
|
}
|
||
|
}
|
||
|
qprintf( "%5i faces\n", count );
|
||
|
|
||
|
tree->headnode = AllocNode();
|
||
|
VectorCopy( tree->mins, tree->headnode->mins );
|
||
|
VectorCopy( tree->maxs, tree->headnode->maxs );
|
||
|
c_faceLeafs = 0;
|
||
|
|
||
|
BuildFaceTree_r ( tree->headnode, list );
|
||
|
|
||
|
qprintf( "%5i leafs\n", c_faceLeafs );
|
||
|
|
||
|
return tree;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
BspFaceForPortal
|
||
|
=================
|
||
|
*/
|
||
|
bspface_t *BspFaceForPortal( portal_t *p ) {
|
||
|
bspface_t *f;
|
||
|
|
||
|
f = AllocBspFace();
|
||
|
f->w = CopyWinding( p->winding );
|
||
|
f->planenum = p->onnode->planenum & ~1;
|
||
|
|
||
|
return f;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
MakeStructuralBspFaceList
|
||
|
=================
|
||
|
*/
|
||
|
bspface_t *MakeStructuralBspFaceList( bspbrush_t *list ) {
|
||
|
bspbrush_t *b;
|
||
|
int i;
|
||
|
side_t *s;
|
||
|
winding_t *w;
|
||
|
bspface_t *f, *flist;
|
||
|
|
||
|
flist = NULL;
|
||
|
for ( b = list ; b ; b = b->next ) {
|
||
|
if ( b->detail ) {
|
||
|
continue;
|
||
|
}
|
||
|
for ( i = 0 ; i < b->numsides ; i++ ) {
|
||
|
s = &b->sides[i];
|
||
|
w = s->winding;
|
||
|
if ( !w ) {
|
||
|
continue;
|
||
|
}
|
||
|
f = AllocBspFace();
|
||
|
f->w = CopyWinding( w );
|
||
|
f->planenum = s->planenum & ~1;
|
||
|
f->next = flist;
|
||
|
if (s->surfaceFlags & SURF_HINT) {
|
||
|
//f->priority = HINT_PRIORITY;
|
||
|
f->hint = qtrue;
|
||
|
}
|
||
|
flist = f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return flist;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
MakeVisibleBspFaceList
|
||
|
=================
|
||
|
*/
|
||
|
bspface_t *MakeVisibleBspFaceList( bspbrush_t *list ) {
|
||
|
bspbrush_t *b;
|
||
|
int i;
|
||
|
side_t *s;
|
||
|
winding_t *w;
|
||
|
bspface_t *f, *flist;
|
||
|
|
||
|
flist = NULL;
|
||
|
for ( b = list ; b ; b = b->next ) {
|
||
|
if ( b->detail ) {
|
||
|
continue;
|
||
|
}
|
||
|
for ( i = 0 ; i < b->numsides ; i++ ) {
|
||
|
s = &b->sides[i];
|
||
|
w = s->visibleHull;
|
||
|
if ( !w ) {
|
||
|
continue;
|
||
|
}
|
||
|
f = AllocBspFace();
|
||
|
f->w = CopyWinding( w );
|
||
|
f->planenum = s->planenum & ~1;
|
||
|
f->next = flist;
|
||
|
if (s->surfaceFlags & SURF_HINT) {
|
||
|
//f->priority = HINT_PRIORITY;
|
||
|
f->hint = qtrue;
|
||
|
}
|
||
|
flist = f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return flist;
|
||
|
}
|
||
|
|