/** * @brief The faces vertexes have been added to the superverts[] array, * and there may be more there than can be held in a face (MAXEDGES). * * If less, the faces vertexnums[] will be filled in, otherwise * face will reference a tree of split[] faces until all of the * vertexnums can be added. * * @note superverts[base] will become face->vertexnums[0], and the others * will be circularly filled in. */ static void FaceFromSuperverts (node_t* node, face_t* f, int base) { int i, remaining; remaining = numsuperverts; /* must split into two faces, because of vertex overload */ while (remaining > MAXEDGES) { c_faceoverflows++; face_t* newf = f->split[0] = NewFaceFromFace(f); newf->next = node->faces; node->faces = newf; newf->numpoints = MAXEDGES; for (i = 0; i < MAXEDGES; i++) newf->vertexnums[i] = superverts[(i + base) % numsuperverts]; f->split[1] = NewFaceFromFace(f); f = f->split[1]; f->next = node->faces; node->faces = f; remaining -= (MAXEDGES - 2); base = (base + MAXEDGES - 1) % numsuperverts; } /* copy the vertexes back to the face */ f->numpoints = remaining; for (i = 0; i < remaining; i++) f->vertexnums[i] = superverts[(i + base) % numsuperverts]; }
/** * @brief Chop up faces that are larger than we want in the surface cache */ static void SubdivideFace (node_t* node, face_t* f) { if (f->merged) return; /* special (non-surface cached) faces don't need subdivision */ const dBspTexinfo_t* tex = &curTile->texinfo[f->texinfo]; if (tex->surfaceFlags & SURF_WARP) return; for (int axis = 0; axis < 2; axis++) { while (1) { const winding_t* w = f->w; winding_t* frontw, *backw; float mins = 999999; float maxs = -999999; vec3_t temp; vec_t v; VectorCopy(tex->vecs[axis], temp); for (int i = 0; i < w->numpoints; i++) { v = DotProduct(w->p[i], temp); if (v < mins) mins = v; if (v > maxs) maxs = v; } /* no bsp subdivide for this winding? */ if (maxs - mins <= config.subdivideSize) break; /* split it */ c_subdivide++; v = VectorNormalize(temp); vec_t dist = (mins + config.subdivideSize - 16) / v; ClipWindingEpsilon(w, temp, dist, ON_EPSILON, &frontw, &backw); if (!frontw || !backw) Sys_Error("SubdivideFace: didn't split the polygon (texture: '%s')", tex->texture); f->split[0] = NewFaceFromFace(f); f->split[0]->w = frontw; f->split[0]->next = node->faces; node->faces = f->split[0]; f->split[1] = NewFaceFromFace(f); f->split[1]->w = backw; f->split[1]->next = node->faces; node->faces = f->split[1]; SubdivideFace(node, f->split[0]); SubdivideFace(node, f->split[1]); return; } } }
//----------------------------------------------------------------------------- // Purpose: Copies a face and its winding // Input : *pFace - // Output : face_t //----------------------------------------------------------------------------- face_t *CopyFace( face_t *pFace ) { face_t *f = NewFaceFromFace( pFace ); f->w = CopyWinding( pFace->w ); return f; }
/** * @brief If two polygons share a common edge and the edges that meet at the * common points are both inside the other polygons, merge them * * @return nullptr if the faces couldn't be merged, or the new face. * @note The originals will NOT be freed. */ static face_t* TryMerge (face_t* f1, face_t* f2, const vec3_t planenormal) { face_t* newf; winding_t* nw; if (!f1->w || !f2->w) return nullptr; if (f1->texinfo != f2->texinfo) return nullptr; if (f1->planenum != f2->planenum) /* on front and back sides */ return nullptr; if (f1->contentFlags != f2->contentFlags) return nullptr; nw = TryMergeWinding(f1->w, f2->w, planenormal); if (!nw) return nullptr; c_merge++; newf = NewFaceFromFace(f1); newf->w = nw; f1->merged = newf; f2->merged = newf; return newf; }
/* ================== SaveOutside Saves all of the faces in the outside list to the bsp plane list ================== */ static void SaveOutside(bool mirror) { face_t *f, *next, *newf; int i; int planenum; for (f = outside; f; f = next) { next = f->next; csgfaces++; planenum = f->planenum; if (mirror) { newf = NewFaceFromFace(f); newf->w.numpoints = f->w.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->w.numpoints; i++) // add points backwards VectorCopy(f->w.points[f->w.numpoints - 1 - i], newf->w.points[i]); validfaces[planenum] = MergeFaceToList(newf, validfaces[planenum]); } validfaces[planenum] = MergeFaceToList(f, validfaces[planenum]); validfaces[planenum] = FreeMergeListScraps(validfaces[planenum]); } }
/* Saves all of the faces in the outside list to the bsp plane list */ static void SaveOutside( tree_t *tree, bool mirror ) { int planenum; face_t *f, *next, *newf; for( f = outside; f; f = next ) { next = f->next; numcsgfaces++; planenum = f->planenum; if( mirror ) { newf = NewFaceFromFace( f ); newf->planeside = f->planeside ^ 1; // reverse side newf->contents[0] = f->contents[1]; newf->contents[1] = f->contents[0]; newf->winding = ReverseWinding( f->winding ); tree->validfaces[planenum] = MergeFaceToList_r( newf, tree->validfaces[planenum] ); } tree->validfaces[planenum] = MergeFaceToList_r( f, tree->validfaces[planenum]); tree->validfaces[planenum] = FreeMergeListScraps( tree->validfaces[planenum] ); } }
bface_t *CopyFace (bface_t *f) { bface_t *n; n = NewFaceFromFace (f); n->w = CopyWinding (f->w); VectorCopy (f->mins, n->mins); VectorCopy (f->maxs, n->maxs); return n; }
/* ============= TryMerge If two polygons share a common edge and the edges that meet at the common points are both inside the other polygons, merge them Returns NULL if the faces couldn't be merged, or the new face. The originals will NOT be freed. ============= */ static face_t * TryMerge(face_t *f1, face_t *f2) { vec_t *p1, *p2, *p3, *p4, *back; face_t *newf; int i, j, k, l; vec3_t normal, delta, planenormal; vec_t dot; plane_t *plane; bool keep1, keep2; if (f1->w.numpoints == -1 || f2->w.numpoints == -1 || f1->planeside != f2->planeside || f1->texturenum != f2->texturenum || f1->contents[0] != f2->contents[0] || f1->contents[1] != f2->contents[1]) return NULL; // find a common edge p1 = p2 = NULL; // stop compiler warning j = 0; // for (i = 0; i < f1->w.numpoints; i++) { p1 = f1->w.points[i]; p2 = f1->w.points[(i + 1) % f1->w.numpoints]; for (j = 0; j < f2->w.numpoints; j++) { p3 = f2->w.points[j]; p4 = f2->w.points[(j + 1) % f2->w.numpoints]; for (k = 0; k < 3; k++) { if (fabs(p1[k] - p4[k]) > EQUAL_EPSILON || fabs(p2[k] - p3[k]) > EQUAL_EPSILON) break; } if (k == 3) break; } if (j < f2->w.numpoints) break; } if (i == f1->w.numpoints) return NULL; // no matching edges // check slope of connected lines // if the slopes are colinear, the point can be removed plane = &pPlanes[f1->planenum]; VectorCopy(plane->normal, planenormal); if (f1->planeside) VectorSubtract(vec3_origin, planenormal, planenormal); back = f1->w.points[(i + f1->w.numpoints - 1) % f1->w.numpoints]; VectorSubtract(p1, back, delta); CrossProduct(planenormal, delta, normal); VectorNormalize(normal); back = f2->w.points[(j + 2) % f2->w.numpoints]; VectorSubtract(back, p1, delta); dot = DotProduct(delta, normal); if (dot > CONTINUOUS_EPSILON) return NULL; // not a convex polygon keep1 = dot < -CONTINUOUS_EPSILON; back = f1->w.points[(i + 2) % f1->w.numpoints]; VectorSubtract(back, p2, delta); CrossProduct(planenormal, delta, normal); VectorNormalize(normal); back = f2->w.points[(j + f2->w.numpoints - 1) % f2->w.numpoints]; VectorSubtract(back, p2, delta); dot = DotProduct(delta, normal); if (dot > CONTINUOUS_EPSILON) return NULL; // not a convex polygon keep2 = dot < -CONTINUOUS_EPSILON; // build the new polygon if (f1->w.numpoints + f2->w.numpoints > MAXEDGES) { Message(msgWarning, warnTooManyMergePoints); return NULL; } newf = NewFaceFromFace(f1); // copy first polygon if (keep2) k = (i + 1) % f1->w.numpoints; else k = (i + 2) % f1->w.numpoints; for (; k != i; k = (k + 1) % f1->w.numpoints) { VectorCopy(f1->w.points[k], newf->w.points[newf->w.numpoints]); newf->w.numpoints++; } // copy second polygon if (keep1) l = (j + 1) % f2->w.numpoints; else l = (j + 2) % f2->w.numpoints; for (; l != j; l = (l + 1) % f2->w.numpoints) { VectorCopy(f2->w.points[l], newf->w.points[newf->w.numpoints]); newf->w.numpoints++; } UpdateFaceSphere(newf); return newf; }
/* ================== SplitFace ================== */ void SplitFace (face_t *in, plane_t *split, face_t **front, face_t **back) { double dists[MAXEDGES+1]; int sides[MAXEDGES+1]; int counts[3]; double dot; int i, j; face_t *newf, *new2; double *p1, *p2; vec3_t mid; if (in->numpoints < 0) Error ("%s: freed face", __thisfunc__); 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 ("%s: numpoints > MAXEDGES", __thisfunc__); 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 ("%s: numpoints > MAXEDGES", __thisfunc__); #if 0 CheckFace (newf); CheckFace (new2); #endif // free the original face now that is is represented by the fragments FreeFace (in); }
bface_t *ClipFace (brush_t *b, bface_t *f, bface_t **outside, int splitplane, qboolean precedence) { bface_t *front; winding_t *fw, *bw; vec_t d; plane_t *split; int i, count[3]; // handle exact plane matches special if (f->planenum == (splitplane^1) ) { // opposite side, so put on inside list return f; } if ( f->planenum == splitplane ) { // coplanar if (precedence) { // this fragment will go to the inside, because // the earlier one was clipped to the outside return f; } f->next = *outside; *outside = f; return NULL; } split = &mapplanes[splitplane]; #if 0 count[0] = count[1] = count[2] = 0; for (i=0 ; i<f->w->numpoints ; i++) { d = DotProduct (f->w->p[i], split->normal) - split->dist; if (d < -SPLIT_EPSILON) count[1]++; else if (d > SPLIT_EPSILON) count[0]++; else count[2]++; } if (!count[0]) { fw = NULL; bw = f->w; } else if (!count[1]) { fw = f->w; bw = NULL; } else #endif ClipWindingNoCopy (f->w, split->normal, split->dist, &fw, &bw); if (!fw) return f; if (!bw) { f->next = *outside; *outside = f; return NULL; } FreeWinding (f->w); front = NewFaceFromFace (f); front->w = fw; WindingBounds (fw, front->mins, front->maxs); front->next = *outside; *outside = front; f->w = bw; WindingBounds (bw, f->mins, f->maxs); return f; }
static void SplitFaceForTjunc(face_t* f, face_t* original) { int i; face_t* newface; face_t* chain; vec3_t dir, test; vec_t v; int firstcorner, lastcorner; #ifdef _DEBUG static int counter = 0; Log("SplitFaceForTjunc %d\n", counter++); #endif chain = NULL; do { hlassume(f->original == NULL, assume_ValidPointer); // "SplitFaceForTjunc: f->original" if (f->numpoints <= MAXPOINTS) { // the face is now small enough without more cutting // so copy it back to the original *original = *f; original->original = chain; original->next = newlist; newlist = original; return; } tjuncfaces++; restart: // find the last corner VectorSubtract(f->pts[f->numpoints - 1], f->pts[0], dir); VectorNormalize(dir); for (lastcorner = f->numpoints - 1; lastcorner > 0; lastcorner--) { VectorSubtract(f->pts[lastcorner - 1], f->pts[lastcorner], test); VectorNormalize(test); v = DotProduct(test, dir); if (v < 1.0 - ON_EPSILON || v > 1.0 + ON_EPSILON) { break; } } // find the first corner VectorSubtract(f->pts[1], f->pts[0], dir); VectorNormalize(dir); for (firstcorner = 1; firstcorner < f->numpoints - 1; firstcorner++) { VectorSubtract(f->pts[firstcorner + 1], f->pts[firstcorner], test); VectorNormalize(test); v = DotProduct(test, dir); if (v < 1.0 - ON_EPSILON || v > 1.0 + ON_EPSILON) { break; } } if (firstcorner + 2 >= MAXPOINTS) { // rotate the point winding VectorCopy(f->pts[0], test); for (i = 1; i < f->numpoints; i++) { VectorCopy(f->pts[i], f->pts[i - 1]); } VectorCopy(test, f->pts[f->numpoints - 1]); goto restart; } // cut off as big a piece as possible, less than MAXPOINTS, and not // past lastcorner newface = NewFaceFromFace(f); hlassume(f->original == NULL, assume_ValidPointer); // "SplitFaceForTjunc: f->original" newface->original = chain; chain = newface; newface->next = newlist; newlist = newface; if (f->numpoints - firstcorner <= MAXPOINTS) { newface->numpoints = firstcorner + 2; } else if (lastcorner + 2 < MAXPOINTS && f->numpoints - lastcorner <= MAXPOINTS) { newface->numpoints = lastcorner + 2; } else { newface->numpoints = MAXPOINTS; } for (i = 0; i < newface->numpoints; i++) { VectorCopy(f->pts[i], newface->pts[i]); } for (i = newface->numpoints - 1; i < f->numpoints; i++) { VectorCopy(f->pts[i], f->pts[i - (newface->numpoints - 2)]); } f->numpoints -= (newface->numpoints - 2); } while (1); }
/* ================== 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->w.numpoints < 0) Message(msgError, errFreedFace); /* Fast test */ dot = DotProduct(in->origin, split->normal) - split->dist; if (dot > in->radius) { counts[SIDE_FRONT] = 1; counts[SIDE_BACK] = 0; } else if (dot < -in->radius) { counts[SIDE_FRONT] = 0; counts[SIDE_BACK] = 1; } else { CalcSides(&in->w, split, sides, dists, counts); } // Plane doesn't split this face after all if (!counts[SIDE_FRONT]) { *front = NULL; *back = in; return; } if (!counts[SIDE_BACK]) { *front = in; *back = NULL; return; } *back = newf = NewFaceFromFace(in); *front = new2 = NewFaceFromFace(in); // distribute the points and generate splits for (i = 0; i < in->w.numpoints; i++) { // Note: Possible for numpoints on newf or new2 to exceed MAXEDGES if // in->w.numpoints == MAXEDGES and it is a really devious split. p1 = in->w.points[i]; if (sides[i] == SIDE_ON) { VectorCopy(p1, newf->w.points[newf->w.numpoints]); newf->w.numpoints++; VectorCopy(p1, new2->w.points[new2->w.numpoints]); new2->w.numpoints++; continue; } if (sides[i] == SIDE_FRONT) { VectorCopy(p1, new2->w.points[new2->w.numpoints]); new2->w.numpoints++; } else { VectorCopy(p1, newf->w.points[newf->w.numpoints]); newf->w.numpoints++; } if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) continue; // generate a split point p2 = in->w.points[(i + 1) % in->w.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->w.points[newf->w.numpoints]); newf->w.numpoints++; VectorCopy(mid, new2->w.points[new2->w.numpoints]); new2->w.numpoints++; } if (newf->w.numpoints > MAXEDGES || new2->w.numpoints > MAXEDGES) Message(msgError, errLowSplitPointCount); // free the original face now that it is represented by the fragments FreeMem(in, FACE, 1); }
/* ============= TryMerge If two polygons share a common edge and the edges that meet at the common points are both inside the other polygons, merge them Returns NULL if the faces couldn't be merged, or the new face. The originals will NOT be freed. ============= */ face_t *TryMerge (face_t *f1, face_t *f2) { vec_t *p1 = NULL, *p2 = NULL, *p3, *p4, *back; face_t *newf; int i, j, k, l; vec3_t normal, delta, planenormal; vec_t dot; plane_t *plane; qboolean keep1, keep2; if (f1->numpoints == -1 || f2->numpoints == -1 || f1->planeside != f2->planeside || f1->texturenum != f2->texturenum || f1->contents[0] != f2->contents[0] || f1->contents[1] != f2->contents[1]) return NULL; // // find a common edge // for (i=0, j = 0 ; i<f1->numpoints ; i++) { p1 = f1->pts[i]; p2 = f1->pts[(i+1)%f1->numpoints]; for (j=0 ; j<f2->numpoints ; j++) { p3 = f2->pts[j]; p4 = f2->pts[(j+1)%f2->numpoints]; for (k=0 ; k<3 ; k++) { if (fabs(p1[k] - p4[k]) > EQUAL_EPSILON || fabs(p2[k] - p3[k]) > EQUAL_EPSILON) break; } if (k==3) break; } if (j < f2->numpoints) break; } if (i == f1->numpoints) return NULL; // no matching edges // // check slope of connected lines // if the slopes are colinear, the point can be removed // plane = &planes[f1->planenum]; VectorCopy (plane->normal, planenormal); if (f1->planeside) VectorNegate (planenormal, planenormal); back = f1->pts[(i+f1->numpoints-1)%f1->numpoints]; VectorSubtract (p1, back, delta); CrossProduct (planenormal, delta, normal); VectorNormalize (normal); back = f2->pts[(j+2)%f2->numpoints]; VectorSubtract (back, p1, delta); dot = DotProduct (delta, normal); if (dot > CONTINUOUS_EPSILON) return NULL; // not a convex polygon keep1 = dot < -CONTINUOUS_EPSILON; back = f1->pts[(i+2)%f1->numpoints]; VectorSubtract (back, p2, delta); CrossProduct (planenormal, delta, normal); VectorNormalize (normal); back = f2->pts[(j+f2->numpoints-1)%f2->numpoints]; VectorSubtract (back, p2, delta); dot = DotProduct (delta, normal); if (dot > CONTINUOUS_EPSILON) return NULL; // not a convex polygon keep2 = dot < -CONTINUOUS_EPSILON; // // build the new polygon // if (f1->numpoints + f2->numpoints > MAXEDGES) { // Error ("TryMerge: too many edges!"); return NULL; } newf = NewFaceFromFace (f1); // copy first polygon for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints) { if (k==(i+1)%f1->numpoints && !keep2) continue; VectorCopy (f1->pts[k], newf->pts[newf->numpoints]); newf->numpoints++; } // copy second polygon for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints) { if (l==(j+1)%f2->numpoints && !keep1) continue; VectorCopy (f2->pts[l], newf->pts[newf->numpoints]); newf->numpoints++; } return newf; }
void FaceFromSuperface (face_t *original) { int i, numpts; face_t *newf, *chain; vec3_t dir, test; vec_t v; int firstcorner, lastcorner; chain = NULL; do { if( superface->numpoints < 3 ) { c_degenerateFaces++; return; } if (superface->numpoints <= MAX_VERTS_ON_FACE) { // the face is now small enough without more cutting // so copy it back to the original *original = superface->original; original->winding = CopyWindingExt( superface->numpoints, superface->points ); original->original = chain; original->next = newlist; newlist = original; return; } c_tjuncfaces++; restart: // find the last corner VectorSubtract (superface->points[superface->numpoints-1], superface->points[0], dir); VectorNormalize (dir); for (lastcorner=superface->numpoints-1 ; lastcorner > 0 ; lastcorner--) { VectorSubtract (superface->points[lastcorner-1], superface->points[lastcorner], test); VectorNormalize (test); v = DotProduct (test, dir); if (v < 0.9999 || v > 1.00001) { break; } } // find the first corner VectorSubtract (superface->points[1], superface->points[0], dir); VectorNormalize (dir); for (firstcorner=1 ; firstcorner < superface->numpoints-1 ; firstcorner++) { VectorSubtract (superface->points[firstcorner+1], superface->points[firstcorner], test); VectorNormalize (test); v = DotProduct (test, dir); if (v < 0.9999 || v > 1.00001) break; } if (firstcorner+2 >= MAX_VERTS_ON_FACE) { c_rotated++; // rotate the point winding VectorCopy (superface->points[0], test); for (i=1 ; i<superface->numpoints ; i++) VectorCopy (superface->points[i], superface->points[i-1]); VectorCopy (test, superface->points[superface->numpoints-1]); goto restart; } // cut off as big a piece as possible, less than MAXPOINTS, and not // past lastcorner newf = NewFaceFromFace (&superface->original); newf->original = chain; chain = newf; newf->next = newlist; newlist = newf; if (superface->numpoints - firstcorner <= MAX_VERTS_ON_FACE) numpts = firstcorner + 2; else if (lastcorner+2 < MAX_VERTS_ON_FACE && superface->numpoints - lastcorner <= MAX_VERTS_ON_FACE) numpts = lastcorner + 2; else numpts = MAX_VERTS_ON_FACE; if( numpts < 3 ) { c_degenerateFaces++; return; } newf->winding = CopyWindingExt( numpts, superface->points ); for (i=newf->winding->numpoints-1 ; i<superface->numpoints ; i++) VectorCopy (superface->points[i], superface->points[i-(newf->winding->numpoints-2)]); superface->numpoints -= (newf->winding->numpoints-2); } while (1); }
//----------------------------------------------------------------------------- // Purpose: // Input : *pFace - input face to test // *pbrush - brush to clip face against // **pOutputList - list of faces clipped from pFace // Output : Returns true if the brush completely clips the face //----------------------------------------------------------------------------- // NOTE: This assumes the brushes have already been chopped so that no solid space // is enclosed by more than one brush!! bool ClipFaceToBrush( face_t *pFace, bspbrush_t *pbrush, face_t **pOutputList ) { int planenum = pFace->planenum & (~1); int foundSide = -1; CUtlVector<int> sortedSides; int i; for ( i = 0; i < pbrush->numsides && foundSide < 0; i++ ) { int bplane = pbrush->sides[i].planenum & (~1); if ( bplane == planenum ) foundSide = i; } Vector offset = -0.5f * (pbrush->maxs + pbrush->mins); face_t *currentface = CopyFace( pFace ); if ( foundSide >= 0 ) { sortedSides.RemoveAll(); for ( i = 0; i < pbrush->numsides; i++ ) { // don't clip to bevels if ( pbrush->sides[i].bevel ) continue; if ( g_MainMap->mapplanes[pbrush->sides[i].planenum].type <= PLANE_Z ) { sortedSides.AddToHead( i ); } else { sortedSides.AddToTail( i ); } } for ( i = 0; i < sortedSides.Size(); i++ ) { int index = sortedSides[i]; if ( index == foundSide ) continue; plane_t *plane = &g_MainMap->mapplanes[pbrush->sides[index].planenum]; winding_t *frontwinding, *backwinding; ClipWindingEpsilon_Offset(currentface->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, offset); // only clip if some part of this face is on the back side of all brush sides if ( !backwinding || WindingIsTiny(backwinding)) { FreeFaceList( *pOutputList ); *pOutputList = NULL; break; } if ( frontwinding && !WindingIsTiny(frontwinding) ) { // add this fragment to the return list // make a face for the fragment face_t *f = NewFaceFromFace( pFace ); f->w = frontwinding; // link the fragment in f->next = *pOutputList; *pOutputList = f; } // update the current winding to be the part behind each plane FreeWinding( currentface->w ); currentface->w = backwinding; } // free the bit that is left in solid or not clipped (if we broke out early) FreeFace( currentface ); // if we made it all the way through and didn't produce any fragments then the whole face was clipped away if ( !*pOutputList && i == sortedSides.Size() ) { return true; } } return false; }
//----------------------------------------------------------------------------- // Purpose: Recursively filter a face into the tree leaving references to the // original face in any visible leaves that a clipped fragment falls // into. // Input : *node - current head of tree // *face - clipped face fragment // *original - unclipped original face // Output : Returns true if any references were left //----------------------------------------------------------------------------- bool MergeFace_r( node_t *node, face_t *face, face_t *original ) { bool referenced = false; if ( node->planenum == PLANENUM_LEAF ) { if ( node->contents & CONTENTS_SOLID ) { FreeFace( face ); return false; } leafface_t *plist = new leafface_t; plist->pFace = original; plist->pNext = node->leaffacelist; node->leaffacelist = plist; referenced = true; } else { // UNDONE: Don't copy the faces each time unless it's necessary!?!?! plane_t *plane = &g_MainMap->mapplanes[node->planenum]; winding_t *frontwinding, *backwinding, *onwinding; Vector offset; WindingCenter( face->w, offset ); // UNDONE: Export epsilon from original face clipping code ClassifyWindingEpsilon_Offset(face->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, &onwinding, -offset); if ( onwinding ) { // face is in the split plane, go down the appropriate side according to the facing direction assert( frontwinding == NULL ); assert( backwinding == NULL ); if ( DotProduct( g_MainMap->mapplanes[face->planenum].normal, g_MainMap->mapplanes[node->planenum].normal ) > 0 ) { frontwinding = onwinding; } else { backwinding = onwinding; } } if ( frontwinding ) { face_t *tmp = NewFaceFromFace( face ); tmp->w = frontwinding; referenced = MergeFace_r( node->children[0], tmp, original ); } if ( backwinding ) { face_t *tmp = NewFaceFromFace( face ); tmp->w = backwinding; bool test = MergeFace_r( node->children[1], tmp, original ); referenced = referenced || test; } } FreeFace( face ); return referenced; }
static void SplitFaceForTjunc(face_t *f, face_t *original) { int i; face_t *newf, *chain; vec3_t dir, test; vec_t v; int firstcorner, lastcorner; chain = NULL; do { if (f->w.numpoints <= MAXPOINTS) { // the face is now small enough without more cutting // so copy it back to the original *original = *f; original->original = chain; original->next = newlist; newlist = original; return; } tjuncfaces++; restart: // find the last corner VectorSubtract(f->w.points[f->w.numpoints - 1], f->w.points[0], dir); VectorNormalize(dir); for (lastcorner = f->w.numpoints - 1; lastcorner > 0; lastcorner--) { VectorSubtract(f->w.points[lastcorner - 1], f->w.points[lastcorner], test); VectorNormalize(test); v = DotProduct(test, dir); if (v < 1 - ANGLEEPSILON || v > 1 + ANGLEEPSILON) { break; } } // find the first corner VectorSubtract(f->w.points[1], f->w.points[0], dir); VectorNormalize(dir); for (firstcorner = 1; firstcorner < f->w.numpoints - 1; firstcorner++) { VectorSubtract(f->w.points[firstcorner + 1], f->w.points[firstcorner], test); VectorNormalize(test); v = DotProduct(test, dir); if (v < 1 - ANGLEEPSILON || v > 1 + ANGLEEPSILON) { break; } } if (firstcorner + 2 >= MAXPOINTS) { // rotate the point winding VectorCopy(f->w.points[0], test); for (i = 1; i < f->w.numpoints; i++) { VectorCopy(f->w.points[i], f->w.points[i - 1]); } VectorCopy(test, f->w.points[f->w.numpoints - 1]); goto restart; } // cut off as big a piece as possible, less than MAXPOINTS, and not // past lastcorner newf = NewFaceFromFace(f); if (f->original) Error(errOriginalExists); newf->original = chain; chain = newf; newf->next = newlist; newlist = newf; if (f->w.numpoints - firstcorner <= MAXPOINTS) newf->w.numpoints = firstcorner + 2; else if (lastcorner + 2 < MAXPOINTS && f->w.numpoints - lastcorner <= MAXPOINTS) newf->w.numpoints = lastcorner + 2; else newf->w.numpoints = MAXPOINTS; for (i = 0; i < newf->w.numpoints; i++) { VectorCopy(f->w.points[i], newf->w.points[i]); } for (i = newf->w.numpoints - 1; i < f->w.numpoints; i++) { VectorCopy(f->w.points[i], f->w.points[i - (newf->w.numpoints - 2)]); } f->w.numpoints -= (newf->w.numpoints - 2); } while (1); }