]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - curves.c
curves.c and .h: Remove whitespace at the top of both files
[xonotic/darkplaces.git] / curves.c
index e4ef52ca1ca997c2ded8fe40953de196a756de8b..1d9b2941934ebcc0ecb947f6dfce267a01dd3654 100644 (file)
--- a/curves.c
+++ b/curves.c
@@ -1,6 +1,5 @@
-
 /*
-this code written by Forest Hale, on 2004-10-17, and placed into public domain
+this code written by Ashley Rose Hale (LadyHavoc), on 2004-10-17, and placed into public domain
 this implements Quadratic BSpline surfaces as seen in Quake3 by id Software
 
 a small rant on misuse of the name 'bezier': many people seem to think that
@@ -136,17 +135,18 @@ void Q3PatchTesselateFloat(int numcomponents, int outputstride, float *outputver
 #endif
 }
 
-static int Q3PatchTesselation(float bestsquareddeviation, float tolerance)
+static int Q3PatchTesselation(float largestsquared3xcurvearea, float tolerance)
 {
        float f;
-       f = sqrt(bestsquareddeviation) / tolerance;
-       //if(f < 0.25) // REALLY flat patches
+       // f is actually a squared 2x curve area... so the formula had to be adjusted to give roughly the same subdivisions
+       f = pow(largestsquared3xcurvearea / 64.0f, 0.25f) / tolerance;
+       //if(f < 0.25) // VERY flat patches
        if(f < 0.0001) // TOTALLY flat patches
                return 0;
        else if(f < 2)
                return 1;
        else
-               return (int) floor(log(f) / log(2)) + 1;
+               return (int) floor(log(f) / log(2.0f)) + 1;
                // this is always at least 2
                // maps [0.25..0.5[ to -1 (actually, 1 is returned)
                // maps [0.5..1[ to 0 (actually, 1 is returned)
@@ -155,52 +155,117 @@ static int Q3PatchTesselation(float bestsquareddeviation, float tolerance)
                // maps [4..8[ to 4
 }
 
+static float Squared3xCurveArea(const float *a, const float *control, const float *b, int components)
+{
+#if 0
+       // mimicing the old behaviour with the new code...
+
+       float deviation;
+       float quartercurvearea = 0;
+       int c;
+       for (c = 0;c < components;c++)
+       {
+               deviation = control[c] * 0.5f - a[c] * 0.25f - b[c] * 0.25f;
+               quartercurvearea += deviation*deviation;
+       }
+
+       // But as the new code now works on the squared 2x curve area, let's scale the value
+       return quartercurvearea * quartercurvearea * 64.0;
+
+#else
+       // ideally, we'd like the area between the spline a->control->b and the line a->b.
+       // but as this is hard to calculate, let's calculate an upper bound of it:
+       // the area of the triangle a->control->b->a.
+       //
+       // one can prove that the area of a quadratic spline = 2/3 * the area of
+       // the triangle of its control points!
+       // to do it, first prove it for the spline through (0,0), (1,1), (2,0)
+       // (which is a parabola) and then note that moving the control point
+       // left/right is just shearing and keeps the area of both the spline and
+       // the triangle invariant.
+       //
+       // why are we going for the spline area anyway?
+       // we know that:
+       //
+       //   the area between the spline and the line a->b is a measure of the
+       //   error of approximation of the spline by the line.
+       //
+       //   also, on circle-like or parabola-like curves, you easily get that the
+       //   double amount of line approximation segments reduces the error to its quarter
+       //   (also, easy to prove for splines by doing it for one specific one, and using
+       //   affine transforms to get all other splines)
+       //
+       // so...
+       //
+       // let's calculate the area! but we have to avoid the cross product, as
+       // components is not necessarily 3
+       //
+       // the area of a triangle spanned by vectors a and b is
+       //
+       // 0.5 * |a| |b| sin gamma
+       //
+       // now, cos gamma is
+       //
+       // a.b / (|a| |b|)
+       //
+       // so the area is
+       // 
+       // 0.5 * sqrt(|a|^2 |b|^2 - (a.b)^2)
+       int c;
+       float aa = 0, bb = 0, ab = 0;
+       for (c = 0;c < components;c++)
+       {
+               float xa = a[c] - control[c];
+               float xb = b[c] - control[c];
+               aa += xa * xa;
+               ab += xa * xb;
+               bb += xb * xb;
+       }
+       // area is 0.5 * sqrt(aa*bb - ab*ab)
+       // 2x TRIANGLE area is sqrt(aa*bb - ab*ab)
+       // 3x CURVE area is sqrt(aa*bb - ab*ab)
+       return aa * bb - ab * ab;
+#endif
+}
+
 // returns how much tesselation of each segment is needed to remain under tolerance
 int Q3PatchTesselationOnX(int patchwidth, int patchheight, int components, const float *in, float tolerance)
 {
-       int c, x, y;
+       int x, y;
        const float *patch;
-       float deviation, squareddeviation, bestsquareddeviation;
-       bestsquareddeviation = 0;
+       float squared3xcurvearea, largestsquared3xcurvearea;
+       largestsquared3xcurvearea = 0;
        for (y = 0;y < patchheight;y++)
        {
                for (x = 0;x < patchwidth-1;x += 2)
                {
-                       squareddeviation = 0;
-                       for (c = 0, patch = in + ((y * patchwidth) + x) * components;c < components;c++, patch++)
-                       {
-                               deviation = patch[components] * 0.5f - patch[0] * 0.25f - patch[2*components] * 0.25f;
-                               squareddeviation += deviation*deviation;
-                       }
-                       if (bestsquareddeviation < squareddeviation)
-                               bestsquareddeviation = squareddeviation;
+                       patch = in + ((y * patchwidth) + x) * components;
+                       squared3xcurvearea = Squared3xCurveArea(&patch[0], &patch[components], &patch[2*components], components);
+                       if (largestsquared3xcurvearea < squared3xcurvearea)
+                               largestsquared3xcurvearea = squared3xcurvearea;
                }
        }
-       return Q3PatchTesselation(bestsquareddeviation, tolerance);
+       return Q3PatchTesselation(largestsquared3xcurvearea, tolerance);
 }
 
 // returns how much tesselation of each segment is needed to remain under tolerance
 int Q3PatchTesselationOnY(int patchwidth, int patchheight, int components, const float *in, float tolerance)
 {
-       int c, x, y;
+       int x, y;
        const float *patch;
-       float deviation, squareddeviation, bestsquareddeviation;
-       bestsquareddeviation = 0;
+       float squared3xcurvearea, largestsquared3xcurvearea;
+       largestsquared3xcurvearea = 0;
        for (y = 0;y < patchheight-1;y += 2)
        {
                for (x = 0;x < patchwidth;x++)
                {
-                       squareddeviation = 0;
-                       for (c = 0, patch = in + ((y * patchwidth) + x) * components;c < components;c++, patch++)
-                       {
-                               deviation = patch[patchwidth*components] * 0.5f - patch[0] * 0.25f - patch[2*patchwidth*components] * 0.25f;
-                               squareddeviation += deviation*deviation;
-                       }
-                       if (bestsquareddeviation < squareddeviation)
-                               bestsquareddeviation = squareddeviation;
+                       patch = in + ((y * patchwidth) + x) * components;
+                       squared3xcurvearea = Squared3xCurveArea(&patch[0], &patch[patchwidth*components], &patch[2*patchwidth*components], components);
+                       if (largestsquared3xcurvearea < squared3xcurvearea)
+                               largestsquared3xcurvearea = squared3xcurvearea;
                }
        }
-       return Q3PatchTesselation(bestsquareddeviation, tolerance);
+       return Q3PatchTesselation(largestsquared3xcurvearea, tolerance);
 }
 
 // Find an equal vertex in array. Check only vertices with odd X and Y
@@ -211,7 +276,7 @@ static int FindEqualOddVertexInArray(int numcomponents, float *vertex, float *ve
        {
                for (x=0; x<width; x+=2)
                {
-                       qboolean found = true;
+                       qbool found = true;
                        for (j=0; j<numcomponents; j++)
                                if (fabs(*(vertex+j) - *(vertices+j)) > 0.05)
                                // div0: this is notably smaller than the smallest radiant grid
@@ -270,8 +335,8 @@ int Q3PatchAdjustTesselation(int numcomponents, patchinfo_t *patch1, float *patc
 
        struct {int id1,id2;} commonverts[8];
        int i, j, k, side1, side2, *tess1, *tess2;
-       int dist1, dist2;
-       qboolean modified = false;
+       int dist1 = 0, dist2 = 0;
+       qbool modified = false;
 
        // Potential paired vertices (corners of the first patch)
        commonverts[0].id1 = 0;
@@ -336,18 +401,38 @@ void Q3PatchTriangleElements(int *elements, int width, int height, int firstvert
        int x, y, row0, row1;
        for (y = 0;y < height - 1;y++)
        {
-               row0 = firstvertex + (y + 0) * width;
-               row1 = firstvertex + (y + 1) * width;
-               for (x = 0;x < width - 1;x++)
+               if(y % 2)
+               {
+                       // swap the triangle order in odd rows as optimization for collision stride
+                       row0 = firstvertex + (y + 0) * width + width - 2;
+                       row1 = firstvertex + (y + 1) * width + width - 2;
+                       for (x = 0;x < width - 1;x++)
+                       {
+                               *elements++ = row1;
+                               *elements++ = row1 + 1;
+                               *elements++ = row0 + 1;
+                               *elements++ = row0;
+                               *elements++ = row1;
+                               *elements++ = row0 + 1;
+                               row0--;
+                               row1--;
+                       }
+               }
+               else
                {
-                       *elements++ = row0;
-                       *elements++ = row1;
-                       *elements++ = row0 + 1;
-                       *elements++ = row1;
-                       *elements++ = row1 + 1;
-                       *elements++ = row0 + 1;
-                       row0++;
-                       row1++;
+                       row0 = firstvertex + (y + 0) * width;
+                       row1 = firstvertex + (y + 1) * width;
+                       for (x = 0;x < width - 1;x++)
+                       {
+                               *elements++ = row0;
+                               *elements++ = row1;
+                               *elements++ = row0 + 1;
+                               *elements++ = row1;
+                               *elements++ = row1 + 1;
+                               *elements++ = row0 + 1;
+                               row0++;
+                               row1++;
+                       }
                }
        }
 }