// 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; iplanenum = 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 ; inumpoints ; 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; }