quakeforge/tools/qfbsp/source/csg4.c

420 lines
8.9 KiB
C
Raw Normal View History

2002-09-19 18:51:19 +00:00
/*
Copyright (C) 1996-1997 Id Software, Inc.
2002-09-19 18:51:19 +00:00
This program 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.
2002-09-19 18:51:19 +00:00
This program 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.
2002-09-19 18:51:19 +00:00
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2002-09-19 18:51:19 +00:00
See file, 'COPYING', for details.
*/
2002-09-23 16:27:17 +00:00
static const char rcsid[] =
"$Id$";
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include "string.h"
#endif
#include "QF/sys.h"
#include "bsp5.h"
/*
2002-09-23 16:27:17 +00:00
NOTES
2002-09-23 16:27:17 +00:00
Brushes that touch still need to be split at the cut point to make a
tjunction
*/
2002-09-19 17:14:23 +00:00
face_t *validfaces[MAX_MAP_PLANES];
face_t *inside, *outside;
int brushfaces;
int csgfaces;
int csgmergefaces;
2002-09-19 18:51:19 +00:00
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
DrawList (face_t *list)
{
2002-09-19 17:14:23 +00:00
for (; list; list = list->next)
Draw_DrawFace (list);
}
/*
2002-09-23 16:27:17 +00:00
NewFaceFromFace
2002-09-23 16:27:17 +00:00
Duplicates the non point information of a face, used by SplitFace and
MergeFace.
*/
2002-09-19 17:14:23 +00:00
face_t *
2002-09-19 18:51:19 +00:00
NewFaceFromFace (face_t *in)
{
2002-09-19 17:14:23 +00:00
face_t *newf;
newf = AllocFace ();
newf->planenum = in->planenum;
2002-09-19 17:14:23 +00:00
newf->texturenum = in->texturenum;
newf->planeside = in->planeside;
newf->original = in->original;
newf->contents[0] = in->contents[0];
newf->contents[1] = in->contents[1];
2002-09-19 17:14:23 +00:00
return newf;
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
SplitFace (face_t *in, plane_t *split, face_t **front, face_t **back)
{
2002-09-19 18:51:19 +00:00
face_t *newf, *new2;
int i, j;
2002-09-19 17:14:23 +00:00
int sides[MAXEDGES + 1];
int counts[3];
vec_t dot;
2002-09-19 18:51:19 +00:00
vec_t dists[MAXEDGES + 1];
2002-09-19 17:14:23 +00:00
vec_t *p1, *p2;
vec3_t mid;
if (in->numpoints < 0)
Sys_Error ("SplitFace: freed face");
counts[0] = counts[1] = counts[2] = 0;
2002-09-23 16:27:17 +00:00
// determine sides for each point
2002-09-19 17:14:23 +00:00
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];
2002-09-19 17:14:23 +00:00
if (!counts[0]) {
*front = NULL;
*back = in;
return;
}
2002-09-19 17:14:23 +00:00
if (!counts[1]) {
*front = in;
*back = NULL;
return;
}
2002-09-19 17:14:23 +00:00
*back = newf = NewFaceFromFace (in);
*front = new2 = NewFaceFromFace (in);
2002-09-19 17:14:23 +00:00
2002-09-23 16:27:17 +00:00
// distribute the points and generate splits
2002-09-19 17:14:23 +00:00
for (i = 0; i < in->numpoints; i++) {
if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
Sys_Error ("SplitFace: numpoints > MAXEDGES");
p1 = in->pts[i];
2002-09-19 17:14:23 +00:00
if (sides[i] == SIDE_ON) {
VectorCopy (p1, newf->pts[newf->numpoints]);
newf->numpoints++;
VectorCopy (p1, new2->pts[new2->numpoints]);
new2->numpoints++;
continue;
}
2002-09-19 17:14:23 +00:00
if (sides[i] == SIDE_FRONT) {
VectorCopy (p1, new2->pts[new2->numpoints]);
new2->numpoints++;
2002-09-19 17:14:23 +00:00
} else {
VectorCopy (p1, newf->pts[newf->numpoints]);
newf->numpoints++;
}
2002-09-19 17:14:23 +00:00
if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
continue;
2002-09-19 17:14:23 +00:00
// generate a split point
p2 = in->pts[(i + 1) % in->numpoints];
dot = dists[i] / (dists[i] - dists[i + 1]);
2002-09-19 18:51:19 +00:00
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
2002-09-19 17:14:23 +00:00
mid[j] = p1[j] + dot * (p2[j] - p1[j]);
}
2002-09-19 17:14:23 +00:00
VectorCopy (mid, newf->pts[newf->numpoints]);
newf->numpoints++;
VectorCopy (mid, new2->pts[new2->numpoints]);
new2->numpoints++;
}
if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
Sys_Error ("SplitFace: numpoints > MAXEDGES");
2002-09-23 16:27:17 +00:00
// free the original face now that is is represented by the fragments
FreeFace (in);
}
/*
2002-09-23 16:27:17 +00:00
ClipInside
2002-09-23 16:27:17 +00:00
Clips all of the faces in the inside list, possibly moving them to the
outside list or spliting it into a piece in each list.
2002-09-23 16:27:17 +00:00
Faces exactly on the plane will stay inside unless overdrawn by later brush
2002-09-23 16:27:17 +00:00
frontside is the side of the plane that holds the outside list
*/
2002-09-19 17:14:23 +00:00
void
ClipInside (int splitplane, int frontside, qboolean precedence)
{
2002-09-19 18:51:19 +00:00
face_t *insidelist, *next, *f;
2002-09-19 17:14:23 +00:00
face_t *frags[2];
plane_t *split;
split = &planes[splitplane];
2002-09-19 17:14:23 +00:00
insidelist = NULL;
2002-09-19 17:14:23 +00:00
for (f = inside; f; f = next) {
next = f->next;
2002-09-19 17:14:23 +00:00
if (f->planenum == splitplane) { // exactly on, handle special
2002-09-19 18:51:19 +00:00
if (frontside != f->planeside || precedence) { // always clip off
// opposite faceing
frags[frontside] = NULL;
frags[!frontside] = f;
2002-09-19 17:14:23 +00:00
} else { // leave it on the outside
frags[frontside] = f;
frags[!frontside] = NULL;
}
2002-09-19 17:14:23 +00:00
} else { // proper split
SplitFace (f, split, &frags[0], &frags[1]);
}
2002-09-19 17:14:23 +00:00
if (frags[frontside]) {
frags[frontside]->next = outside;
outside = frags[frontside];
}
2002-09-19 17:14:23 +00:00
if (frags[!frontside]) {
frags[!frontside]->next = insidelist;
insidelist = frags[!frontside];
}
}
2002-09-19 17:14:23 +00:00
inside = insidelist;
}
/*
2002-09-23 16:27:17 +00:00
SaveOutside
2002-09-23 16:27:17 +00:00
Saves all of the faces in the outside list to the bsp plane list
*/
2002-09-19 17:14:23 +00:00
void
SaveOutside (qboolean mirror)
{
2002-09-19 17:14:23 +00:00
face_t *f, *next, *newf;
2002-09-19 18:51:19 +00:00
int planenum, i;
2002-09-19 17:14:23 +00:00
for (f = outside; f; f = next) {
next = f->next;
csgfaces++;
Draw_DrawFace (f);
planenum = f->planenum;
2002-09-19 17:14:23 +00:00
if (mirror) {
newf = NewFaceFromFace (f);
2002-09-19 17:14:23 +00:00
newf->numpoints = f->numpoints;
newf->planeside = f->planeside ^ 1; // reverse side
newf->contents[0] = f->contents[1];
newf->contents[1] = f->contents[0];
2002-09-19 17:14:23 +00:00
for (i = 0; i < f->numpoints; i++) // add points backwards
{
2002-09-19 17:14:23 +00:00
VectorCopy (f->pts[f->numpoints - 1 - i], newf->pts[i]);
}
2002-09-19 17:14:23 +00:00
} else
newf = NULL;
2002-09-19 17:14:23 +00:00
validfaces[planenum] = MergeFaceToList (f, validfaces[planenum]);
if (newf)
2002-09-19 18:51:19 +00:00
validfaces[planenum] = MergeFaceToList (newf,
validfaces[planenum]);
validfaces[planenum] = FreeMergeListScraps (validfaces[planenum]);
}
}
/*
2002-09-23 16:27:17 +00:00
FreeInside
2002-09-23 16:27:17 +00:00
Free all the faces that got clipped out
*/
2002-09-19 17:14:23 +00:00
void
FreeInside (int contents)
{
2002-09-19 18:51:19 +00:00
face_t *next, *f;
2002-09-19 17:14:23 +00:00
for (f = inside; f; f = next) {
next = f->next;
2002-09-19 17:14:23 +00:00
if (contents != CONTENTS_SOLID) {
f->contents[0] = contents;
f->next = outside;
outside = f;
2002-09-19 17:14:23 +00:00
} else
FreeFace (f);
}
}
/*
2002-09-23 16:27:17 +00:00
BuildSurfaces
2002-09-23 16:27:17 +00:00
Returns a chain of all the external surfaces with one or more visible
faces.
*/
2002-09-19 17:14:23 +00:00
surface_t *
BuildSurfaces (void)
{
2002-09-19 17:14:23 +00:00
face_t *count;
2002-09-19 18:51:19 +00:00
face_t **f;
2002-09-19 17:14:23 +00:00
int i;
2002-09-19 18:51:19 +00:00
surface_t *surfhead, *s;
2002-09-19 17:14:23 +00:00
surfhead = NULL;
2002-09-19 17:14:23 +00:00
f = validfaces;
2002-09-19 17:14:23 +00:00
for (i = 0; i < numbrushplanes; i++, f++) {
if (!*f)
2002-09-19 17:14:23 +00:00
continue; // nothing left on this plane
2002-09-23 16:27:17 +00:00
// create a new surface to hold the faces on this plane
s = AllocSurface ();
s->planenum = i;
s->next = surfhead;
surfhead = s;
s->faces = *f;
2002-09-19 17:14:23 +00:00
for (count = s->faces; count; count = count->next)
csgmergefaces++;
2002-09-19 17:14:23 +00:00
CalcSurfaceInfo (s); // bounding box and flags
}
return surfhead;
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
CopyFacesToOutside (brush_t *b)
{
2002-09-19 18:51:19 +00:00
face_t *newf, *f;
2002-09-19 17:14:23 +00:00
outside = NULL;
2002-09-19 17:14:23 +00:00
for (f = b->faces; f; f = f->next) {
brushfaces++;
newf = AllocFace ();
*newf = *f;
newf->next = outside;
newf->contents[0] = CONTENTS_EMPTY;
newf->contents[1] = b->contents;
outside = newf;
}
}
/*
2002-09-23 16:27:17 +00:00
CSGFaces
2002-09-23 16:27:17 +00:00
Returns a list of surfaces containing aall of the faces
*/
2002-09-19 17:14:23 +00:00
surface_t *
2002-09-19 18:51:19 +00:00
CSGFaces (brushset_t *bs)
{
2002-09-19 17:14:23 +00:00
brush_t *b1, *b2;
2002-09-19 18:51:19 +00:00
face_t *f;
2002-09-19 17:14:23 +00:00
int i;
qboolean overwrite;
surface_t *surfhead;
qprintf ("---- CSGFaces ----\n");
2002-09-19 17:14:23 +00:00
memset (validfaces, 0, sizeof (validfaces));
csgfaces = brushfaces = csgmergefaces = 0;
2002-09-19 17:14:23 +00:00
Draw_ClearWindow ();
2002-09-19 17:14:23 +00:00
2002-09-23 16:27:17 +00:00
// do the solid faces
2002-09-19 17:14:23 +00:00
for (b1 = bs->brushes; b1; b1 = b1->next) {
// set outside to a copy of the brush's faces
CopyFacesToOutside (b1);
2002-09-19 17:14:23 +00:00
overwrite = false;
2002-09-19 17:14:23 +00:00
for (b2 = bs->brushes; b2; b2 = b2->next) {
// see if b2 needs to clip a chunk out of b1
if (b1 == b2) {
overwrite = true; // later brushes now overwrite
continue;
}
2002-09-19 17:14:23 +00:00
// check bounding box first
for (i = 0; i < 3; i++)
if (b1->mins[i] > b2->maxs[i] || b1->maxs[i] < b2->mins[i])
break;
2002-09-19 17:14:23 +00:00
if (i < 3)
continue;
2002-09-19 17:14:23 +00:00
// divide faces by the planes of the new brush
inside = outside;
outside = NULL;
2002-09-19 17:14:23 +00:00
for (f = b2->faces; f; f = f->next)
ClipInside (f->planenum, f->planeside, overwrite);
2002-09-19 17:14:23 +00:00
// these faces are continued in another brush, so get rid of them
if (b1->contents == CONTENTS_SOLID
&& b2->contents <= CONTENTS_WATER)
FreeInside (b2->contents);
else
FreeInside (CONTENTS_SOLID);
}
2002-09-19 17:14:23 +00:00
// all of the faces left in outside are real surface faces
if (b1->contents != CONTENTS_SOLID)
2002-09-19 17:14:23 +00:00
SaveOutside (true); // mirror faces for inside view
else
SaveOutside (false);
}
#if 0
if (!csgfaces)
Sys_Error ("No faces");
#endif
surfhead = BuildSurfaces ();
2002-09-19 17:14:23 +00:00
qprintf ("%5i brushfaces\n", brushfaces);
qprintf ("%5i csgfaces\n", csgfaces);
qprintf ("%5i mergedfaces\n", csgmergefaces);
2002-09-19 17:14:23 +00:00
return surfhead;
}