mirror of
https://github.com/id-Software/Quake-Tools.git
synced 2026-03-19 16:39:31 +01:00
The source release of the qutils.
This commit is contained in:
464
qutils/QBSP/CSG4.C
Normal file
464
qutils/QBSP/CSG4.C
Normal file
@@ -0,0 +1,464 @@
|
||||
// csg4.c
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
/*
|
||||
|
||||
NOTES
|
||||
-----
|
||||
Brushes that touch still need to be split at the cut point to make a tjunction
|
||||
|
||||
*/
|
||||
|
||||
face_t *validfaces[MAX_MAP_PLANES];
|
||||
|
||||
|
||||
face_t *inside, *outside;
|
||||
int brushfaces;
|
||||
int csgfaces;
|
||||
int csgmergefaces;
|
||||
|
||||
void DrawList (face_t *list)
|
||||
{
|
||||
for ( ; list ; list=list->next)
|
||||
Draw_DrawFace (list);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
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->planeside = in->planeside;
|
||||
newf->original = in->original;
|
||||
newf->contents[0] = in->contents[0];
|
||||
newf->contents[1] = in->contents[1];
|
||||
|
||||
return newf;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
SplitFace
|
||||
|
||||
==================
|
||||
*/
|
||||
void SplitFace (face_t *in, plane_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
|
||||
|
||||
// free the original face now that is is represented by the fragments
|
||||
FreeFace (in);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ClipInside
|
||||
|
||||
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.
|
||||
|
||||
Faces exactly on the plane will stay inside unless overdrawn by later brush
|
||||
|
||||
frontside is the side of the plane that holds the outside list
|
||||
=================
|
||||
*/
|
||||
void ClipInside (int splitplane, int frontside, qboolean precedence)
|
||||
{
|
||||
face_t *f, *next;
|
||||
face_t *frags[2];
|
||||
face_t *insidelist;
|
||||
plane_t *split;
|
||||
|
||||
split = &planes[splitplane];
|
||||
|
||||
insidelist = NULL;
|
||||
for (f=inside ; f ; f=next)
|
||||
{
|
||||
next = f->next;
|
||||
|
||||
if (f->planenum == splitplane)
|
||||
{ // exactly on, handle special
|
||||
if ( frontside != f->planeside || precedence )
|
||||
{ // allways clip off opposite faceing
|
||||
frags[frontside] = NULL;
|
||||
frags[!frontside] = f;
|
||||
}
|
||||
else
|
||||
{ // leave it on the outside
|
||||
frags[frontside] = f;
|
||||
frags[!frontside] = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // proper split
|
||||
SplitFace (f, split, &frags[0], &frags[1]);
|
||||
}
|
||||
|
||||
if (frags[frontside])
|
||||
{
|
||||
frags[frontside]->next = outside;
|
||||
outside = frags[frontside];
|
||||
}
|
||||
if (frags[!frontside])
|
||||
{
|
||||
frags[!frontside]->next = insidelist;
|
||||
insidelist = frags[!frontside];
|
||||
}
|
||||
}
|
||||
|
||||
inside = insidelist;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
SaveOutside
|
||||
|
||||
Saves all of the faces in the outside list to the bsp plane list
|
||||
==================
|
||||
*/
|
||||
void SaveOutside (qboolean mirror)
|
||||
{
|
||||
face_t *f , *next, *newf;
|
||||
int i;
|
||||
int planenum;
|
||||
|
||||
for (f=outside ; f ; f=next)
|
||||
{
|
||||
next = f->next;
|
||||
csgfaces++;
|
||||
Draw_DrawFace (f);
|
||||
planenum = f->planenum;
|
||||
|
||||
if (mirror)
|
||||
{
|
||||
newf = NewFaceFromFace (f);
|
||||
|
||||
newf->numpoints = f->numpoints;
|
||||
newf->planeside = f->planeside ^ 1; // reverse side
|
||||
newf->contents[0] = f->contents[1];
|
||||
newf->contents[1] = f->contents[0];
|
||||
|
||||
for (i=0 ; i<f->numpoints ; i++) // add points backwards
|
||||
{
|
||||
VectorCopy (f->pts[f->numpoints-1-i], newf->pts[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
newf = NULL;
|
||||
|
||||
validfaces[planenum] = MergeFaceToList(f, validfaces[planenum]);
|
||||
if (newf)
|
||||
validfaces[planenum] = MergeFaceToList(newf, validfaces[planenum]);
|
||||
|
||||
validfaces[planenum] = FreeMergeListScraps (validfaces[planenum]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
FreeInside
|
||||
|
||||
Free all the faces that got clipped out
|
||||
==================
|
||||
*/
|
||||
void FreeInside (int contents)
|
||||
{
|
||||
face_t *f, *next;
|
||||
|
||||
for (f=inside ; f ; f=next)
|
||||
{
|
||||
next = f->next;
|
||||
|
||||
if (contents != CONTENTS_SOLID)
|
||||
{
|
||||
f->contents[0] = contents;
|
||||
f->next = outside;
|
||||
outside = f;
|
||||
}
|
||||
else
|
||||
FreeFace (f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
BuildSurfaces
|
||||
|
||||
Returns a chain of all the external surfaces with one or more visible
|
||||
faces.
|
||||
==================
|
||||
*/
|
||||
surface_t *BuildSurfaces (void)
|
||||
{
|
||||
face_t **f;
|
||||
face_t *count;
|
||||
int i;
|
||||
surface_t *s;
|
||||
surface_t *surfhead;
|
||||
|
||||
surfhead = NULL;
|
||||
|
||||
f = validfaces;
|
||||
for (i=0 ; i<numbrushplanes ; i++, f++)
|
||||
{
|
||||
if (!*f)
|
||||
continue; // nothing left on this plane
|
||||
|
||||
// create a new surface to hold the faces on this plane
|
||||
s = AllocSurface ();
|
||||
s->planenum = i;
|
||||
s->next = surfhead;
|
||||
surfhead = s;
|
||||
s->faces = *f;
|
||||
for (count = s->faces ; count ; count=count->next)
|
||||
csgmergefaces++;
|
||||
CalcSurfaceInfo (s); // bounding box and flags
|
||||
}
|
||||
|
||||
return surfhead;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
CopyFacesToOutside
|
||||
==================
|
||||
*/
|
||||
void CopyFacesToOutside (brush_t *b)
|
||||
{
|
||||
face_t *f, *newf;
|
||||
|
||||
outside = NULL;
|
||||
|
||||
for (f=b->faces ; f ; f=f->next)
|
||||
{
|
||||
brushfaces++;
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<f->numpoints ; i++)
|
||||
printf ("(%f,%f,%f) ",f->pts[i][0], f->pts[i][1], f->pts[i][2]);
|
||||
printf ("\n");
|
||||
}
|
||||
#endif
|
||||
newf = AllocFace ();
|
||||
*newf = *f;
|
||||
newf->next = outside;
|
||||
newf->contents[0] = CONTENTS_EMPTY;
|
||||
newf->contents[1] = b->contents;
|
||||
outside = newf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CSGFaces
|
||||
|
||||
Returns a list of surfaces containing aall of the faces
|
||||
==================
|
||||
*/
|
||||
surface_t *CSGFaces (brushset_t *bs)
|
||||
{
|
||||
brush_t *b1, *b2;
|
||||
int i;
|
||||
qboolean overwrite;
|
||||
face_t *f;
|
||||
surface_t *surfhead;
|
||||
|
||||
qprintf ("---- CSGFaces ----\n");
|
||||
|
||||
memset (validfaces, 0, sizeof(validfaces));
|
||||
|
||||
csgfaces = brushfaces = csgmergefaces = 0;
|
||||
|
||||
Draw_ClearWindow ();
|
||||
|
||||
//
|
||||
// do the solid faces
|
||||
//
|
||||
for (b1=bs->brushes ; b1 ; b1 = b1->next)
|
||||
{
|
||||
// set outside to a copy of the brush's faces
|
||||
CopyFacesToOutside (b1);
|
||||
|
||||
overwrite = false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// check bounding box first
|
||||
for (i=0 ; i<3 ; i++)
|
||||
if (b1->mins[i] > b2->maxs[i] || b1->maxs[i] < b2->mins[i])
|
||||
break;
|
||||
if (i<3)
|
||||
continue;
|
||||
|
||||
// divide faces by the planes of the new brush
|
||||
|
||||
inside = outside;
|
||||
outside = NULL;
|
||||
|
||||
for (f=b2->faces ; f ; f=f->next)
|
||||
ClipInside (f->planenum, f->planeside, overwrite);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// all of the faces left in outside are real surface faces
|
||||
if (b1->contents != CONTENTS_SOLID)
|
||||
SaveOutside (true); // mirror faces for inside view
|
||||
else
|
||||
SaveOutside (false);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (!csgfaces)
|
||||
Error ("No faces");
|
||||
#endif
|
||||
|
||||
surfhead = BuildSurfaces ();
|
||||
|
||||
qprintf ("%5i brushfaces\n", brushfaces);
|
||||
qprintf ("%5i csgfaces\n", csgfaces);
|
||||
qprintf ("%5i mergedfaces\n", csgmergefaces);
|
||||
|
||||
return surfhead;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user