X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=tools%2Fquake3%2Fq3map2%2Fbrush.c;h=12754085cd675a91de7e65d621723de603a35b95;hb=94cb905da9c38e65cff6dcd9a56874c33c367df2;hp=2517ca63d210e28ed1778863fedf2b102800e8f7;hpb=6ce0c65e7716ae089adc3484cc2faaedd9e7730c;p=xonotic%2Fnetradiant.git diff --git a/tools/quake3/q3map2/brush.c b/tools/quake3/q3map2/brush.c index 2517ca63..12754085 100644 --- a/tools/quake3/q3map2/brush.c +++ b/tools/quake3/q3map2/brush.c @@ -277,6 +277,50 @@ void SnapWeldVector( vec3_t a, vec3_t b, vec3_t out ) } } +/* +================== +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]; + } + } +} + /* @@ -338,10 +382,71 @@ qboolean FixWinding( winding_t *w ) 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; +} /* @@ -353,7 +458,11 @@ returns false if the brush doesn't enclose a valid volume qboolean CreateBrushWindings( brush_t *brush ) { int i, j; +#if EXPERIMENTAL_HIGH_PRECISION_MATH_Q3MAP2_FIXES + winding_accu_t *w; +#else winding_t *w; +#endif side_t *side; plane_t *plane; @@ -366,7 +475,11 @@ qboolean CreateBrushWindings( brush_t *brush ) plane = &mapplanes[ side->planenum ]; /* make huge winding */ +#if EXPERIMENTAL_HIGH_PRECISION_MATH_Q3MAP2_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++ ) @@ -378,14 +491,39 @@ qboolean CreateBrushWindings( brush_t *brush ) if( brush->sides[ j ].bevel ) continue; plane = &mapplanes[ brush->sides[ j ].planenum ^ 1 ]; +#if EXPERIMENTAL_HIGH_PRECISION_MATH_Q3MAP2_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 EXPERIMENTAL_HIGH_PRECISION_MATH_Q3MAP2_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 EXPERIMENTAL_HIGH_PRECISION_MATH_Q3MAP2_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 */ @@ -506,6 +644,8 @@ void WriteBSPBrushMap( char *name, brush_t *list ) fprintf (f, "{\n"); for (i=0,s=list->sides ; inumsides ; 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]);