// ===================================================================================== // ChopWinding // ===================================================================================== inline winding_t* ChopWinding(winding_t* const in, pstack_t* const stack, const plane_t* const split) { vec_t dists[128]; int sides[128]; int counts[3]; vec_t dot; int i; vec3_t mid; winding_t* neww; counts[0] = counts[1] = counts[2] = 0; if (in->numpoints > (sizeof(sides) / sizeof(*sides))) { Error("Winding with too many sides!"); } // determine sides for each point for (i = 0; i < in->numpoints; i++) { dot = DotProduct(in->points[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]]++; } if (!counts[1]) { return in; // completely on front side } if (!counts[0]) { FreeStackWinding(in, stack); return NULL; } sides[i] = sides[0]; dists[i] = dists[0]; neww = AllocStackWinding(stack); neww->numpoints = 0; for (i = 0; i < in->numpoints; i++) { vec_t* p1 = in->points[i]; if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING) { Warning("ChopWinding : rejected(1) due to too many points\n"); FreeStackWinding(neww, stack); return in; // can't chop -- fall back to original } if (sides[i] == SIDE_ON) { VectorCopy(p1, neww->points[neww->numpoints]); neww->numpoints++; continue; } else if (sides[i] == SIDE_FRONT) { VectorCopy(p1, neww->points[neww->numpoints]); neww->numpoints++; } if ((sides[i + 1] == SIDE_ON) | (sides[i + 1] == sides[i])) // | instead of || for branch optimization { continue; } if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING) { Warning("ChopWinding : rejected(2) due to too many points\n"); FreeStackWinding(neww, stack); return in; // can't chop -- fall back to original } // generate a split point { unsigned tmp = i + 1; if (tmp >= in->numpoints) { tmp = 0; } const vec_t* p2 = in->points[tmp]; dot = dists[i] / (dists[i] - dists[i + 1]); const vec_t* normal = split->normal; const vec_t dist = split->dist; unsigned int j; for (j = 0; j < 3; j++) { // avoid round off error when possible if (normal[j] < (1.0 - NORMAL_EPSILON)) { if (normal[j] > (-1.0 + NORMAL_EPSILON)) { mid[j] = p1[j] + dot * (p2[j] - p1[j]); } else { mid[j] = -dist; } } else { mid[j] = dist; } } } VectorCopy(mid, neww->points[neww->numpoints]); neww->numpoints++; } // free the original winding FreeStackWinding(in, stack); return neww; }
/* ================== ClipStackWinding Clips the winding to the plane, returning the new winding on the positive side. Frees the input winding (if on stack). If the resulting winding would have too many points, the clip operation is aborted and the original winding is returned. ================== */ winding_t * ClipStackWinding(winding_t *in, pstack_t *stack, plane_t *split) { vec_t dists[MAX_WINDING + 1]; int sides[MAX_WINDING + 1]; int counts[3]; vec_t dot, fraction; int i, j; vec_t *p1, *p2; vec3_t mid; winding_t *neww; /* Fast test first */ dot = DotProduct(in->origin, split->normal) - split->dist; if (dot < -in->radius) { FreeStackWinding(in, stack); return NULL; } else if (dot > in->radius) { return in; } if (in->numpoints > MAX_WINDING) Error("%s: in->numpoints > MAX_WINDING (%d > %d)", __func__, in->numpoints, MAX_WINDING); counts[0] = counts[1] = counts[2] = 0; /* determine sides for each point */ for (i = 0; i < in->numpoints; i++) { dot = DotProduct(in->points[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]) { FreeStackWinding(in, stack); return NULL; } if (!counts[1]) return in; neww = AllocStackWinding(stack); neww->numpoints = 0; VectorCopy(in->origin, neww->origin); neww->radius = in->radius; for (i = 0; i < in->numpoints; i++) { p1 = in->points[i]; if (sides[i] == SIDE_ON) { if (neww->numpoints == MAX_WINDING_FIXED) goto noclip; VectorCopy(p1, neww->points[neww->numpoints]); neww->numpoints++; continue; } if (sides[i] == SIDE_FRONT) { if (neww->numpoints == MAX_WINDING_FIXED) goto noclip; 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]; fraction = 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] + fraction * (p2[j] - p1[j]); } if (neww->numpoints == MAX_WINDING_FIXED) goto noclip; VectorCopy(mid, neww->points[neww->numpoints]); neww->numpoints++; } FreeStackWinding(in, stack); return neww; noclip: FreeStackWinding(neww, stack); c_noclip++; return in; }