]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake3/q3map2/brush.c
Renaming #define constants:
[xonotic/netradiant.git] / tools / quake3 / q3map2 / brush.c
index 28ade081bb46e0fcd05bf9e3625827464ba7099e..1093be3b4a11d49c58f6a7b52940c0b846191828 100644 (file)
@@ -1,4 +1,5 @@
-/*
+/* -------------------------------------------------------------------------------
+
 Copyright (C) 1999-2007 id Software, Inc. and contributors.
 For a list of contributors, see the accompanying CONTRIBUTORS file.
 
@@ -77,7 +78,7 @@ int   CountBrushList( brush_t *brushes )
        
        
        /* count brushes */
-       for( brushes; brushes != NULL; brushes = brushes->next )
+       for( ; brushes != NULL; brushes = brushes->next )
                c++;
        return c;
 }
@@ -92,13 +93,13 @@ allocates a new brush
 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 )
@@ -121,7 +122,7 @@ void FreeBrush( brush_t *b )
        
        
        /* 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;
@@ -133,8 +134,8 @@ void FreeBrush( brush_t *b )
                        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 );
@@ -155,7 +156,7 @@ void FreeBrushList( brush_t *brushes )
        
        
        /* walk brush list */
-       for( brushes; brushes != NULL; brushes = next )
+       for( ; brushes != NULL; brushes = next )
        {
                next = brushes->next;
                FreeBrush( brushes );
@@ -172,12 +173,12 @@ duplicates the brush, sides, and windings
 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 );
        
@@ -276,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];
+               }
+       }
+}
+
 
 
 /*
@@ -337,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;
+}
 
 
 /*
@@ -352,7 +458,11 @@ returns false if the brush doesn't enclose a valid volume
 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;
        
@@ -365,7 +475,11 @@ qboolean CreateBrushWindings( brush_t *brush )
                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++ )
@@ -376,17 +490,40 @@ qboolean CreateBrushWindings( brush_t *brush )
                                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 */
@@ -507,6 +644,8 @@ void WriteBSPBrushMap( char *name, brush_t *list )
                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]);
@@ -850,9 +989,6 @@ void SplitBrush( brush_t *brush, int planenum, brush_t **front, brush_t **back )
        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);
        }
@@ -910,18 +1046,10 @@ void SplitBrush( brush_t *brush, int planenum, brush_t **front, brush_t **back )
        // 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;
                }