This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/common/bsplib/qbsp3.c

724 lines
15 KiB
C

//=======================================================================
// Copyright XashXT Group 2007 ©
// qbsp.c - emit bsp nodes and leafs
//=======================================================================
#include "bsplib.h"
#include "const.h"
int block_xl = -8, block_xh = 7, block_yl = -8, block_yh = 7;
int entity_num;
node_t *block_nodes[10][10];
int c_nofaces;
int c_facenodes;
int firstmodleaf;
/*
=========================================================
ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES
=========================================================
*/
/*
============
EmitPlanes
There is no oportunity to discard planes, because all of the original
brushes will be saved in the map.
============
*/
void EmitPlanes( void )
{
int i;
dplane_t *dp;
plane_t *mp;
mp = mapplanes;
for( i = 0; i < nummapplanes; i++, mp++ )
{
dp = &dplanes[numplanes];
VectorCopy( mp->normal, dp->normal );
dp->dist = mp->dist;
numplanes++;
}
}
//========================================================
void EmitMarkFace( dleaf_t *leaf_p, bspface_t *f )
{
int i;
int facenum;
while( f->merged ) f = f->merged;
if( f->split[0] )
{
EmitMarkFace( leaf_p, f->split[0] );
EmitMarkFace( leaf_p, f->split[1] );
return;
}
facenum = f->outputnumber;
if( facenum == -1 ) return; // degenerate face
if( facenum < 0 || facenum >= numfaces )
Sys_Break( "bad leafface\n" );
for( i = leaf_p->firstleafface; i < numleaffaces; i++ )
if( dleaffaces[i] == facenum ) break; // merged out face
if( i == numleaffaces )
{
if( numleaffaces >= MAX_MAP_LEAFFACES )
Sys_Break( "MAX_MAP_LEAFFACES limit exceeded\n" );
dleaffaces[numleaffaces] = facenum;
numleaffaces++;
}
}
/*
==================
EmitLeaf
==================
*/
void EmitLeaf( node_t *node )
{
portal_t *p;
bspface_t *f;
bspbrush_t *b;
dleaf_t *leaf_p;
int s;
// emit a leaf
if( numleafs >= MAX_MAP_LEAFS ) Sys_Break( "MAX_MAP_LEAFS linit exceeded\n" );
leaf_p = &dleafs[numleafs];
numleafs++;
leaf_p->contents = node->contents;
leaf_p->cluster = node->cluster;
leaf_p->area = node->area;
// write bounding box info
VectorCopy( node->mins, leaf_p->mins );
VectorCopy( node->maxs, leaf_p->maxs );
// write the leafbrushes
leaf_p->firstleafbrush = numleafbrushes;
for( b = node->brushlist; b; b = b->next )
{
if( numleafbrushes >= MAX_MAP_LEAFBRUSHES )
Sys_Break( "MAX_MAP_LEAFBRUSHES\n" );
dleafbrushes[numleafbrushes] = b->original->outputnum;
numleafbrushes++;
}
leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;
// write the leaffaces
if( leaf_p->contents & CONTENTS_SOLID ) return; // no leaffaces in solids
leaf_p->firstleafface = numleaffaces;
for( p = node->portals; p; p = p->next[s] )
{
s = (p->nodes[1] == node);
f = p->face[s];
if( !f ) continue; // not a visible portal
EmitMarkFace( leaf_p, f );
}
leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;
}
/*
==================
EmitFace
==================
*/
void EmitFace( bspface_t *f )
{
dface_t *df;
int i, e;
f->outputnumber = -1;
if( f->numpoints < 3 ) return; // degenerated
if( f->merged || f->split[0] || f->split[1] )
return; // not a final face
// save output number so leaffaces can use
f->outputnumber = numfaces;
if( numfaces >= MAX_MAP_FACES )
Sys_Break( "MAX_MAP_FACES limit exceeded\n" );
df = &dfaces[numfaces];
numfaces++;
// planenum is used by qlight, but not quake
df->planenum = f->planenum & (~1);
df->side = f->planenum & 1;
df->firstedge = numsurfedges;
df->numedges = f->numpoints;
df->texinfo = f->texinfo;
// FIXME: emit indices here, not edges
for( i = 0; i < f->numpoints; i++ )
{
e = GetEdge( f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f );
if( numsurfedges >= MAX_MAP_SURFEDGES )
Sys_Break( "MAX_MAP_SURFEDGES limit exceeded\n" );
dsurfedges[numsurfedges] = e;
numsurfedges++;
}
}
/*
============
EmitDrawingNode_r
============
*/
int EmitDrawNode_r( node_t *node )
{
dnode_t *n;
bspface_t *f;
int i;
if( node->planenum == PLANENUM_LEAF )
{
EmitLeaf( node );
return -numleafs;
}
// emit a node
if( numnodes == MAX_MAP_NODES )
Sys_Break( "MAX_MAP_NODES limit exceeded\n" );
n = &dnodes[numnodes];
numnodes++;
VectorCopy( node->mins, n->mins );
VectorCopy( node->maxs, n->maxs );
if( node->planenum & 1 )
Sys_Break( "WriteDrawNodes_r: odd planenum\n" );
n->planenum = node->planenum;
n->firstface = numfaces;
if( !node->faces ) c_nofaces++;
else c_facenodes++;
for( f = node->faces; f; f = f->next )
EmitFace( f );
n->numfaces = numfaces - n->firstface;
// recursively output the other nodes
for( i = 0; i < 2; i++ )
{
if( node->children[i]->planenum == PLANENUM_LEAF )
{
n->children[i] = -(numleafs + 1);
EmitLeaf( node->children[i] );
}
else
{
n->children[i] = numnodes;
EmitDrawNode_r( node->children[i] );
}
}
return n - dnodes;
}
//===========================================================
/*
============
SetModelNumbers
============
*/
void SetModelNumbers( void )
{
int i, models = 1;
for( i = 1; i<num_entities; i++ )
{
if( entities[i].brushes )
{
SetKeyValue( &entities[i], "model", va( "*%i", models ));
models++;
}
}
}
/*
============
SetLightStyles
============
*/
void SetLightStyles( void )
{
char *t;
bsp_entity_t *e;
int i, j, k, stylenum = 0;
char lighttargets[64][MAX_QPATH];
// any light that is controlled (has a targetname)
// must have a unique style number generated for it
for( i = 1; i < num_entities; i++ )
{
e = &entities[i];
t = ValueForKey( e, "classname" );
if( com.strncmp( t, "light", 5 ))
{
if(!com.strncmp( t, "func_light", 10 ))
{
// may create func_light family
// e.g. func_light_fluoro, func_light_broken etc
k = com.atoi(ValueForKey( e, "spawnflags" ));
if( k & SF_START_ON ) t = "-2"; // initially on
else t = "-1"; // initially off
}
else t = ValueForKey( e, "style" );
switch( com.atoi( t ))
{
case 0: continue; // not a light, no style, generally pretty boring
case -1: // normal switchable texlight (start off)
SetKeyValue( e, "style", va( "%i", 32 + stylenum ));
stylenum++;
continue;
case -2: // backwards switchable texlight (start on)
SetKeyValue(e, "style", va( "%i", -(32 + stylenum )));
stylenum++;
continue;
default: continue; // nothing to set
}
}
t = ValueForKey( e, "targetname" );
if( !t[0] ) continue;
// find this targetname
for( j = 0; j < stylenum; j++ )
{
if( !com.strcmp( lighttargets[j], t ))
break; // already exist
}
if( j == stylenum )
{
if( stylenum == 64 )
{
Msg( "switchable lightstyles limit (%i) exceeded at entity #%i\n", 64, i );
break; // nothing to process
}
com.strncpy( lighttargets[j], t, MAX_QPATH );
stylenum++;
}
SetKeyValue( e, "style", va( "%i", 32 + j ));
}
}
//===========================================================
/*
============
EmitBrushes
============
*/
void EmitBrushes( bspbrush_t *brushes )
{
int j;
dbrush_t *db;
bspbrush_t *b;
dbrushside_t *cp;
for( b = brushes; b; b = b->next )
{
if( numbrushes == MAX_MAP_BRUSHES ) Sys_Break( "MAX_MAP_BRUSHES limit exceeded\n" );
b->outputnum = numbrushes;
db = &dbrushes[numbrushes];
numbrushes++;
db->contents = b->contents;
db->firstside = numbrushsides;
db->numsides = 0;
for( j = 0; j < b->numsides; j++ )
{
if( numbrushsides == MAX_MAP_BRUSHSIDES )
Sys_Break( "MAX_MAP_BRUSHSIDES limit exceeded\n" );
cp = &dbrushsides[numbrushsides];
db->numsides++;
numbrushsides++;
cp->planenum = b->sides[j].planenum;
cp->texinfo = b->sides[j].texinfo;
}
}
}
//===========================================================
/*
==================
BeginBSPFile
==================
*/
void BeginBSPFile( void )
{
// these values may actually be initialized
// if the file existed when loaded, so clear them explicitly
nummodels = 0;
numfaces = 0;
numnodes = 0;
numbrushsides = 0;
numvertexes = 0;
numleaffaces = 0;
numleafbrushes = 0;
numsurfedges = 0;
numedges = 1; // edge 0 is not used, because 0 can't be negated.
numvertexes = 1; // leave vertex 0 as an error
numleafs = 1; // leave leaf 0 as an error
numindexes = 1; // store number of initial vertex
dleafs[0].contents = CONTENTS_SOLID; // assume error
}
/*
============
EndBSPFile
============
*/
void EndBSPFile( void )
{
EmitPlanes();
UnparseEntities();
// write the map
WriteBSPFile();
}
/*
==================
BeginModel
==================
*/
void BeginModel( void )
{
bspbrush_t *b;
bsp_entity_t *e;
dmodel_t *mod;
vec3_t mins, maxs;
if( nummodels == MAX_MAP_MODELS )
Sys_Break( "MAX_MAP_MODELS limit exceeded\n" );
mod = &dmodels[nummodels];
// bound the brushes
e = &entities[entity_num];
ClearBounds( mins, maxs );
for( b = e->brushes; b; b = b->next )
{
if( !b->numsides ) continue; // not a real brush (origin brush, etc)
AddPointToBounds (b->mins, mins, maxs);
AddPointToBounds (b->maxs, mins, maxs);
}
VectorCopy( mins, mod->mins );
VectorCopy( maxs, mod->maxs );
mod->firstface = numfaces;
mod->firstbrush = numbrushes;
EmitBrushes( e->brushes );
}
/*
==================
EndModel
==================
*/
void EndModel( node_t *headnode )
{
dmodel_t *mod;
mod = &dmodels[nummodels];
EmitDrawNode_r( headnode );
mod->numfaces = numfaces - mod->firstface;
mod->numbrushes = numbrushes - mod->firstbrush;
EmitAreaPortals( headnode );
nummodels++;
}
/*
============
BlockTree
============
*/
node_t *BlockTree (int xl, int yl, int xh, int yh)
{
node_t *node;
vec3_t normal;
float dist;
int mid;
if (xl == xh && yl == yh)
{
node = block_nodes[xl+5][yl+5];
if (!node)
{ // return an empty leaf
node = AllocNode ();
node->planenum = PLANENUM_LEAF;
node->contents = 0; //CONTENTS_SOLID;
return node;
}
return node;
}
// create a seperator along the largest axis
node = AllocNode ();
if (xh - xl > yh - yl)
{ // split x axis
mid = xl + (xh-xl)/2 + 1;
normal[0] = 1;
normal[1] = 0;
normal[2] = 0;
dist = mid*32768;
node->planenum = FindFloatPlane (normal, dist);
node->children[0] = BlockTree ( mid, yl, xh, yh);
node->children[1] = BlockTree ( xl, yl, mid-1, yh);
}
else
{
mid = yl + (yh-yl)/2 + 1;
normal[0] = 0;
normal[1] = 1;
normal[2] = 0;
dist = mid*32768;
node->planenum = FindFloatPlane (normal, dist);
node->children[0] = BlockTree ( xl, mid, xh, yh);
node->children[1] = BlockTree ( xl, yl, xh, mid-1);
}
return node;
}
/*
============
ProcessBlock_Thread
============
*/
void ProcessBlock_Thread( int blocknum )
{
int xblock, yblock;
vec3_t mins, maxs;
bspbrush_t *brushes, *start;
tree_t *tree;
node_t *node;
yblock = block_yl + blocknum / (block_xh-block_xl+1);
xblock = block_xl + blocknum % (block_xh-block_xl+1);
mins[0] = xblock*32768;
mins[1] = yblock*32768;
mins[2] = -131072;
maxs[0] = (xblock+1)*32768;
maxs[1] = (yblock+1)*32768;
maxs[2] = 131072;
start = entities[entity_num].brushes;
// the makelist and chopbrushes could be cached between the passes...
brushes = MakeBspBrushList( start, mins, maxs, false );
if( !brushes )
{
node = AllocNode ();
node->planenum = PLANENUM_LEAF;
node->contents = CONTENTS_SOLID;
block_nodes[xblock+5][yblock+5] = node;
return;
}
brushes = ChopBrushes( brushes );
tree = BrushBSP( brushes, mins, maxs );
block_nodes[xblock+5][yblock+5] = tree->headnode;
}
/*
============
ProcessWorldModel
============
*/
void ProcessWorldModel( void )
{
tree_t *tree;
bool optimize;
bool leaked = false;
BeginModel();
// perform per-block operations
if (block_xh * 32768 > map_maxs[0])
block_xh = floor(map_maxs[0]/32768.0);
if ( (block_xl+1) * 32768 < map_mins[0])
block_xl = floor(map_mins[0]/32768.0);
if (block_yh * 32768 > map_maxs[1])
block_yh = floor(map_maxs[1]/32768.0);
if ( (block_yl+1) * 32768 < map_mins[1])
block_yl = floor(map_mins[1]/32768.0);
if (block_xl <-4) block_xl = -4;
if (block_yl <-4) block_yl = -4;
if (block_xh > 3) block_xh = 3;
if (block_yh > 3) block_yh = 3;
for (optimize = false; optimize <= true; optimize++)
{
RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1), true, ProcessBlock_Thread);
// build the division tree
// oversizing the blocks guarantees that all the boundaries
// will also get nodes.
tree = AllocTree ();
tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1);
tree->mins[0] = (block_xl)*32768;
tree->mins[1] = (block_yl)*32768;
tree->mins[2] = map_mins[2] - 8;
tree->maxs[0] = (block_xh+1)*32768;
tree->maxs[1] = (block_yh+1)*32768;
tree->maxs[2] = map_maxs[2] + 8;
//
// perform the global operations
//
MakeTreePortals (tree);
if (FloodEntities (tree))
{
FillOutside (tree->headnode);
}
else
{
Msg("**** leaked ****\n");
leaked = true;
LeakFile (tree);
}
MarkVisibleSides( tree, entities[entity_num].brushes );
if( leaked ) break;
if( !optimize ) FreeTree( tree );
}
FloodAreas(tree);
MakeFaces(tree->headnode);
FixTjuncs(tree->headnode);
PruneNodes(tree->headnode);
EndModel( tree->headnode );
if(!leaked) WritePortalFile( tree );
FreeTree( tree );
}
/*
============
ProcessSubModel
============
*/
void ProcessSubModel( void )
{
bsp_entity_t *e;
tree_t *tree;
bspbrush_t *list;
vec3_t mins, maxs;
BeginModel();
e = &entities[entity_num];
mins[0] = mins[1] = mins[2] = -131072;
maxs[0] = maxs[1] = maxs[2] = 131072;
list = MakeBspBrushList( e->brushes, mins, maxs, false );
list = ChopBrushes( list );
tree = BrushBSP( list, mins, maxs );
MakeTreePortals( tree );
MarkVisibleSides( tree, e->brushes );
MakeFaces (tree->headnode);
FixTjuncs (tree->headnode);
EndModel( tree->headnode );
FreeTree( tree );
}
/*
============
ProcessModels
============
*/
void ProcessModels (void)
{
BeginBSPFile ();
for( entity_num = 0; entity_num < num_entities; entity_num++ )
{
if( !entities[entity_num].brushes )
continue;
if( !entity_num )
ProcessWorldModel();
else ProcessSubModel();
}
EndBSPFile();
}
/*
============
WbspMain
============
*/
void WbspMain ( bool option )
{
Msg("---- CSG ---- [%s]\n", onlyents ? "onlyents" : "normal" );
// if onlyents, just grab the entites and resave
if( option )
{
LoadBSPFile();
num_entities = 0;
LoadMapFile();
SetModelNumbers();
SetLightStyles();
UnparseEntities();
WriteBSPFile();
}
else
{
// start from scratch
LoadMapFile();
SetModelNumbers();
SetLightStyles();
ProcessModels();
LoadBSPFile();
ProcessCollisionTree();
WriteBSPFile();
}
}