1019 lines
19 KiB
C++
1019 lines
19 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
|
*
|
|
* 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 g_bspfilename[1024];
|
|
char pointfilename[1024];
|
|
char portfilename[1024];
|
|
|
|
FILE* polyfiles[NUM_HULLS];
|
|
|
|
int hullnum;
|
|
|
|
//===========================================================================
|
|
|
|
|
|
/*
|
|
=================
|
|
BaseWindingForPlane
|
|
=================
|
|
*/
|
|
winding_t* BaseWindingForPlane(dplane_t* p)
|
|
{
|
|
vec3_t org, vright, vup;
|
|
winding_t* w;
|
|
vec3_t temp;
|
|
|
|
#if 0
|
|
// find the major axis
|
|
vec_t v;
|
|
|
|
vec_t max = -BOGUS_RANGE;
|
|
int x = -1;
|
|
for (int 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 = reinterpret_cast<winding_t*>(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;
|
|
|
|
sides[0] = SIDE_FRONT;
|
|
dists[0] = 0;
|
|
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;
|
|
|
|
sides[0] = SIDE_FRONT;
|
|
dists[0] = 0;
|
|
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");
|
|
|
|
sides[0] = SIDE_FRONT;
|
|
dists[0] = 0;
|
|
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 = reinterpret_cast<winding_t*>(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 = reinterpret_cast<face_t*>(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 = reinterpret_cast<surface_t*>(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 = reinterpret_cast<portal_t*>(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 = reinterpret_cast<node_t*>(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 = reinterpret_cast<surfchain_t*>(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 r;
|
|
int planenum, texturenum, 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, &texturenum, &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 (texturenum > numtexinfo)
|
|
Error("ReadSurfs: %i > numtexinfo", texturenum);
|
|
|
|
|
|
f = AllocFace();
|
|
f->planenum = planenum;
|
|
f->texturenum = texturenum;
|
|
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)
|
|
{
|
|
surfchain_t* surfs;
|
|
node_t* nodes;
|
|
dmodel_t* model;
|
|
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();
|
|
|
|
// 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();
|
|
strcpy(g_bspfilename, ExpandArg(argv[i]));
|
|
StripExtension(g_bspfilename);
|
|
|
|
//
|
|
// do it!
|
|
//
|
|
start = I_FloatTime();
|
|
|
|
ProcessFile(g_bspfilename);
|
|
|
|
end = I_FloatTime();
|
|
printf("%5.0f seconds elapsed\n", end - start);
|
|
|
|
return 0;
|
|
}
|