1999-04-07 00:00:00 +00:00
|
|
|
/***
|
|
|
|
*
|
2002-12-23 00:00:00 +00:00
|
|
|
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
1999-04-07 00:00:00 +00:00
|
|
|
*
|
|
|
|
* This product contains software technology licensed from Id
|
|
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
|
|
* All Rights Reserved.
|
|
|
|
*
|
|
|
|
****/
|
|
|
|
|
|
|
|
// qbsp.c
|
|
|
|
|
|
|
|
#include "bsp5.h"
|
|
|
|
|
|
|
|
//
|
|
|
|
// command line flags
|
|
|
|
//
|
|
|
|
qboolean drawflag;
|
|
|
|
qboolean nofill;
|
|
|
|
qboolean notjunc;
|
|
|
|
qboolean allverbose;
|
|
|
|
qboolean nogfx;
|
|
|
|
qboolean noclip;
|
|
|
|
qboolean leakonly;
|
|
|
|
qboolean watervis;
|
|
|
|
|
|
|
|
int subdivide_size = 240;
|
|
|
|
|
|
|
|
char bspfilename[1024];
|
|
|
|
char pointfilename[1024];
|
|
|
|
char portfilename[1024];
|
|
|
|
|
|
|
|
FILE *polyfiles[NUM_HULLS];
|
|
|
|
|
|
|
|
int hullnum;
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
BaseWindingForPlane
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
winding_t *BaseWindingForPlane (dplane_t *p)
|
|
|
|
{
|
|
|
|
int i, x;
|
|
|
|
vec_t max, v;
|
|
|
|
vec3_t org, vright, vup;
|
|
|
|
winding_t *w;
|
|
|
|
vec3_t temp;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// find the major axis
|
|
|
|
|
|
|
|
max = -BOGUS_RANGE;
|
|
|
|
x = -1;
|
|
|
|
for (i=0 ; i<3; i++)
|
|
|
|
{
|
|
|
|
v = fabs(p->normal[i]);
|
|
|
|
if (v > max)
|
|
|
|
{
|
|
|
|
x = i;
|
|
|
|
max = v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (x==-1)
|
|
|
|
Error ("BaseWindingForPlane: no axis found");
|
|
|
|
|
|
|
|
VectorCopy (vec3_origin, vup);
|
|
|
|
switch (x)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
vup[2] = 1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
vup[0] = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
v = DotProduct (vup, p->normal);
|
|
|
|
VectorMA (vup, -v, p->normal, vup);
|
|
|
|
VectorNormalize (vup);
|
|
|
|
|
|
|
|
VectorScale (p->normal, p->dist, org);
|
|
|
|
|
|
|
|
CrossProduct (vup, p->normal, vright);
|
|
|
|
|
|
|
|
VectorScale (vup, 8192, vup);
|
|
|
|
VectorScale (vright, 8192, vright);
|
|
|
|
#else
|
|
|
|
VectorScale (p->normal, p->dist, org);
|
|
|
|
|
|
|
|
VectorCopy (vec3_origin, vup);
|
|
|
|
VectorCopy (vec3_origin, vright);
|
|
|
|
if (!p->normal[1] && !p->normal[2])
|
|
|
|
{
|
|
|
|
vup[2] = 8192;
|
|
|
|
vright[1] = 8192*p->normal[0];
|
|
|
|
}
|
|
|
|
else if (!p->normal[0] && !p->normal[2])
|
|
|
|
{
|
|
|
|
vup[2] = 8192;
|
|
|
|
vright[0] = -8192*p->normal[1];
|
|
|
|
}
|
|
|
|
else if (!p->normal[0] && !p->normal[1])
|
|
|
|
{
|
|
|
|
vup[1] = 8192;
|
|
|
|
vright[0] = 8192*p->normal[2];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vup[0] = -2*p->normal[1]*p->normal[2];
|
|
|
|
vup[1] = p->normal[0]*p->normal[2];
|
|
|
|
vup[2] = p->normal[0]*p->normal[1];
|
|
|
|
|
|
|
|
VectorNormalize (vup);
|
|
|
|
|
|
|
|
VectorCopy (p->normal, temp); // to vec3_t
|
|
|
|
CrossProduct (vup, temp, vright);
|
|
|
|
|
|
|
|
VectorScale (vup, 8192, vup);
|
|
|
|
VectorScale (vright, 8192, vright);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// project a really big axis aligned box onto the plane
|
|
|
|
w = NewWinding (4);
|
|
|
|
|
|
|
|
VectorSubtract (org, vright, w->points[0]);
|
|
|
|
VectorAdd (w->points[0], vup, w->points[0]);
|
|
|
|
|
|
|
|
VectorAdd (org, vright, w->points[1]);
|
|
|
|
VectorAdd (w->points[1], vup, w->points[1]);
|
|
|
|
|
|
|
|
VectorAdd (org, vright, w->points[2]);
|
|
|
|
VectorSubtract (w->points[2], vup, w->points[2]);
|
|
|
|
|
|
|
|
VectorSubtract (org, vright, w->points[3]);
|
|
|
|
VectorSubtract (w->points[3], vup, w->points[3]);
|
|
|
|
|
|
|
|
w->numpoints = 4;
|
|
|
|
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
CopyWinding
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
winding_t *CopyWinding (winding_t *w)
|
|
|
|
{
|
|
|
|
int size;
|
|
|
|
winding_t *c;
|
|
|
|
|
|
|
|
size = (int)((winding_t *)0)->points[w->numpoints];
|
|
|
|
c = malloc (size);
|
|
|
|
memcpy (c, w, size);
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
ClipWinding
|
|
|
|
|
|
|
|
Clips the winding to the plane, returning the new winding on the positive side
|
|
|
|
Frees the input winding.
|
|
|
|
If keepon is true, an exactly on-plane winding will be saved, otherwise
|
|
|
|
it will be clipped away.
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
winding_t *ClipWinding (winding_t *in, dplane_t *split, qboolean keepon)
|
|
|
|
{
|
|
|
|
vec_t dists[MAX_POINTS_ON_WINDING];
|
|
|
|
int sides[MAX_POINTS_ON_WINDING];
|
|
|
|
int counts[3];
|
|
|
|
vec_t dot;
|
|
|
|
int i, j;
|
|
|
|
vec_t *p1, *p2;
|
|
|
|
vec3_t mid;
|
|
|
|
winding_t *neww;
|
|
|
|
int maxpts;
|
|
|
|
|
|
|
|
counts[0] = counts[1] = counts[2] = 0;
|
|
|
|
|
|
|
|
// determine sides for each point
|
|
|
|
// do this exactly, with no epsilon so tiny portals still work
|
|
|
|
for (i=0 ; i<in->numpoints ; i++)
|
|
|
|
{
|
|
|
|
dot = DotProduct (in->points[i], split->normal);
|
|
|
|
dot -= split->dist;
|
|
|
|
dists[i] = dot;
|
|
|
|
if (dot > 0)
|
|
|
|
sides[i] = SIDE_FRONT;
|
|
|
|
else if (dot < 0)
|
|
|
|
sides[i] = SIDE_BACK;
|
|
|
|
else
|
|
|
|
sides[i] = SIDE_ON;
|
|
|
|
counts[sides[i]]++;
|
|
|
|
}
|
|
|
|
sides[i] = sides[0];
|
|
|
|
dists[i] = dists[0];
|
|
|
|
|
|
|
|
if (keepon && !counts[0] && !counts[1])
|
|
|
|
return in;
|
|
|
|
|
|
|
|
if (!counts[0])
|
|
|
|
{
|
|
|
|
FreeWinding (in);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!counts[1])
|
|
|
|
return in;
|
|
|
|
|
|
|
|
maxpts = in->numpoints+4; // can't use counts[0]+2 because
|
|
|
|
// of fp grouping errors
|
|
|
|
neww = NewWinding (maxpts);
|
|
|
|
|
|
|
|
for (i=0 ; i<in->numpoints ; i++)
|
|
|
|
{
|
|
|
|
p1 = in->points[i];
|
|
|
|
|
|
|
|
if (sides[i] == SIDE_ON)
|
|
|
|
{
|
|
|
|
VectorCopy (p1, neww->points[neww->numpoints]);
|
|
|
|
neww->numpoints++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sides[i] == SIDE_FRONT)
|
|
|
|
{
|
|
|
|
VectorCopy (p1, neww->points[neww->numpoints]);
|
|
|
|
neww->numpoints++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// generate a split point
|
|
|
|
p2 = in->points[(i+1)%in->numpoints];
|
|
|
|
|
|
|
|
dot = dists[i] / (dists[i]-dists[i+1]);
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
|
|
{ // avoid round off error when possible
|
|
|
|
if (split->normal[j] == 1)
|
|
|
|
mid[j] = split->dist;
|
|
|
|
else if (split->normal[j] == -1)
|
|
|
|
mid[j] = -split->dist;
|
|
|
|
else
|
|
|
|
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorCopy (mid, neww->points[neww->numpoints]);
|
|
|
|
neww->numpoints++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (neww->numpoints > maxpts)
|
|
|
|
Error ("ClipWinding: points exceeded estimate");
|
|
|
|
|
|
|
|
// free the original winding
|
|
|
|
FreeWinding (in);
|
|
|
|
|
|
|
|
return neww;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
DivideWinding
|
|
|
|
|
|
|
|
Divides a winding by a plane, producing one or two windings. The
|
|
|
|
original winding is not damaged or freed. If only on one side, the
|
|
|
|
returned winding will be the input winding. If on both sides, two
|
|
|
|
new windings will be created.
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
|
|
|
|
//#define DIVIDE_EPSILON 0.5
|
|
|
|
#define DIVIDE_EPSILON ON_EPSILON
|
|
|
|
|
|
|
|
|
|
|
|
void DivideWinding (winding_t *in, dplane_t *split, winding_t **front, winding_t **back)
|
|
|
|
{
|
|
|
|
vec_t dists[MAX_POINTS_ON_WINDING];
|
|
|
|
int sides[MAX_POINTS_ON_WINDING];
|
|
|
|
int counts[3];
|
|
|
|
vec_t dot;
|
|
|
|
int i, j;
|
|
|
|
vec_t *p1, *p2;
|
|
|
|
vec3_t mid;
|
|
|
|
winding_t *f, *b;
|
|
|
|
int maxpts;
|
|
|
|
|
|
|
|
counts[0] = counts[1] = counts[2] = 0;
|
|
|
|
|
|
|
|
// determine sides for each point
|
|
|
|
for (i=0 ; i<in->numpoints ; i++)
|
|
|
|
{
|
|
|
|
dot = DotProduct (in->points[i], split->normal);
|
|
|
|
dot -= split->dist;
|
|
|
|
dists[i] = dot;
|
|
|
|
if (dot > DIVIDE_EPSILON)
|
|
|
|
sides[i] = SIDE_FRONT;
|
|
|
|
else if (dot < -DIVIDE_EPSILON)
|
|
|
|
sides[i] = SIDE_BACK;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sides[i] = SIDE_ON;
|
|
|
|
}
|
|
|
|
counts[sides[i]]++;
|
|
|
|
}
|
|
|
|
sides[i] = sides[0];
|
|
|
|
dists[i] = dists[0];
|
|
|
|
|
|
|
|
*front = *back = NULL;
|
|
|
|
|
|
|
|
if (!counts[0])
|
|
|
|
{
|
|
|
|
*back = in;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!counts[1])
|
|
|
|
{
|
|
|
|
*front = in;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
maxpts = in->numpoints+4; // can't use counts[0]+2 because
|
|
|
|
// of fp grouping errors
|
|
|
|
|
|
|
|
*front = f = NewWinding (maxpts);
|
|
|
|
*back = b = NewWinding (maxpts);
|
|
|
|
|
|
|
|
for (i=0 ; i<in->numpoints ; i++)
|
|
|
|
{
|
|
|
|
p1 = in->points[i];
|
|
|
|
|
|
|
|
if (sides[i] == SIDE_ON)
|
|
|
|
{
|
|
|
|
VectorCopy (p1, f->points[f->numpoints]);
|
|
|
|
f->numpoints++;
|
|
|
|
VectorCopy (p1, b->points[b->numpoints]);
|
|
|
|
b->numpoints++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sides[i] == SIDE_FRONT)
|
|
|
|
{
|
|
|
|
VectorCopy (p1, f->points[f->numpoints]);
|
|
|
|
f->numpoints++;
|
|
|
|
}
|
|
|
|
if (sides[i] == SIDE_BACK)
|
|
|
|
{
|
|
|
|
VectorCopy (p1, b->points[b->numpoints]);
|
|
|
|
b->numpoints++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// generate a split point
|
|
|
|
p2 = in->points[(i+1)%in->numpoints];
|
|
|
|
|
|
|
|
dot = dists[i] / (dists[i]-dists[i+1]);
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
|
|
{ // avoid round off error when possible
|
|
|
|
if (split->normal[j] == 1)
|
|
|
|
mid[j] = split->dist;
|
|
|
|
else if (split->normal[j] == -1)
|
|
|
|
mid[j] = -split->dist;
|
|
|
|
else
|
|
|
|
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorCopy (mid, f->points[f->numpoints]);
|
|
|
|
f->numpoints++;
|
|
|
|
VectorCopy (mid, b->points[b->numpoints]);
|
|
|
|
b->numpoints++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (f->numpoints > maxpts || b->numpoints > maxpts)
|
|
|
|
Error ("ClipWinding: points exceeded estimate");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
NewFaceFromFace
|
|
|
|
|
|
|
|
Duplicates the non point information of a face, used by SplitFace and
|
|
|
|
MergeFace.
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
face_t *NewFaceFromFace (face_t *in)
|
|
|
|
{
|
|
|
|
face_t *newf;
|
|
|
|
|
|
|
|
newf = AllocFace ();
|
|
|
|
|
|
|
|
newf->planenum = in->planenum;
|
|
|
|
newf->texturenum = in->texturenum;
|
|
|
|
newf->original = in->original;
|
|
|
|
newf->contents = in->contents;
|
|
|
|
|
|
|
|
return newf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SplitFaceTmp( face_t *in, dplane_t *split, face_t **front, face_t **back )
|
|
|
|
{
|
|
|
|
vec_t dists[MAXEDGES+1];
|
|
|
|
int sides[MAXEDGES+1];
|
|
|
|
int counts[3];
|
|
|
|
vec_t dot;
|
|
|
|
int i, j;
|
|
|
|
face_t *newf, *new2;
|
|
|
|
vec_t *p1, *p2;
|
|
|
|
vec3_t mid;
|
|
|
|
|
|
|
|
if (in->numpoints < 0)
|
|
|
|
Error ("SplitFace: freed face");
|
|
|
|
counts[0] = counts[1] = counts[2] = 0;
|
|
|
|
|
|
|
|
// determine sides for each point
|
|
|
|
for (i=0 ; i<in->numpoints ; i++)
|
|
|
|
{
|
|
|
|
dot = DotProduct (in->pts[i], split->normal);
|
|
|
|
dot -= split->dist;
|
|
|
|
dists[i] = dot;
|
|
|
|
if (dot > ON_EPSILON)
|
|
|
|
sides[i] = SIDE_FRONT;
|
|
|
|
else if (dot < -ON_EPSILON)
|
|
|
|
sides[i] = SIDE_BACK;
|
|
|
|
else
|
|
|
|
sides[i] = SIDE_ON;
|
|
|
|
counts[sides[i]]++;
|
|
|
|
}
|
|
|
|
sides[i] = sides[0];
|
|
|
|
dists[i] = dists[0];
|
|
|
|
|
|
|
|
if (!counts[0])
|
|
|
|
{
|
|
|
|
*front = NULL;
|
|
|
|
*back = in;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!counts[1])
|
|
|
|
{
|
|
|
|
*front = in;
|
|
|
|
*back = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
*back = newf = NewFaceFromFace (in);
|
|
|
|
*front = new2 = NewFaceFromFace (in);
|
|
|
|
|
|
|
|
// distribute the points and generate splits
|
|
|
|
|
|
|
|
for (i=0 ; i<in->numpoints ; i++)
|
|
|
|
{
|
|
|
|
if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
|
|
|
|
Error ("SplitFace: numpoints > MAXEDGES");
|
|
|
|
|
|
|
|
p1 = in->pts[i];
|
|
|
|
|
|
|
|
if (sides[i] == SIDE_ON)
|
|
|
|
{
|
|
|
|
VectorCopy (p1, newf->pts[newf->numpoints]);
|
|
|
|
newf->numpoints++;
|
|
|
|
VectorCopy (p1, new2->pts[new2->numpoints]);
|
|
|
|
new2->numpoints++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sides[i] == SIDE_FRONT)
|
|
|
|
{
|
|
|
|
VectorCopy (p1, new2->pts[new2->numpoints]);
|
|
|
|
new2->numpoints++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VectorCopy (p1, newf->pts[newf->numpoints]);
|
|
|
|
newf->numpoints++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// generate a split point
|
|
|
|
p2 = in->pts[(i+1)%in->numpoints];
|
|
|
|
|
|
|
|
dot = dists[i] / (dists[i]-dists[i+1]);
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
|
|
{ // avoid round off error when possible
|
|
|
|
if (split->normal[j] == 1)
|
|
|
|
mid[j] = split->dist;
|
|
|
|
else if (split->normal[j] == -1)
|
|
|
|
mid[j] = -split->dist;
|
|
|
|
else
|
|
|
|
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorCopy (mid, newf->pts[newf->numpoints]);
|
|
|
|
newf->numpoints++;
|
|
|
|
VectorCopy (mid, new2->pts[new2->numpoints]);
|
|
|
|
new2->numpoints++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
|
|
|
|
Error ("SplitFace: numpoints > MAXEDGES");
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
CheckFace (newf);
|
|
|
|
CheckFace (new2);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
SplitFace
|
|
|
|
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
void SplitFace (face_t *in, dplane_t *split, face_t **front, face_t **back)
|
|
|
|
{
|
|
|
|
SplitFaceTmp( in, split, front, back );
|
|
|
|
|
|
|
|
// free the original face now that is is represented by the fragments
|
|
|
|
if ( *front && *back )
|
|
|
|
FreeFace (in);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
int c_activefaces, c_peakfaces;
|
|
|
|
int c_activesurfaces, c_peaksurfaces;
|
|
|
|
int c_activewindings, c_peakwindings;
|
|
|
|
int c_activeportals, c_peakportals;
|
|
|
|
|
|
|
|
void PrintMemory (void)
|
|
|
|
{
|
|
|
|
printf ("faces : %6i (%6i)\n", c_activefaces, c_peakfaces);
|
|
|
|
printf ("surfaces: %6i (%6i)\n", c_activesurfaces, c_peaksurfaces);
|
|
|
|
printf ("windings: %6i (%6i)\n", c_activewindings, c_peakwindings);
|
|
|
|
printf ("portals : %6i (%6i)\n", c_activeportals, c_peakportals);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
NewWinding
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
winding_t *NewWinding (int points)
|
|
|
|
{
|
|
|
|
winding_t *w;
|
|
|
|
int size;
|
|
|
|
|
|
|
|
if (points > MAX_POINTS_ON_WINDING)
|
|
|
|
Error ("NewWinding: %i points", points);
|
|
|
|
|
|
|
|
c_activewindings++;
|
|
|
|
if (c_activewindings > c_peakwindings)
|
|
|
|
c_peakwindings = c_activewindings;
|
|
|
|
|
|
|
|
size = (int)((winding_t *)0)->points[points];
|
|
|
|
w = malloc (size);
|
|
|
|
memset (w, 0, size);
|
|
|
|
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FreeWinding (winding_t *w)
|
|
|
|
{
|
|
|
|
c_activewindings--;
|
|
|
|
free (w);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===========
|
|
|
|
AllocFace
|
|
|
|
===========
|
|
|
|
*/
|
|
|
|
face_t *AllocFace (void)
|
|
|
|
{
|
|
|
|
face_t *f;
|
|
|
|
|
|
|
|
c_activefaces++;
|
|
|
|
if (c_activefaces > c_peakfaces)
|
|
|
|
c_peakfaces = c_activefaces;
|
|
|
|
|
|
|
|
f = malloc (sizeof(face_t));
|
|
|
|
memset (f, 0, sizeof(face_t));
|
|
|
|
f->planenum = -1;
|
|
|
|
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FreeFace (face_t *f)
|
|
|
|
{
|
|
|
|
c_activefaces--;
|
|
|
|
free (f);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===========
|
|
|
|
AllocSurface
|
|
|
|
===========
|
|
|
|
*/
|
|
|
|
surface_t *AllocSurface (void)
|
|
|
|
{
|
|
|
|
surface_t *s;
|
|
|
|
|
|
|
|
s = malloc (sizeof(surface_t));
|
|
|
|
memset (s, 0, sizeof(surface_t));
|
|
|
|
|
|
|
|
c_activesurfaces++;
|
|
|
|
if (c_activesurfaces > c_peaksurfaces)
|
|
|
|
c_peaksurfaces = c_activesurfaces;
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FreeSurface (surface_t *s)
|
|
|
|
{
|
|
|
|
c_activesurfaces--;
|
|
|
|
free (s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===========
|
|
|
|
AllocPortal
|
|
|
|
===========
|
|
|
|
*/
|
|
|
|
portal_t *AllocPortal (void)
|
|
|
|
{
|
|
|
|
portal_t *p;
|
|
|
|
|
|
|
|
c_activeportals++;
|
|
|
|
if (c_activeportals > c_peakportals)
|
|
|
|
c_peakportals = c_activeportals;
|
|
|
|
|
|
|
|
p = malloc (sizeof(portal_t));
|
|
|
|
memset (p, 0, sizeof(portal_t));
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FreePortal (portal_t *p)
|
|
|
|
{
|
|
|
|
c_activeportals--;
|
|
|
|
free (p);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===========
|
|
|
|
AllocNode
|
|
|
|
===========
|
|
|
|
*/
|
|
|
|
node_t *AllocNode (void)
|
|
|
|
{
|
|
|
|
node_t *n;
|
|
|
|
|
|
|
|
n = malloc (sizeof(node_t));
|
|
|
|
memset (n, 0, sizeof(node_t));
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
face_t *validfaces[MAX_MAP_PLANES];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void AddFaceToBounds (face_t *f, vec3_t mins, vec3_t maxs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i=0 ; i<f->numpoints ; i++)
|
|
|
|
AddPointToBounds (f->pts[i], mins, maxs);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
SurflistFromValidFaces
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
surfchain_t *SurflistFromValidFaces (void)
|
|
|
|
{
|
|
|
|
surface_t *n;
|
|
|
|
int i;
|
|
|
|
face_t *f, *next;
|
|
|
|
surfchain_t *sc;
|
|
|
|
|
|
|
|
sc = malloc(sizeof(*sc));
|
|
|
|
ClearBounds (sc->mins, sc->maxs);
|
|
|
|
sc->surfaces = NULL;
|
|
|
|
|
|
|
|
// grab planes from both sides
|
|
|
|
for (i=0 ; i<numplanes ; i+=2)
|
|
|
|
{
|
|
|
|
if (!validfaces[i] && !validfaces[i+1])
|
|
|
|
continue;
|
|
|
|
n = AllocSurface ();
|
|
|
|
n->next = sc->surfaces;
|
|
|
|
sc->surfaces = n;
|
|
|
|
ClearBounds (n->mins, n->maxs);
|
|
|
|
n->planenum = i;
|
|
|
|
|
|
|
|
n->faces = NULL;
|
|
|
|
for (f = validfaces[i] ; f ; f=next)
|
|
|
|
{
|
|
|
|
next = f->next;
|
|
|
|
f->next = n->faces;
|
|
|
|
n->faces = f;
|
|
|
|
AddFaceToBounds (f, n->mins, n->maxs);
|
|
|
|
}
|
|
|
|
for (f = validfaces[i+1] ; f ; f=next)
|
|
|
|
{
|
|
|
|
next = f->next;
|
|
|
|
f->next = n->faces;
|
|
|
|
n->faces = f;
|
|
|
|
AddFaceToBounds (f, n->mins, n->maxs);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AddPointToBounds (n->mins, sc->mins, sc->maxs);
|
|
|
|
AddPointToBounds (n->maxs, sc->mins, sc->maxs);
|
|
|
|
|
|
|
|
validfaces[i] = NULL;
|
|
|
|
validfaces[i+1] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// merge all possible polygons
|
|
|
|
|
|
|
|
MergeAll (sc->surfaces);
|
|
|
|
|
|
|
|
return sc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
ReadSurfs
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
surfchain_t *ReadSurfs (FILE *file)
|
|
|
|
{
|
|
|
|
int nump;
|
|
|
|
int r;
|
|
|
|
int planenum, texinfo, contents, numpoints;
|
|
|
|
face_t *f;
|
|
|
|
int i;
|
|
|
|
double v[3];
|
|
|
|
|
|
|
|
// read in the polygons
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
r = fscanf (file, "%i %i %i %i\n", &planenum, &texinfo, &contents, &numpoints);
|
|
|
|
if (r == 0 || r == -1)
|
|
|
|
return NULL;
|
|
|
|
if (planenum == -1) // end of model
|
|
|
|
break;
|
|
|
|
if (r != 4)
|
|
|
|
Error ("ReadSurfs: scanf failure");
|
|
|
|
if (numpoints > MAXPOINTS)
|
|
|
|
Error ("ReadSurfs: %i > MAXPOINTS", numpoints);
|
|
|
|
if (planenum > numplanes)
|
|
|
|
Error ("ReadSurfs: %i > numplanes", planenum);
|
|
|
|
if (texinfo > numtexinfo)
|
|
|
|
Error ("ReadSurfs: %i > numtexinfo", texinfo);
|
|
|
|
|
|
|
|
|
|
|
|
f = AllocFace ();
|
|
|
|
f->planenum = planenum;
|
|
|
|
f->texturenum = texinfo;
|
|
|
|
f->contents = contents;
|
|
|
|
f->numpoints = numpoints;
|
|
|
|
f->next = validfaces[planenum];
|
|
|
|
validfaces[planenum] = f;
|
|
|
|
|
|
|
|
for (i=0 ; i<f->numpoints ; i++)
|
|
|
|
{
|
|
|
|
r = fscanf (file, "%lf %lf %lf\n", &v[0], &v[1], &v[2]);
|
|
|
|
VectorCopy (v, f->pts[i]);
|
|
|
|
}
|
|
|
|
fscanf (file, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return SurflistFromValidFaces ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
ProcessModel
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
qboolean ProcessModel (void)
|
|
|
|
{
|
|
|
|
char mod[80];
|
|
|
|
surfchain_t *surfs;
|
|
|
|
node_t *nodes;
|
|
|
|
dmodel_t *model;
|
|
|
|
int i;
|
|
|
|
int startleafs;
|
|
|
|
|
|
|
|
surfs = ReadSurfs (polyfiles[0]);
|
|
|
|
|
|
|
|
if (!surfs)
|
|
|
|
return false; // all models are done
|
|
|
|
|
|
|
|
VectorCopy (surfs->mins, draw_mins);
|
|
|
|
VectorCopy (surfs->maxs, draw_maxs);
|
|
|
|
|
|
|
|
if (nummodels >= MAX_MAP_MODELS)
|
|
|
|
Error ("nummodels == MAX_MAP_MODELS");
|
|
|
|
|
|
|
|
startleafs = numleafs;
|
|
|
|
model = &dmodels[nummodels];
|
|
|
|
nummodels++;
|
|
|
|
|
|
|
|
VectorCopy (surfs->mins, model->mins);
|
|
|
|
VectorCopy (surfs->maxs, model->maxs);
|
|
|
|
|
|
|
|
//
|
|
|
|
// SolidBSP generates a node tree
|
|
|
|
//
|
|
|
|
nodes = SolidBSP (surfs);
|
|
|
|
|
|
|
|
//
|
|
|
|
// build all the portals in the bsp tree
|
|
|
|
// some portals are solid polygons, and some are paths to other leafs
|
|
|
|
//
|
|
|
|
if (nummodels == 1 && !nofill) // assume non-world bmodels are simple
|
|
|
|
nodes = FillOutside (nodes, true); // make a leakfile if bad
|
|
|
|
|
|
|
|
FreePortals (nodes);
|
|
|
|
|
|
|
|
// fix tjunctions
|
|
|
|
tjunc (nodes);
|
|
|
|
|
|
|
|
MakeFaceEdges (nodes);
|
|
|
|
|
|
|
|
// emit the faces for the bsp file
|
|
|
|
model->headnode[0] = numnodes;
|
|
|
|
model->firstface = numfaces;
|
|
|
|
WriteDrawNodes (nodes);
|
|
|
|
model->numfaces = numfaces - model->firstface;;
|
|
|
|
model->visleafs = numleafs - startleafs;
|
|
|
|
|
|
|
|
if (noclip)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
//
|
|
|
|
// the clipping hulls are simpler
|
|
|
|
//
|
|
|
|
for (hullnum = 1 ; hullnum < NUM_HULLS ; hullnum++)
|
|
|
|
{
|
|
|
|
surfs = ReadSurfs (polyfiles[hullnum]);
|
|
|
|
nodes = SolidBSP (surfs);
|
|
|
|
if (nummodels == 1 && !nofill) // assume non-world bmodels are simple
|
|
|
|
nodes = FillOutside (nodes, false);
|
|
|
|
FreePortals (nodes);
|
|
|
|
model->headnode[hullnum] = numclipnodes;
|
|
|
|
WriteClipNodes (nodes);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
ProcessFile
|
|
|
|
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
void ProcessFile (char *bspfilename)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char name[1024];
|
|
|
|
|
|
|
|
// create filenames
|
|
|
|
sprintf (portfilename, "%s.prt", bspfilename);
|
|
|
|
remove (portfilename);
|
|
|
|
|
|
|
|
sprintf (pointfilename, "%s.pts", bspfilename);
|
|
|
|
remove (pointfilename);
|
|
|
|
|
|
|
|
// open the polygon files
|
|
|
|
for (i=0 ; i<NUM_HULLS ; i++)
|
|
|
|
{
|
|
|
|
sprintf (name, "%s.p%i", bspfilename, i);
|
|
|
|
polyfiles[i] = fopen (name, "r");
|
|
|
|
if (!polyfiles[i])
|
|
|
|
Error ("Can't open %s", name);
|
|
|
|
}
|
|
|
|
|
|
|
|
// load the output of qcsg
|
|
|
|
strcat (bspfilename, ".bsp");
|
|
|
|
LoadBSPFile (bspfilename);
|
|
|
|
ParseEntities ();
|
|
|
|
|
|
|
|
// init the tables to be shared by all models
|
|
|
|
BeginBSPFile ();
|
|
|
|
|
|
|
|
// process each model individually
|
|
|
|
while (ProcessModel ())
|
|
|
|
;
|
|
|
|
|
|
|
|
// write the updated bsp file out
|
|
|
|
FinishBSPFile ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
main
|
|
|
|
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
extern char qproject[];
|
|
|
|
|
|
|
|
int main (int argc, char **argv)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
double start, end;
|
|
|
|
|
|
|
|
printf( "qbsp2.exe v2.2 (%s)\n", __DATE__ );
|
|
|
|
printf ("---- qbsp2 ----\n" );
|
|
|
|
|
|
|
|
//
|
|
|
|
// check command line flags
|
|
|
|
//
|
|
|
|
for (i=1 ; i<argc ; i++)
|
|
|
|
{
|
|
|
|
if (argv[i][0] != '-')
|
|
|
|
break;
|
|
|
|
if (!strcmp(argv[i],"-threads"))
|
|
|
|
{
|
|
|
|
numthreads = atoi (argv[i+1]);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
else if (!strcmp(argv[i], "-v"))
|
|
|
|
{
|
|
|
|
printf ("verbose = true\n");
|
|
|
|
verbose = true;
|
|
|
|
}
|
|
|
|
else if (!strcmp (argv[i],"-notjunc"))
|
|
|
|
notjunc = true;
|
|
|
|
else if (!strcmp (argv[i],"-draw"))
|
|
|
|
drawflag = true;
|
|
|
|
else if (!strcmp (argv[i],"-watervis"))
|
|
|
|
watervis = true;
|
|
|
|
else if (!strcmp (argv[i],"-noclip"))
|
|
|
|
noclip = true;
|
|
|
|
else if (!strcmp (argv[i],"-nofill"))
|
|
|
|
nofill = true;
|
|
|
|
else if (!strcmp (argv[i],"-verbose"))
|
|
|
|
allverbose = true;
|
|
|
|
else if (!strcmp (argv[i],"-leakonly"))
|
|
|
|
leakonly = true;
|
|
|
|
else if (!strcmp (argv[i],"-nogfx"))
|
|
|
|
nogfx = true;
|
|
|
|
else if( !strcmp( argv[ i ], "-proj" ) )
|
|
|
|
{
|
|
|
|
strcpy( qproject, argv[ i + 1 ] );
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
else if (!strcmp (argv[i],"-subdivide"))
|
|
|
|
{
|
|
|
|
subdivide_size = atoi(argv[i+1]);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Error ("qbsp: Unknown option '%s'", argv[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i != argc - 2 && i != argc - 1)
|
|
|
|
Error ("usage: qbsp [-draw] [-leakonly] [-noclip] [-nofill] [-nogfx] [-notjunc] [-proj name] [-subdivide size] [-threads n] [-v] [-watervis] sourcefile");
|
|
|
|
|
|
|
|
ThreadSetDefault ();
|
|
|
|
|
|
|
|
SetQdirFromPath (argv[i]);
|
|
|
|
strcpy (bspfilename, ExpandArg(argv[i]));
|
|
|
|
StripExtension (bspfilename);
|
|
|
|
|
|
|
|
//
|
|
|
|
// do it!
|
|
|
|
//
|
|
|
|
start = I_FloatTime ();
|
|
|
|
|
|
|
|
ProcessFile (bspfilename);
|
|
|
|
|
|
|
|
end = I_FloatTime ();
|
|
|
|
printf ("%5.0f seconds elapsed\n", end-start);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|