vis, qbsp: improve robustness of ClipWinding/DivideWinding

Carry over some correctness fixes from recent changes to vis into the
qbsp versions of ClipWinding and make the implementations more
consistent overall.

Most importantly:
* Ensure we always have allocated one extra dists/sides slot for wrap around
* Check we have space for a new vertex every time we try to add one

Signed-off-by: Kevin Shanahan <kmshanah@disenchant.net>
This commit is contained in:
Kevin Shanahan 2013-04-23 13:15:22 +09:30
parent dddae057d6
commit 37f87a28be
2 changed files with 77 additions and 51 deletions

View File

@ -164,16 +164,20 @@ it will be clipped away.
winding_t * winding_t *
ClipWinding(winding_t *in, const plane_t *split, bool keepon) ClipWinding(winding_t *in, const plane_t *split, bool keepon)
{ {
vec_t dists[MAX_POINTS_ON_WINDING]; vec_t dists[MAX_POINTS_ON_WINDING + 1];
int sides[MAX_POINTS_ON_WINDING]; int sides[MAX_POINTS_ON_WINDING + 1];
int counts[3]; int counts[3];
vec_t dot; vec_t fraction;
int i, j; int i, j;
vec_t *p1, *p2; vec_t *p1, *p2;
vec3_t mid; vec3_t mid;
winding_t *neww; winding_t *neww;
int maxpts; int maxpts;
if (in->numpoints > MAX_POINTS_ON_WINDING)
Error("Internal error: in->numpoints > MAX (%s: %d > %d)",
__func__, in->numpoints, MAX_POINTS_ON_WINDING);
CalcSides(in, split, sides, dists, counts); CalcSides(in, split, sides, dists, counts);
if (keepon && !counts[SIDE_FRONT] && !counts[SIDE_BACK]) if (keepon && !counts[SIDE_FRONT] && !counts[SIDE_BACK])
@ -187,20 +191,24 @@ ClipWinding(winding_t *in, const plane_t *split, bool keepon)
if (!counts[SIDE_BACK]) if (!counts[SIDE_BACK])
return in; return in;
maxpts = in->numpoints + 4; // can't use counts[0]+2 because /* can't use maxpoints = counts[0] + 2 because of fp grouping errors */
// of fp grouping errors maxpts = in->numpoints + 4;
neww = AllocMem(WINDING, maxpts, true); neww = AllocMem(WINDING, maxpts, true);
for (i = 0; i < in->numpoints; i++) { for (i = 0; i < in->numpoints; i++) {
p1 = in->points[i]; p1 = in->points[i];
if (sides[i] == SIDE_ON) { if (sides[i] == SIDE_ON) {
if (neww->numpoints == maxpts)
goto noclip;
VectorCopy(p1, neww->points[neww->numpoints]); VectorCopy(p1, neww->points[neww->numpoints]);
neww->numpoints++; neww->numpoints++;
continue; continue;
} }
if (sides[i] == SIDE_FRONT) { if (sides[i] == SIDE_FRONT) {
if (neww->numpoints == maxpts)
goto noclip;
VectorCopy(p1, neww->points[neww->numpoints]); VectorCopy(p1, neww->points[neww->numpoints]);
neww->numpoints++; neww->numpoints++;
} }
@ -208,31 +216,31 @@ ClipWinding(winding_t *in, const plane_t *split, bool keepon)
if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
continue; continue;
// generate a split point /* generate a split point */
p2 = in->points[(i + 1) % in->numpoints]; p2 = in->points[(i + 1) % in->numpoints];
fraction = dists[i] / (dists[i] - dists[i + 1]);
dot = dists[i] / (dists[i] - dists[i + 1]);
for (j = 0; j < 3; j++) { for (j = 0; j < 3; j++) {
// avoid round off error when possible /* avoid round off error when possible */
if (split->normal[j] == 1) if (split->normal[j] == 1)
mid[j] = split->dist; mid[j] = split->dist;
else if (split->normal[j] == -1) else if (split->normal[j] == -1)
mid[j] = -split->dist; mid[j] = -split->dist;
else else
mid[j] = p1[j] + dot * (p2[j] - p1[j]); mid[j] = p1[j] + fraction * (p2[j] - p1[j]);
} }
if (neww->numpoints == maxpts)
goto noclip;
VectorCopy(mid, neww->points[neww->numpoints]); VectorCopy(mid, neww->points[neww->numpoints]);
neww->numpoints++; neww->numpoints++;
} }
if (neww->numpoints > maxpts)
Error("Internal error: excess clipped points (%s)", __func__);
// free the original winding
FreeMem(in, WINDING, 1); FreeMem(in, WINDING, 1);
return neww; return neww;
noclip:
Error("Internal error: new->numpoints > MAX (%s: %d > %d)",
__func__, neww->numpoints, maxpts);
} }
@ -250,17 +258,19 @@ void
DivideWinding(winding_t *in, const plane_t *split, winding_t **front, DivideWinding(winding_t *in, const plane_t *split, winding_t **front,
winding_t **back) winding_t **back)
{ {
vec_t dists[MAX_POINTS_ON_WINDING]; vec_t dists[MAX_POINTS_ON_WINDING + 1];
int sides[MAX_POINTS_ON_WINDING]; int sides[MAX_POINTS_ON_WINDING + 1];
int counts[3]; int counts[3];
vec_t dot; vec_t fraction;
int i, j; int i, j;
vec_t *p1, *p2; vec_t *p1, *p2;
vec3_t mid; vec3_t mid;
winding_t *f, *b; winding_t *f, *b;
int maxpts; int maxpts;
counts[0] = counts[1] = counts[2] = 0; if (in->numpoints > MAX_POINTS_ON_WINDING)
Error("Internal error: in->numpoints > MAX (%s: %d > %d)",
__func__, in->numpoints, MAX_POINTS_ON_WINDING);
CalcSides(in, split, sides, dists, counts); CalcSides(in, split, sides, dists, counts);
@ -275,9 +285,8 @@ DivideWinding(winding_t *in, const plane_t *split, winding_t **front,
return; return;
} }
maxpts = in->numpoints + 4; // can't use counts[0]+2 because /* can't use maxpoints = counts[0] + 2 because of fp grouping errors */
// of fp grouping errors maxpts = in->numpoints + 4;
*front = f = AllocMem(WINDING, maxpts, true); *front = f = AllocMem(WINDING, maxpts, true);
*back = b = AllocMem(WINDING, maxpts, true); *back = b = AllocMem(WINDING, maxpts, true);
@ -285,18 +294,26 @@ DivideWinding(winding_t *in, const plane_t *split, winding_t **front,
p1 = in->points[i]; p1 = in->points[i];
if (sides[i] == SIDE_ON) { if (sides[i] == SIDE_ON) {
if (f->numpoints == maxpts)
goto noclip_front;
VectorCopy(p1, f->points[f->numpoints]); VectorCopy(p1, f->points[f->numpoints]);
f->numpoints++; f->numpoints++;
if (b->numpoints == maxpts)
goto noclip_back;
VectorCopy(p1, b->points[b->numpoints]); VectorCopy(p1, b->points[b->numpoints]);
b->numpoints++; b->numpoints++;
continue; continue;
} }
if (sides[i] == SIDE_FRONT) { if (sides[i] == SIDE_FRONT) {
if (f->numpoints == maxpts)
goto noclip_front;
VectorCopy(p1, f->points[f->numpoints]); VectorCopy(p1, f->points[f->numpoints]);
f->numpoints++; f->numpoints++;
} }
if (sides[i] == SIDE_BACK) { if (sides[i] == SIDE_BACK) {
if (b->numpoints == maxpts)
goto noclip_back;
VectorCopy(p1, b->points[b->numpoints]); VectorCopy(p1, b->points[b->numpoints]);
b->numpoints++; b->numpoints++;
} }
@ -304,27 +321,37 @@ DivideWinding(winding_t *in, const plane_t *split, winding_t **front,
if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
continue; continue;
// generate a split point /* generate a split point */
p2 = in->points[(i + 1) % in->numpoints]; p2 = in->points[(i + 1) % in->numpoints];
dot = dists[i] / (dists[i] - dists[i + 1]); fraction = dists[i] / (dists[i] - dists[i + 1]);
for (j = 0; j < 3; j++) { // avoid round off error when possible for (j = 0; j < 3; j++) {
/* avoid round off error when possible */
if (split->normal[j] == 1) if (split->normal[j] == 1)
mid[j] = split->dist; mid[j] = split->dist;
else if (split->normal[j] == -1) else if (split->normal[j] == -1)
mid[j] = -split->dist; mid[j] = -split->dist;
else else
mid[j] = p1[j] + dot * (p2[j] - p1[j]); mid[j] = p1[j] + fraction * (p2[j] - p1[j]);
} }
if (f->numpoints == maxpts)
goto noclip_front;
VectorCopy(mid, f->points[f->numpoints]); VectorCopy(mid, f->points[f->numpoints]);
f->numpoints++; f->numpoints++;
if (b->numpoints == maxpts)
goto noclip_back;
VectorCopy(mid, b->points[b->numpoints]); VectorCopy(mid, b->points[b->numpoints]);
b->numpoints++; b->numpoints++;
} }
return;
if (f->numpoints > maxpts || b->numpoints > maxpts) noclip_front:
Error("Internal error: excess clipped points (%s)", __func__); Error("Internal error: front->numpoints > MAX (%s: %d > %d)",
__func__, f->numpoints, maxpts);
noclip_back:
Error("Internal error: back->numpoints > MAX (%s: %d > %d)",
__func__, b->numpoints, maxpts);
} }

