/* ================== CopyWindingAccuIncreaseSizeAndFreeOld ================== */ winding_accu_t *CopyWindingAccuIncreaseSizeAndFreeOld(winding_accu_t *w) { int i; winding_accu_t *c; if (!w) Error("CopyWindingAccuIncreaseSizeAndFreeOld: winding is NULL"); c = AllocWindingAccu(w->numpoints + 1); c->numpoints = w->numpoints; for (i = 0; i < c->numpoints; i++) { VectorCopyAccu(w->p[i], c->p[i]); } FreeWindingAccu(w); return c; }
qboolean CreateBrushWindings( brush_t *brush ){ int i, j; #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES winding_accu_t *w; #else winding_t *w; #endif side_t *side; plane_t *plane; /* walk the list of brush sides */ for ( i = 0; i < brush->numsides; i++ ) { /* get side and plane */ side = &brush->sides[ i ]; plane = &mapplanes[ side->planenum ]; /* make huge winding */ #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES w = BaseWindingForPlaneAccu( plane->normal, plane->dist ); #else w = BaseWindingForPlane( plane->normal, plane->dist ); #endif /* walk the list of brush sides */ for ( j = 0; j < brush->numsides && w != NULL; j++ ) { if ( i == j ) { continue; } if ( brush->sides[ j ].planenum == ( brush->sides[ i ].planenum ^ 1 ) ) { continue; /* back side clipaway */ } if ( brush->sides[ j ].bevel ) { continue; } plane = &mapplanes[ brush->sides[ j ].planenum ^ 1 ]; #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES ChopWindingInPlaceAccu( &w, plane->normal, plane->dist, 0 ); #else ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); // CLIP_EPSILON ); #endif /* ydnar: fix broken windings that would generate trifans */ #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES // I think it's better to FixWindingAccu() once after we chop with all planes // so that error isn't multiplied. There is nothing natural about welding // the points unless they are the final endpoints. ChopWindingInPlaceAccu() // is able to handle all kinds of degenerate windings. #else FixWinding( w ); #endif } /* set side winding */ #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES if ( w != NULL ) { FixWindingAccu( w ); if ( w->numpoints < 3 ) { FreeWindingAccu( w ); w = NULL; } } side->winding = ( w ? CopyWindingAccuToRegular( w ) : NULL ); if ( w ) { FreeWindingAccu( w ); } #else side->winding = w; #endif } /* find brush bounds */ return BoundBrush( brush ); }
/* ============= ChopWindingInPlaceAccu ============= */ void ChopWindingInPlaceAccu(winding_accu_t **inout, vec3_t normal, vec_t dist, vec_t crudeEpsilon) { vec_accu_t fineEpsilon; winding_accu_t *in; int counts[3]; int i, j; vec_accu_t dists[MAX_POINTS_ON_WINDING + 1]; int sides[MAX_POINTS_ON_WINDING + 1]; int maxpts; winding_accu_t *f; vec_accu_t *p1, *p2; vec_accu_t w; vec3_accu_t mid, normalAccu; // We require at least a very small epsilon. It's a good idea for several reasons. // First, we will be dividing by a potentially very small distance below. We don't // want that distance to be too small; otherwise, things "blow up" with little accuracy // due to the division. (After a second look, the value w below is in range (0,1), but // graininess problem remains.) Second, Having minimum epsilon also prevents the following // situation. Say for example we have a perfect octagon defined by the input winding. // Say our chopping plane (defined by normal and dist) is essentially the same plane // that the octagon is sitting on. Well, due to rounding errors, it may be that point // 1 of the octagon might be in front, point 2 might be in back, point 3 might be in // front, point 4 might be in back, and so on. So we could end up with a very ugly- // looking chopped winding, and this might be undesirable, and would at least lead to // a possible exhaustion of MAX_POINTS_ON_WINDING. It's better to assume that points // very very close to the plane are on the plane, using an infinitesimal epsilon amount. // Now, the original ChopWindingInPlace() function used a vec_t-based winding_t. // So this minimum epsilon is quite similar to casting the higher resolution numbers to // the lower resolution and comparing them in the lower resolution mode. We explicitly // choose the minimum epsilon as something around the vec_t epsilon of one because we // want the resolution of vec_accu_t to have a large resolution around the epsilon. // Some of that leftover resolution even goes away after we scale to points far away. // Here is a further discussion regarding the choice of smallestEpsilonAllowed. // In the 32 float world (we can assume vec_t is that), the "epsilon around 1.0" is // 0.00000011921. In the 64 bit float world (we can assume vec_accu_t is that), the // "epsilon around 1.0" is 0.00000000000000022204. (By the way these two epsilons // are defined as VEC_SMALLEST_EPSILON_AROUND_ONE VEC_ACCU_SMALLEST_EPSILON_AROUND_ONE // respectively.) If you divide the first by the second, you get approximately // 536,885,246. Dividing that number by 200,000 (a typical base winding coordinate) // gives 2684. So in other words, if our smallestEpsilonAllowed was chosen as exactly // VEC_SMALLEST_EPSILON_AROUND_ONE, you would be guaranteed at least 2000 "ticks" in // 64-bit land inside of the epsilon for all numbers we're dealing with. static const vec_accu_t smallestEpsilonAllowed = ((vec_accu_t) VEC_SMALLEST_EPSILON_AROUND_ONE) * 0.5; if (crudeEpsilon < smallestEpsilonAllowed) fineEpsilon = smallestEpsilonAllowed; else fineEpsilon = (vec_accu_t) crudeEpsilon; in = *inout; counts[0] = counts[1] = counts[2] = 0; VectorCopyRegularToAccu(normal, normalAccu); for (i = 0; i < in->numpoints; i++) { dists[i] = DotProductAccu(in->p[i], normalAccu) - dist; if (dists[i] > fineEpsilon) sides[i] = SIDE_FRONT; else if (dists[i] < -fineEpsilon) sides[i] = SIDE_BACK; else sides[i] = SIDE_ON; counts[sides[i]]++; } sides[i] = sides[0]; dists[i] = dists[0]; // I'm wondering if whatever code that handles duplicate planes is robust enough // that we never get a case where two nearly equal planes result in 2 NULL windings // due to the 'if' statement below. TODO: Investigate this. if (!counts[SIDE_FRONT]) { FreeWindingAccu(in); *inout = NULL; return; } if (!counts[SIDE_BACK]) { return; // Winding is unmodified. } // NOTE: The least number of points that a winding can have at this point is 2. // In that case, one point is SIDE_FRONT and the other is SIDE_BACK. maxpts = counts[SIDE_FRONT] + 2; // We dynamically expand if this is too small. f = AllocWindingAccu(maxpts); for (i = 0; i < in->numpoints; i++) { p1 = in->p[i]; if (sides[i] == SIDE_ON || sides[i] == SIDE_FRONT) { if (f->numpoints >= MAX_POINTS_ON_WINDING) Error("ChopWindingInPlaceAccu: MAX_POINTS_ON_WINDING"); if (f->numpoints >= maxpts) // This will probably never happen. { Sys_FPrintf(SYS_VRB, "WARNING: estimate on chopped winding size incorrect (no problem)\n"); f = CopyWindingAccuIncreaseSizeAndFreeOld(f); maxpts++; } VectorCopyAccu(p1, f->p[f->numpoints]); f->numpoints++; if (sides[i] == SIDE_ON) continue; } if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) { continue; } // Generate a split point. p2 = in->p[((i + 1) == in->numpoints) ? 0 : (i + 1)]; // The divisor's absolute value is greater than the dividend's absolute value. // w is in the range (0,1). w = dists[i] / (dists[i] - dists[i + 1]); for (j = 0; j < 3; j++) { // Avoid round-off error when possible. Check axis-aligned normal. if (normal[j] == 1) mid[j] = dist; else if (normal[j] == -1) mid[j] = -dist; else mid[j] = p1[j] + (w * (p2[j] - p1[j])); } if (f->numpoints >= MAX_POINTS_ON_WINDING) Error("ChopWindingInPlaceAccu: MAX_POINTS_ON_WINDING"); if (f->numpoints >= maxpts) // This will probably never happen. { Sys_FPrintf(SYS_VRB, "WARNING: estimate on chopped winding size incorrect (no problem)\n"); f = CopyWindingAccuIncreaseSizeAndFreeOld(f); maxpts++; } VectorCopyAccu(mid, f->p[f->numpoints]); f->numpoints++; } FreeWindingAccu(in); *inout = f; }