-/*
+/* -------------------------------------------------------------------------------
+
Copyright (C) 1999-2007 id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
/* count brushes */
- for( brushes; brushes != NULL; brushes = brushes->next )
+ for( ; brushes != NULL; brushes = brushes->next )
c++;
return c;
}
brush_t *AllocBrush( int numSides )
{
brush_t *bb;
- int c;
+ size_t c;
/* allocate and clear */
if( numSides <= 0 )
Error( "AllocBrush called with numsides = %d", numSides );
- c = (int) &(((brush_t*) 0)->sides[ numSides ]);
+ c = (size_t)&(((brush_t*) 0)->sides[ numSides ]);
bb = safe_malloc( c );
memset( bb, 0, c );
if( numthreads == 1 )
/* error check */
- if( *((int*) b) == 0xFEFEFEFE )
+ if( *((unsigned int*) b) == 0xFEFEFEFE )
{
Sys_FPrintf( SYS_VRB, "WARNING: Attempt to free an already freed brush!\n" );
return;
FreeWinding( b->sides[ i ].winding );
/* ydnar: overwrite it */
- memset( b, 0xFE, (int) &(((brush_t*) 0)->sides[ b->numsides ]) );
- *((int*) b) = 0xFEFEFEFE;
+ memset( b, 0xFE, (size_t)&(((brush_t*) 0)->sides[ b->numsides ]) );
+ *((unsigned int*) b) = 0xFEFEFEFE;
/* free it */
free( b );
/* walk brush list */
- for( brushes; brushes != NULL; brushes = next )
+ for( ; brushes != NULL; brushes = next )
{
next = brushes->next;
FreeBrush( brushes );
brush_t *CopyBrush( brush_t *brush )
{
brush_t *newBrush;
- int size;
+ size_t size;
int i;
/* copy brush */
- size = (int) &(((brush_t*) 0)->sides[ brush->numsides ]);
+ size = (size_t)&(((brush_t*) 0)->sides[ brush->numsides ]);
newBrush = AllocBrush( brush->numsides );
memcpy( newBrush, brush, size );
}
}
+/*
+==================
+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];
+ }
+ }
+}
+
/*
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++ )
continue; /* back side clipaway */
if( brush->sides[ j ].bevel )
continue;
- if( brush->sides[ j ].backSide )
- 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 = BaseWindingForPlane (plane->normal, plane->dist);
for (i=0 ; i<brush->numsides && w ; i++)
{
- if ( brush->sides[i].backSide ) {
- continue; // fake back-sided polygons never split
- }
plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
}
// see if we have valid polygons on both sides
for (i=0 ; i<2 ; i++)
{
- BoundBrush (b[i]);
- for (j=0 ; j<3 ; j++)
+ if (b[i]->numsides < 3 || !BoundBrush (b[i]))
{
- if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD)
- {
+ if (b[i]->numsides >= 3)
Sys_FPrintf (SYS_VRB,"bogus brush after clip\n");
- break;
- }
- }
-
- if (b[i]->numsides < 3 || j < 3)
- {
FreeBrush (b[i]);
b[i] = NULL;
}