quake-hipnotic-sdk/utils/qbsp/solidbsp.c
1997-03-11 00:00:00 +00:00

754 lines
13 KiB
C

// solidbsp.c
#include "bsp5.h"
int leaffaces;
int nodefaces;
int splitnodes;
int c_solid, c_empty, c_water;
qboolean usemidsplit;
//============================================================================
/*
==================
faceside
for bsp hueristic
==================
*/
int faceside (face_t *in, plane_t *split)
{
int frontcount, backcount;
vec_t dot;
int i;
vec_t *p;
frontcount = backcount = 0;
// axial planes are fast
if (split->type < 3)
for (i=0, p = in->pts[0]+split->type ; i<in->numpoints ; i++, p+=3)
{
if (*p > split->dist + on_epsilon)
{
if (backcount)
return side_on;
frontcount = 1;
}
else if (*p < split->dist - on_epsilon)
{
if (frontcount)
return side_on;
backcount = 1;
}
}
else
// sloping planes take longer
for (i=0, p = in->pts[0] ; i<in->numpoints ; i++, p+=3)
{
dot = dotproduct (p, split->normal);
dot -= split->dist;
if (dot > on_epsilon)
{
if (backcount)
return side_on;
frontcount = 1;
}
else if (dot < -on_epsilon)
{
if (frontcount)
return side_on;
backcount = 1;
}
}
if (!frontcount)
return side_back;
if (!backcount)
return side_front;
return side_on;
}
/*
==================
choosemidplanefromlist
the clipping hull bsp doesn't worry about avoiding splits
==================
*/
surface_t *choosemidplanefromlist (surface_t *surfaces, vec3_t mins, vec3_t maxs)
{
int j,l;
surface_t *p, *bestsurface;
vec_t bestvalue, value, dist;
plane_t *plane;
//
// pick the plane that splits the least
//
bestvalue = 6*8192*8192;
bestsurface = null;
for (p=surfaces ; p ; p=p->next)
{
if (p->onnode)
continue;
plane = &planes[p->planenum];
// check for axis aligned surfaces
l = plane->type;
if (l > plane_z)
continue;
//
// calculate the split metric along axis l, smaller values are better
//
value = 0;
dist = plane->dist * plane->normal[l];
for (j=0 ; j<3 ; j++)
{
if (j == l)
{
value += (maxs[l]-dist)*(maxs[l]-dist);
value += (dist-mins[l])*(dist-mins[l]);
}
else
value += 2*(maxs[j]-mins[j])*(maxs[j]-mins[j]);
}
if (value > bestvalue)
continue;
//
// currently the best!
//
bestvalue = value;
bestsurface = p;
}
if (!bestsurface)
{
for (p=surfaces ; p ; p=p->next)
if (!p->onnode)
return p; // first valid surface
error ("choosemidplanefromlist: no valid planes");
}
return bestsurface;
}
/*
==================
chooseplanefromlist
the real bsp hueristic
==================
*/
surface_t *chooseplanefromlist (surface_t *surfaces, vec3_t mins, vec3_t maxs, qboolean usefloors)
{
int j,k,l;
surface_t *p, *p2, *bestsurface;
vec_t bestvalue, bestdistribution, value, dist;
plane_t *plane;
face_t *f;
//
// pick the plane that splits the least
//
bestvalue = 99999;
bestsurface = null;
bestdistribution = 9e30;
for (p=surfaces ; p ; p=p->next)
{
if (p->onnode)
continue;
plane = &planes[p->planenum];
k = 0;
if (!usefloors && plane->normal[2] == 1)
continue;
for (p2=surfaces ; p2 ; p2=p2->next)
{
if (p2 == p)
continue;
if (p2->onnode)
continue;
for (f=p2->faces ; f ; f=f->next)
{
if (faceside (f, plane) == side_on)
{
k++;
if (k >= bestvalue)
break;
}
}
if (k > bestvalue)
break;
}
if (k > bestvalue)
continue;
// if equal numbers, axial planes win, then decide on spatial subdivision
if (k < bestvalue || (k == bestvalue && plane->type < plane_anyx) )
{
// check for axis aligned surfaces
l = plane->type;
if (l <= plane_z)
{ // axial aligned
//
// calculate the split metric along axis l
//
value = 0;
for (j=0 ; j<3 ; j++)
{
if (j == l)
{
dist = plane->dist * plane->normal[l];
value += (maxs[l]-dist)*(maxs[l]-dist);
value += (dist-mins[l])*(dist-mins[l]);
}
else
value += 2*(maxs[j]-mins[j])*(maxs[j]-mins[j]);
}
if (value > bestdistribution && k == bestvalue)
continue;
bestdistribution = value;
}
//
// currently the best!
//
bestvalue = k;
bestsurface = p;
}
}
return bestsurface;
}
/*
==================
selectpartition
selects a surface from a linked list of surfaces to split the group on
returns null if the surface list can not be divided any more (a leaf)
==================
*/
surface_t *selectpartition (surface_t *surfaces)
{
int i,j;
vec3_t mins, maxs;
surface_t *p, *bestsurface;
//
// count onnode surfaces
//
i = 0;
bestsurface = null;
for (p=surfaces ; p ; p=p->next)
if (!p->onnode)
{
i++;
bestsurface = p;
}
if (i==0)
return null;
if (i==1)
return bestsurface; // this is a final split
//
// calculate a bounding box of the entire surfaceset
//
for (i=0 ; i<3 ; i++)
{
mins[i] = 99999;
maxs[i] = -99999;
}
for (p=surfaces ; p ; p=p->next)
for (j=0 ; j<3 ; j++)
{
if (p->mins[j] < mins[j])
mins[j] = p->mins[j];
if (p->maxs[j] > maxs[j])
maxs[j] = p->maxs[j];
}
if (usemidsplit) // do fast way for clipping hull
return choosemidplanefromlist (surfaces, mins, maxs);
// do slow way to save poly splits for drawing hull
#if 0
bestsurface = chooseplanefromlist (surfaces, mins, maxs, false);
if (bestsurface)
return bestsurface;
#endif
return chooseplanefromlist (surfaces, mins, maxs, true);
}
//============================================================================
/*
=================
calcsurfaceinfo
calculates the bounding box
=================
*/
void calcsurfaceinfo (surface_t *surf)
{
int i,j;
face_t *f;
if (!surf->faces)
error ("calcsurfaceinfo: surface without a face");
//
// calculate a bounding box
//
for (i=0 ; i<3 ; i++)
{
surf->mins[i] = 99999;
surf->maxs[i] = -99999;
}
for (f=surf->faces ; f ; f=f->next)
{
if (f->contents[0] >= 0 || f->contents[1] >= 0)
error ("bad contents");
for (i=0 ; i<f->numpoints ; i++)
for (j=0 ; j<3 ; j++)
{
if (f->pts[i][j] < surf->mins[j])
surf->mins[j] = f->pts[i][j];
if (f->pts[i][j] > surf->maxs[j])
surf->maxs[j] = f->pts[i][j];
}
}
}
/*
==================
divideplane
==================
*/
void divideplane (surface_t *in, plane_t *split, surface_t **front, surface_t **back)
{
face_t *facet, *next;
face_t *frontlist, *backlist;
face_t *frontfrag, *backfrag;
surface_t *news;
plane_t *inplane;
inplane = &planes[in->planenum];
// parallel case is easy
if (vectorcompare (inplane->normal, split->normal))
{
// check for exactly on node
if (inplane->dist == split->dist)
{ // divide the facets to the front and back sides
news = allocsurface ();
*news = *in;
facet=in->faces;
in->faces = null;
news->faces = null;
in->onnode = news->onnode = true;
for ( ; facet ; facet=next)
{
next = facet->next;
if (facet->planeside == 1)
{
facet->next = news->faces;
news->faces = facet;
}
else
{
facet->next = in->faces;
in->faces = facet;
}
}
if (in->faces)
*front = in;
else
*front = null;
if (news->faces)
*back = news;
else
*back = null;
return;
}
if (inplane->dist > split->dist)
{
*front = in;
*back = null;
}
else
{
*front = null;
*back = in;
}
return;
}
// do a real split. may still end up entirely on one side
// optimize: use bounding box for fast test
frontlist = null;
backlist = null;
for (facet = in->faces ; facet ; facet = next)
{
next = facet->next;
splitface (facet, split, &frontfrag, &backfrag);
if (frontfrag)
{
frontfrag->next = frontlist;
frontlist = frontfrag;
}
if (backfrag)
{
backfrag->next = backlist;
backlist = backfrag;
}
}
// if nothing actually got split, just move the in plane
if (frontlist == null)
{
*front = null;
*back = in;
in->faces = backlist;
return;
}
if (backlist == null)
{
*front = in;
*back = null;
in->faces = frontlist;
return;
}
// stuff got split, so allocate one new plane and reuse in
news = allocsurface ();
*news = *in;
news->faces = backlist;
*back = news;
in->faces = frontlist;
*front = in;
// recalc bboxes and flags
calcsurfaceinfo (news);
calcsurfaceinfo (in);
}
/*
==================
dividenodebounds
==================
*/
void dividenodebounds (node_t *node, plane_t *split)
{
vectorcopy (node->mins, node->children[0]->mins);
vectorcopy (node->mins, node->children[1]->mins);
vectorcopy (node->maxs, node->children[0]->maxs);
vectorcopy (node->maxs, node->children[1]->maxs);
// optimize: sloping cuts can give a better bbox than this...
if (split->type > 2)
return;
node->children[0]->mins[split->type] =
node->children[1]->maxs[split->type] = split->dist;
}
/*
==================
linkconvexfaces
determines the contents of the leaf and creates the final list of
original faces that have some fragment inside this leaf
==================
*/
void linkconvexfaces (surface_t *planelist, node_t *leafnode)
{
face_t *f, *next;
surface_t *surf, *pnext;
int i, count;
leafnode->faces = null;
leafnode->contents = 0;
leafnode->planenum = -1;
count = 0;
for ( surf = planelist ; surf ; surf = surf->next)
{
for (f = surf->faces ; f ; f=f->next)
{
count++;
if (!leafnode->contents)
leafnode->contents = f->contents[0];
else if (leafnode->contents != f->contents[0])
error ("mixed face contents in leafnode");
}
}
if (!leafnode->contents)
leafnode->contents = contents_solid;
switch (leafnode->contents)
{
case contents_empty:
c_empty++;
break;
case contents_solid:
c_solid++;
break;
case contents_water:
case contents_slime:
case contents_lava:
case contents_sky:
c_water++;
break;
default:
error ("linkconvexfaces: bad contents number");
}
//
// write the list of faces, and free the originals
//
leaffaces += count;
leafnode->markfaces = malloc(sizeof(face_t *)*(count+1));
i = 0;
for ( surf = planelist ; surf ; surf = pnext)
{
pnext = surf->next;
for (f = surf->faces ; f ; f=next)
{
next = f->next;
leafnode->markfaces[i] = f->original;
i++;
freeface (f);
}
freesurface (surf);
}
leafnode->markfaces[i] = null; // sentinal
}
/*
==================
linknodefaces
returns a duplicated list of all faces on surface
==================
*/
face_t *linknodefaces (surface_t *surface)
{
face_t *f, *new, **prevptr;
face_t *list;
list = null;
// subdivide
prevptr = &surface->faces;
while (1)
{
f = *prevptr;
if (!f)
break;
subdivideface (f, prevptr);
f = *prevptr;
prevptr = &f->next;
}
// copy
for (f=surface->faces ; f ; f=f->next)
{
nodefaces++;
new = allocface ();
*new = *f;
f->original = new;
new->next = list;
list = new;
}
return list;
}
/*
==================
partitionsurfaces
==================
*/
void partitionsurfaces (surface_t *surfaces, node_t *node)
{
surface_t *split, *p, *next;
surface_t *frontlist, *backlist;
surface_t *frontfrag, *backfrag;
plane_t *splitplane;
split = selectpartition (surfaces);
if (!split)
{ // this is a leaf node
node->planenum = planenum_leaf;
linkconvexfaces (surfaces, node);
return;
}
splitnodes++;
node->faces = linknodefaces (split);
node->children[0] = allocnode ();
node->children[1] = allocnode ();
node->planenum = split->planenum;
splitplane = &planes[split->planenum];
dividenodebounds (node, splitplane);
//
// multiple surfaces, so split all the polysurfaces into front and back lists
//
frontlist = null;
backlist = null;
for (p=surfaces ; p ; p=next)
{
next = p->next;
divideplane (p, splitplane, &frontfrag, &backfrag);
if (frontfrag && backfrag)
{
// the plane was split, which may expose oportunities to merge
// adjacent faces into a single face
// mergeplanefaces (frontfrag);
// mergeplanefaces (backfrag);
}
if (frontfrag)
{
if (!frontfrag->faces)
error ("surface with no faces");
frontfrag->next = frontlist;
frontlist = frontfrag;
}
if (backfrag)
{
if (!backfrag->faces)
error ("surface with no faces");
backfrag->next = backlist;
backlist = backfrag;
}
}
partitionsurfaces (frontlist, node->children[0]);
partitionsurfaces (backlist, node->children[1]);
}
/*
==================
drawsurface
==================
*/
void drawsurface (surface_t *surf)
{
face_t *f;
for (f=surf->faces ; f ; f=f->next)
draw_drawface (f);
}
/*
==================
drawsurfacelist
==================
*/
void drawsurfacelist (surface_t *surf)
{
draw_clearwindow ();
while (surf)
{
drawsurface (surf);
surf = surf->next;
}
}
/*
==================
solidbsp
==================
*/
node_t *solidbsp (surface_t *surfhead, qboolean midsplit)
{
int i;
node_t *headnode;
qprintf ("----- solidbsp -----\n");
headnode = allocnode ();
usemidsplit = midsplit;
//
// calculate a bounding box for the entire model
//
for (i=0 ; i<3 ; i++)
{
headnode->mins[i] = brushset->mins[i] - sidespace;
headnode->maxs[i] = brushset->maxs[i] + sidespace;
}
//
// recursively partition everything
//
draw_clearwindow ();
splitnodes = 0;
leaffaces = 0;
nodefaces = 0;
c_solid = c_empty = c_water = 0;
partitionsurfaces (surfhead, headnode);
qprintf ("%5i split nodes\n", splitnodes);
qprintf ("%5i solid leafs\n", c_solid);
qprintf ("%5i empty leafs\n", c_empty);
qprintf ("%5i water leafs\n", c_water);
qprintf ("%5i leaffaces\n",leaffaces);
qprintf ("%5i nodefaces\n", nodefaces);
return headnode;
}