]> 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 57b5d81a81d610e7831b09c51be4842d3a381824..1093be3b4a11d49c58f6a7b52940c0b846191828 100644 (file)
@@ -1,6 +1,6 @@
 /* -------------------------------------------------------------------------------
 
-Copyright (C) 1999-2006 Id Software, Inc. and contributors.
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
 For a list of contributors, see the accompanying CONTRIBUTORS file.
 
 This file is part of GtkRadiant.
@@ -78,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;
 }
@@ -93,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 )
@@ -122,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;
@@ -134,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 );
@@ -156,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 );
@@ -173,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 );
        
@@ -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 Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_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 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++ )
@@ -378,14 +491,39 @@ qboolean CreateBrushWindings( brush_t *brush )
                        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 */
@@ -506,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]);