#include "quakedef.h" #include "winding.h" // this code came from qbsp source #define MAX_POINTS_ON_WINDING 64 winding_t *Winding_New(int points) { winding_t *w; w = Mem_Alloc(loadmodel->mempool, sizeof(winding_t) + sizeof(double[3]) * (points - 8)); w->maxpoints = points; return w; } void Winding_Free(winding_t *w) { Mem_Free(w); } winding_t *Winding_NewFromPlane(double normalx, double normaly, double normalz, double dist) { winding_t *w; w = Winding_New(4); BufWinding_NewFromPlane(w, normalx, normaly, normalz, dist); return w; } //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 *Winding_Clip(winding_t *in, double splitnormalx, double splitnormaly, double splitnormalz, double splitdist, int keepon) { winding_t *neww; double dot, *p1, *p2, mid[3], splitnormal[3], dists[MAX_POINTS_ON_WINDING + 1]; int i, j, maxpts, counts[3], sides[MAX_POINTS_ON_WINDING + 1]; splitnormal[0] = splitnormalx; splitnormal[1] = splitnormaly; splitnormal[2] = splitnormalz; counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; // determine sides for each point for (i = 0;i < in->numpoints;i++) { dists[i] = dot = DotProduct(in->points[i], splitnormal) - splitdist; 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 (keepon && !counts[0] && !counts[1]) return in; if (!counts[0]) { Winding_Free(in); return NULL; } if (!counts[1]) return in; maxpts = 0; for (i = 0;i < in->numpoints;i++) { if (sides[i] == SIDE_ON) { maxpts++; continue; } if (sides[i] == SIDE_FRONT) maxpts++; if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; maxpts++; } if (maxpts > MAX_POINTS_ON_WINDING) Sys_Error("Winding_Clip: maxpts > MAX_POINTS_ON_WINDING"); neww = Winding_New(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 (splitnormal[j] == 1) mid[j] = splitdist; else if (splitnormal[j] == -1) mid[j] = -splitdist; else mid[j] = p1[j] + dot* (p2[j]-p1[j]); } VectorCopy(mid, neww->points[neww->numpoints]); neww->numpoints++; } // free the original winding Winding_Free(in); return neww; } //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. void Winding_Divide(winding_t *in, double splitnormalx, double splitnormaly, double splitnormalz, double splitdist, winding_t **front, winding_t **back) { winding_t *f, *b; double dot, *p1, *p2, mid[3], splitnormal[3], dists[MAX_POINTS_ON_WINDING + 1]; int i, j, frontpts, backpts, counts[3], sides[MAX_POINTS_ON_WINDING + 1]; splitnormal[0] = splitnormalx; splitnormal[1] = splitnormaly; splitnormal[2] = splitnormalz; counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; // determine sides for each point for (i = 0;i < in->numpoints;i++) { dot = DotProduct(in->points[i], splitnormal); dot -= splitdist; 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]; *front = *back = NULL; if (!counts[0]) { *back = in; return; } if (!counts[1]) { *front = in; return; } frontpts = 0; backpts = 0; for (i = 0;i < in->numpoints;i++) { if (sides[i] == SIDE_ON) { frontpts++; backpts++; continue; } if (sides[i] == SIDE_FRONT) frontpts++; else if (sides[i] == SIDE_BACK) backpts++; if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; frontpts++; backpts++; } if (frontpts > MAX_POINTS_ON_WINDING) Sys_Error("Winding_Clip: frontpts > MAX_POINTS_ON_WINDING"); if (backpts > MAX_POINTS_ON_WINDING) Sys_Error("Winding_Clip: backpts > MAX_POINTS_ON_WINDING"); *front = f = Winding_New(frontpts); *back = b = Winding_New(backpts); 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++; } else 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 (splitnormal[j] == 1) mid[j] = splitdist; else if (splitnormal[j] == -1) mid[j] = -splitdist; 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++; } } // LordHavoc: these functions are more efficient by not allocating/freeing memory all the time void BufWinding_NewFromPlane(winding_t *w, double normalx, double normaly, double normalz, double dist) { int x; double max, v, org[3], vright[3], vup[3], normal[3]; w->numpoints = 0; if (w->maxpoints < 4) return; w->numpoints = 4; normal[0] = normalx; normal[1] = normaly; normal[2] = normalz; #if 0 VectorVectorsDouble(normal, vright, vup); #else // find the major axis x = 0; max = fabs(normal[0]); v = fabs(normal[1]); if(v > max) { x = 1; max = v; } v = fabs(normal[2]); if(v > max) { x = 2; max = v; } VectorClear(vup); switch(x) { case 0: case 1: vup[2] = 1; break; case 2: vup[0] = 1; break; } v = DotProduct(vup, normal); VectorMA(vup, -v, normal, vup); VectorNormalize(vup); #endif VectorScale(normal, dist, org); CrossProduct(vup, normal, vright); VectorScale(vup, 1024.0*1024.0*1024.0, vup); VectorScale(vright, 1024.0*1024.0*1024.0, vright); // project a really big axis aligned box onto the plane 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]); #if 0 { double n[3]; TriangleNormal(w->points[0], w->points[1], w->points[2], n); VectorNormalize(n); if (fabs(DotProduct(n, normal) - 1) > 0.01f) Con_Printf("%.0f %.0f %.0f (%.0f %.0f %.0f, %.0f %.0f %.0f) != %.0f %.0f %.0f (%.0f %.0f %.0f, %.0f %.0f %.0f, %.0f %.0f %.0f, %.0f %.0f %.0f)\n", normal[0], normal[1], normal[2], vright[0], vright[1], vright[2], vup[0], vup[1], vup[2], n[0], n[1], n[2], w->points[0][0], w->points[0][1], w->points[0][2], w->points[1][0], w->points[1][1], w->points[1][2], w->points[2][0], w->points[2][1], w->points[2][2], w->points[3][0], w->points[3][1], w->points[3][2]); } #endif } void BufWinding_Divide(winding_t *in, double splitnormalx, double splitnormaly, double splitnormalz, double splitdist, winding_t *outfront, int *neededfrontpoints, winding_t *outback, int *neededbackpoints) { double dot, *p1, *p2, mid[3], splitnormal[3], dists[MAX_POINTS_ON_WINDING + 1]; int i, j, frontpts, backpts, counts[3], sides[MAX_POINTS_ON_WINDING + 1]; if (outfront) outfront->numpoints = 0; if (outback) outback->numpoints = 0; if (in->numpoints > MAX_POINTS_ON_WINDING || (!outfront && !outback)) { if (neededfrontpoints) *neededfrontpoints = 0; if (neededbackpoints) *neededbackpoints = 0; return; } splitnormal[0] = splitnormalx; splitnormal[1] = splitnormaly; splitnormal[2] = splitnormalz; counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; // determine sides for each point for (i = 0;i < in->numpoints;i++) { dot = DotProduct(in->points[i], splitnormal); dot -= splitdist; 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]; frontpts = 0; backpts = 0; for (i = 0;i < in->numpoints;i++) { if (sides[i] != SIDE_ON) { if (sides[i] == SIDE_FRONT) frontpts++; else if (sides[i] == SIDE_BACK) backpts++; if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; } frontpts++; backpts++; } if (neededfrontpoints) *neededfrontpoints = frontpts; if (neededbackpoints) *neededbackpoints = backpts; if ((outfront && outfront->maxpoints < frontpts) || (outback && outback->maxpoints < backpts)) return; for (i = 0;i < in->numpoints;i++) { p1 = in->points[i]; if (sides[i] == SIDE_ON) { if (outfront) { VectorCopy(p1, outfront->points[outfront->numpoints]); outfront->numpoints++; } if (outback) { VectorCopy(p1, outback->points[outback->numpoints]); outback->numpoints++; } continue; } if (sides[i] == SIDE_FRONT) { if (outfront) { VectorCopy(p1, outfront->points[outfront->numpoints]); outfront->numpoints++; } } else if (sides[i] == SIDE_BACK) { if (outback) { VectorCopy(p1, outback->points[outback->numpoints]); outback->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 (splitnormal[j] == 1) mid[j] = splitdist; else if (splitnormal[j] == -1) mid[j] = -splitdist; else mid[j] = p1[j] + dot* (p2[j]-p1[j]); } if (outfront) { VectorCopy(mid, outfront->points[outfront->numpoints]); outfront->numpoints++; } if (outback) { VectorCopy(mid, outback->points[outback->numpoints]); outback->numpoints++; } } } void Polygon_Divide_Double(int innumpoints, const double *inpoints, double splitnormalx, double splitnormaly, double splitnormalz, double splitdist, int outfrontmaxpoints, double *outfrontpoints, int *neededfrontpoints, int outbackmaxpoints, double *outbackpoints, int *neededbackpoints) { double dot, mid[3], splitnormal[3], dists[MAX_POINTS_ON_WINDING + 1]; const double *p1, *p2; int i, j, frontpts, backpts, counts[3], sides[MAX_POINTS_ON_WINDING + 1]; if (neededfrontpoints) *neededfrontpoints = 0; if (neededbackpoints) *neededbackpoints = 0; if (innumpoints > MAX_POINTS_ON_WINDING || (!outfrontmaxpoints && !outbackmaxpoints)) return; splitnormal[0] = splitnormalx; splitnormal[1] = splitnormaly; splitnormal[2] = splitnormalz; counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; // determine sides for each point for (i = 0;i < innumpoints;i++) { dot = DotProduct(inpoints + i * 3, splitnormal); dot -= splitdist; 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]; frontpts = 0; backpts = 0; for (i = 0;i < innumpoints;i++) { if (sides[i] != SIDE_ON) { if (sides[i] == SIDE_FRONT) frontpts++; else if (sides[i] == SIDE_BACK) backpts++; if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; } frontpts++; backpts++; } if (neededfrontpoints) *neededfrontpoints = frontpts; if (neededbackpoints) *neededbackpoints = backpts; if ((outfrontmaxpoints && outfrontmaxpoints < frontpts) || (outbackmaxpoints && outbackmaxpoints < backpts)) return; for (i = 0;i < innumpoints;i++) { p1 = inpoints + i * 3; if (sides[i] == SIDE_ON) { if (outfrontmaxpoints) { VectorCopy(p1, outfrontpoints); outfrontpoints += 3; } if (outbackmaxpoints) { VectorCopy(p1, outbackpoints); outbackpoints += 3; } continue; } if (sides[i] == SIDE_FRONT) { if (outfrontmaxpoints) { VectorCopy(p1, outfrontpoints); outfrontpoints += 3; } } else if (sides[i] == SIDE_BACK) { if (outbackmaxpoints) { VectorCopy(p1, outbackpoints); outbackpoints += 3; } } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; // generate a split point p2 = inpoints + ((i+1)%innumpoints) * 3; dot = dists[i] / (dists[i]-dists[i+1]); for (j = 0;j < 3;j++) { // avoid round off error when possible if (splitnormal[j] == 1) mid[j] = splitdist; else if (splitnormal[j] == -1) mid[j] = -splitdist; else mid[j] = p1[j] + dot* (p2[j]-p1[j]); } if (outfrontmaxpoints) { VectorCopy(mid, outfrontpoints); outfrontpoints += 3; } if (outbackmaxpoints) { VectorCopy(mid, outbackpoints); outbackpoints += 3; } } } void Polygon_Divide_Float(int innumpoints, const float *inpoints, float splitnormalx, float splitnormaly, float splitnormalz, float splitdist, int outfrontmaxpoints, float *outfrontpoints, int *neededfrontpoints, int outbackmaxpoints, float *outbackpoints, int *neededbackpoints) { float dot, mid[3], splitnormal[3], dists[MAX_POINTS_ON_WINDING + 1]; const float *p1, *p2; int i, j, frontpts, backpts, counts[3], sides[MAX_POINTS_ON_WINDING + 1]; if (neededfrontpoints) *neededfrontpoints = 0; if (neededbackpoints) *neededbackpoints = 0; if (innumpoints > MAX_POINTS_ON_WINDING || (!outfrontmaxpoints && !outbackmaxpoints)) return; splitnormal[0] = splitnormalx; splitnormal[1] = splitnormaly; splitnormal[2] = splitnormalz; counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; // determine sides for each point for (i = 0;i < innumpoints;i++) { dot = DotProduct(inpoints + i * 3, splitnormal); dot -= splitdist; 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]; frontpts = 0; backpts = 0; for (i = 0;i < innumpoints;i++) { if (sides[i] != SIDE_ON) { if (sides[i] == SIDE_FRONT) frontpts++; else if (sides[i] == SIDE_BACK) backpts++; if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; } frontpts++; backpts++; } if (neededfrontpoints) *neededfrontpoints = frontpts; if (neededbackpoints) *neededbackpoints = backpts; if ((outfrontmaxpoints && outfrontmaxpoints < frontpts) || (outbackmaxpoints && outbackmaxpoints < backpts)) return; for (i = 0;i < innumpoints;i++) { p1 = inpoints + i * 3; if (sides[i] == SIDE_ON) { if (outfrontmaxpoints) { VectorCopy(p1, outfrontpoints); outfrontpoints += 3; } if (outbackmaxpoints) { VectorCopy(p1, outbackpoints); outbackpoints += 3; } continue; } if (sides[i] == SIDE_FRONT) { if (outfrontmaxpoints) { VectorCopy(p1, outfrontpoints); outfrontpoints += 3; } } else if (sides[i] == SIDE_BACK) { if (outbackmaxpoints) { VectorCopy(p1, outbackpoints); outbackpoints += 3; } } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; // generate a split point p2 = inpoints + ((i+1)%innumpoints) * 3; dot = dists[i] / (dists[i]-dists[i+1]); for (j = 0;j < 3;j++) { // avoid round off error when possible if (splitnormal[j] == 1) mid[j] = splitdist; else if (splitnormal[j] == -1) mid[j] = -splitdist; else mid[j] = p1[j] + dot* (p2[j]-p1[j]); } if (outfrontmaxpoints) { VectorCopy(mid, outfrontpoints); outfrontpoints += 3; } if (outbackmaxpoints) { VectorCopy(mid, outbackpoints); outbackpoints += 3; } } }