View File

@ -239,7 +239,7 @@ ClipStackWinding(winding_t *in, pstack_t *stack, plane_t *split)
vec_t dists[MAX_WINDING + 1]; vec_t dists[MAX_WINDING + 1];
int sides[MAX_WINDING + 1]; int sides[MAX_WINDING + 1];
int counts[3]; int counts[3];
vec_t dot; vec_t dot, fraction;
int i, j; int i, j;
vec_t *p1, *p2; vec_t *p1, *p2;
vec3_t mid; vec3_t mid;
@ -254,9 +254,13 @@ ClipStackWinding(winding_t *in, pstack_t *stack, plane_t *split)
return in; 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; counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point /* determine sides for each point */
for (i = 0; i < in->numpoints; i++) { for (i = 0; i < in->numpoints; i++) {
dot = DotProduct(in->points[i], split->normal); dot = DotProduct(in->points[i], split->normal);
dot -= split->dist; dot -= split->dist;
@ -288,20 +292,16 @@ ClipStackWinding(winding_t *in, pstack_t *stack, plane_t *split)
for (i = 0; i < in->numpoints; i++) { for (i = 0; i < in->numpoints; i++) {
p1 = in->points[i]; p1 = in->points[i];
if (neww->numpoints == MAX_WINDING_FIXED) {
/* Can't clip, fall back to original */
FreeStackWinding(neww, stack);
c_noclip++;
return in;
}
if (sides[i] == SIDE_ON) { if (sides[i] == SIDE_ON) {
VectorCopy(p1, neww->points[neww->numpoints]); if (neww->numpoints == MAX_WINDING_FIXED)
goto noclip;
neww->numpoints++; neww->numpoints++;
continue; continue;
} }
if (sides[i] == SIDE_FRONT) { if (sides[i] == SIDE_FRONT) {
if (neww->numpoints == MAX_WINDING_FIXED)
goto noclip;
VectorCopy(p1, neww->points[neww->numpoints]); VectorCopy(p1, neww->points[neww->numpoints]);
neww->numpoints++; neww->numpoints++;
} }
@ -309,34 +309,33 @@ ClipStackWinding(winding_t *in, pstack_t *stack, plane_t *split)
if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
continue; continue;
if (neww->numpoints == MAX_WINDING_FIXED) { /* generate a split point */
/* Can't clip, fall back to original */
FreeStackWinding(neww, stack);
c_noclip++;
return in;
}
// generate a split point
p2 = in->points[(i + 1) % in->numpoints]; p2 = in->points[(i + 1) % in->numpoints];
fraction = dists[i] / (dists[i] - dists[i + 1]);
dot = dists[i] / (dists[i] - dists[i + 1]); for (j = 0; j < 3; j++) {
for (j = 0; j < 3; j++) { // avoid round off error when possible /* avoid round off error when possible */
if (split->normal[j] == 1) if (split->normal[j] == 1)
mid[j] = split->dist; mid[j] = split->dist;
else if (split->normal[j] == -1) else if (split->normal[j] == -1)
mid[j] = -split->dist; mid[j] = -split->dist;
else else
mid[j] = p1[j] + dot * (p2[j] - p1[j]); mid[j] = p1[j] + fraction * (p2[j] - p1[j]);
} }
if (neww->numpoints == MAX_WINDING_FIXED)
goto noclip;
VectorCopy(mid, neww->points[neww->numpoints]); VectorCopy(mid, neww->points[neww->numpoints]);
neww->numpoints++; neww->numpoints++;
} }
FreeStackWinding(in, stack); FreeStackWinding(in, stack);
return neww; return neww;
}
noclip:
FreeStackWinding(neww, stack);
c_noclip++;
return in;
}
//============================================================================ //============================================================================