}
}
+/*
+==================
+SnapWeldVectorAccu
+
+Welds two vectors into a third, taking into account nearest-to-integer
+instead of averaging.
+==================
+*/
+void SnapWeldVectorAccu(vec3_accu_t a, vec3_accu_t b, vec3_accu_t out)
+{
+ // I'm just preserving what I think was the intended logic of the original
+ // SnapWeldVector(). I'm not actually sure where this function should even
+ // be used. I'd like to know which kinds of problems this function addresses.
+
+ // TODO: I thought we're snapping all coordinates to nearest 1/8 unit?
+ // So what is natural about snapping to the nearest integer? Maybe we should
+ // be snapping to the nearest 1/8 unit instead?
+ int i;
+ vec_accu_t ai, bi, ad, bd;
+
+ if (a == NULL || b == NULL || out == NULL)
+ Error("SnapWeldVectorAccu: NULL argument");
+
+ for (i = 0; i < 3; i++)
+ {
+ ai = Q_rintAccu(a[i]);
+ bi = Q_rintAccu(b[i]);
+ ad = fabs(ai - a[i]);
+ bd = fabs(bi - b[i]);
+
+ if (ad < bd)
+ {
+ if (ad < SNAP_EPSILON) out[i] = ai;
+ else out[i] = a[i];
+ }
+ else
+ {
+ if (bd < SNAP_EPSILON) out[i] = bi;
+ else out[i] = b[i];
+ }
+ }
+}
/*
FixWinding() - ydnar
return valid;
}
+/*
+==================
+FixWindingAccu
+Removes degenerate edges (edges that are too short) from a winding.
+Returns qtrue if the winding has been altered by this function.
+Returns qfalse if the winding is untouched by this function.
+It's advised that you check the winding after this function exits to make
+sure it still has at least 3 points. If that is not the case, the winding
+cannot be considered valid. The winding may degenerate to one or two points
+if the some of the winding's points are close together.
+==================
+*/
+qboolean FixWindingAccu(winding_accu_t *w)
+{
+ int i, j, k;
+ vec3_accu_t vec;
+ vec_accu_t dist;
+ qboolean done, altered;
+ if (w == NULL) Error("FixWindingAccu: NULL argument");
+ altered = qfalse;
+
+ while (qtrue)
+ {
+ if (w->numpoints < 2) break; // Don't remove the only remaining point.
+ done = qtrue;
+ for (i = 0; i < w->numpoints; i++)
+ {
+ j = (((i + 1) == w->numpoints) ? 0 : (i + 1));
+
+ VectorSubtractAccu(w->p[i], w->p[j], vec);
+ dist = VectorLengthAccu(vec);
+ if (dist < DEGENERATE_EPSILON)
+ {
+ // TODO: I think the "snap weld vector" was written before
+ // some of the math precision fixes, and its purpose was
+ // probably to address math accuracy issues. We can think
+ // about changing the logic here. Maybe once plane distance
+ // gets 64 bits, we can look at it then.
+ SnapWeldVectorAccu(w->p[i], w->p[j], vec);
+ VectorCopyAccu(vec, w->p[i]);
+ for (k = j + 1; k < w->numpoints; k++)
+ {
+ VectorCopyAccu(w->p[k], w->p[k - 1]);
+ }
+ w->numpoints--;
+ altered = qtrue;
+ // The only way to finish off fixing the winding consistently and
+ // accurately is by fixing the winding all over again. For example,
+ // the point at index i and the point at index i-1 could now be
+ // less than the epsilon distance apart. There are too many special
+ // case problems we'd need to handle if we didn't start from the
+ // beginning.
+ done = qfalse;
+ break; // This will cause us to return to the "while" loop.
+ }
+ }
+ if (done) break;
+ }
+
+ return altered;
+}
/*
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;
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( 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 */
fprintf (f, "{\n");
for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
{
+ // TODO: See if we can use a smaller winding to prevent resolution loss.
+ // Is WriteBSPBrushMap() used only to decompile maps?
w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
w = s->winding;
if (!w)
continue;
- ClipWindingEpsilon (w, plane->normal, plane->dist,
- 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
+ ClipWindingEpsilonStrict (w, plane->normal, plane->dist,
+ 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); /* strict, in parallel case we get the face back because it also is the midwinding */
for (j=0 ; j<2 ; j++)
{
if (!cw[j])