]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - collision.c
fixed black fullbrights on models in realtime mode
[xonotic/darkplaces.git] / collision.c
index c05dcbcb77bd49a5645b3175c76c321c7f591e38..e069fd8caa1f8c06d8e2895b6906b53b3aee3da9 100644 (file)
@@ -2,6 +2,10 @@
 #include "quakedef.h"
 #include "winding.h"
 
+// 1/32 epsilon to keep floating point happy
+#define DIST_EPSILON (0.03125)
+
+#if 0
 typedef struct
 {
        // the hull we're tracing through
@@ -22,9 +26,6 @@ typedef struct
 }
 RecursiveHullCheckTraceInfo_t;
 
-// 1/32 epsilon to keep floating point happy
-#define DIST_EPSILON (0.03125)
-
 #define HULLCHECKSTATE_EMPTY 0
 #define HULLCHECKSTATE_SOLID 1
 #define HULLCHECKSTATE_DONE 2
@@ -48,26 +49,32 @@ loc0:
        // check for empty
        if (num < 0)
        {
-               // translate the fake CONTENTS values in the box bsp tree
-               if (num == CONTENTS_SOLID)
-                       num = t->boxsupercontents;
-               else
-                       num = 0;
+               num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
                if (!t->trace->startfound)
                {
                        t->trace->startfound = true;
                        t->trace->startsupercontents |= num;
                }
+               if (num & SUPERCONTENTS_LIQUIDSMASK)
+                       t->trace->inwater = true;
+               if (num == 0)
+                       t->trace->inopen = true;
                if (num & t->trace->hitsupercontentsmask)
                {
                        // if the first leaf is solid, set startsolid
                        if (t->trace->allsolid)
                                t->trace->startsolid = true;
+#if COLLISIONPARANOID >= 3
+                       Con_Printf("S");
+#endif
                        return HULLCHECKSTATE_SOLID;
                }
                else
                {
                        t->trace->allsolid = false;
+#if COLLISIONPARANOID >= 3
+                       Con_Printf("E");
+#endif
                        return HULLCHECKSTATE_EMPTY;
                }
        }
@@ -91,6 +98,9 @@ loc0:
        {
                if (t2 < 0)
                {
+#if COLLISIONPARANOID >= 3
+                       Con_Printf("<");
+#endif
                        num = node->children[1];
                        goto loc0;
                }
@@ -100,6 +110,9 @@ loc0:
        {
                if (t2 >= 0)
                {
+#if COLLISIONPARANOID >= 3
+                       Con_Printf(">");
+#endif
                        num = node->children[0];
                        goto loc0;
                }
@@ -108,6 +121,9 @@ loc0:
 
        // the line intersects, find intersection point
        // LordHavoc: this uses the original trace for maximum accuracy
+#if COLLISIONPARANOID >= 3
+       Con_Printf("M");
+#endif
        if (plane->type < 3)
        {
                t1 = t->start[plane->type] - plane->dist;
@@ -146,15 +162,19 @@ loc0:
                VectorCopy (plane->normal, t->trace->plane.normal);
        }
 
-       // bias away from surface a bit
-       t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
-       t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
-
+       // calculate the true fraction
+       t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist;
+       t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist;
        midf = t1 / (t1 - t2);
-       t->trace->fraction = bound(0.0f, midf, 1.0);
+       t->trace->realfraction = bound(0, midf, 1);
 
-       VectorMA(t->start, t->trace->fraction, t->dist, t->trace->endpos);
+       // calculate the return fraction which is nudged off the surface a bit
+       midf = (t1 - DIST_EPSILON) / (t1 - t2);
+       t->trace->fraction = bound(0, midf, 1);
 
+#if COLLISIONPARANOID >= 3
+       Con_Printf("D");
+#endif
        return HULLCHECKSTATE_DONE;
 }
 
@@ -203,7 +223,7 @@ static hull_t box_hull;
 static dclipnode_t box_clipnodes[6];
 static mplane_t box_planes[6];
 
-void Collision_Init (void)
+void Mod_Q1BSP_Collision_Init (void)
 {
        int             i;
        int             side;
@@ -254,13 +274,21 @@ void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cm
        rhc.trace = trace;
        rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
        rhc.trace->fraction = 1;
+       rhc.trace->realfraction = 1;
        rhc.trace->allsolid = true;
        VectorCopy(start, rhc.start);
        VectorCopy(end, rhc.end);
        VectorSubtract(rhc.end, rhc.start, rhc.dist);
-       RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
+       Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
+       VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos);
+       if (rhc.trace->startsupercontents)
+               rhc.trace->startsupercontents = boxsupercontents;
 }
+#endif
 
+void Collision_Init (void)
+{
+}
 
 
 
@@ -373,7 +401,6 @@ colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalpla
        colplanef_t planesbuf[256];
        int elementsbuf[1024];
        int polypointbuf[256];
-       float mins[3], maxs[3];
        // construct a collision brush (points, planes, and renderable mesh) from
        // a set of planes, this also optimizes out any unnecessary planes (ones
        // whose polygon is clipped away by the other planes)
@@ -481,25 +508,6 @@ colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalpla
                numplanes++;
        }
 
-       // recalc distances
-       for (j = 0;j < numplanes;j++)
-               planesbuf[j].dist = furthestplanedist_float(planesbuf[j].normal, pointsbuf, numpoints);
-
-       if (numpoints)
-       {
-               VectorCopy(pointsbuf[0].v, mins);
-               VectorCopy(pointsbuf[0].v, maxs);
-               for (j = 1;j < numpoints;j++)
-               {
-                       mins[0] = min(mins[0], pointsbuf[j].v[0]);
-                       mins[1] = min(mins[1], pointsbuf[j].v[1]);
-                       mins[2] = min(mins[2], pointsbuf[j].v[2]);
-                       maxs[0] = max(maxs[0], pointsbuf[j].v[0]);
-                       maxs[1] = max(maxs[1], pointsbuf[j].v[1]);
-                       maxs[2] = max(maxs[2], pointsbuf[j].v[2]);
-               }
-       }
-
        // if nothing is left, there's nothing to allocate
        if (numtriangles < 4 || numplanes < 4 || numpoints < 4)
                return NULL;
@@ -509,8 +517,20 @@ colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalpla
        memcpy(brush->points, pointsbuf, numpoints * sizeof(colpointf_t));
        memcpy(brush->planes, planesbuf, numplanes * sizeof(colplanef_t));
        memcpy(brush->elements, elementsbuf, numtriangles * sizeof(int[3]));
-       VectorCopy(mins, brush->mins);
-       VectorCopy(maxs, brush->maxs);
+       // recalc distances
+       for (j = 0;j < brush->numplanes;j++)
+               brush->planes[j].dist = furthestplanedist_float(brush->planes[j].normal, brush->points, brush->numpoints);
+       VectorCopy(brush->points[0].v, brush->mins);
+       VectorCopy(brush->points[0].v, brush->maxs);
+       for (j = 1;j < brush->numpoints;j++)
+       {
+               brush->mins[0] = min(brush->mins[0], brush->points[j].v[0]);
+               brush->mins[1] = min(brush->mins[1], brush->points[j].v[1]);
+               brush->mins[2] = min(brush->mins[2], brush->points[j].v[2]);
+               brush->maxs[0] = max(brush->maxs[0], brush->points[j].v[0]);
+               brush->maxs[1] = max(brush->maxs[1], brush->points[j].v[1]);
+               brush->maxs[2] = max(brush->maxs[2], brush->points[j].v[2]);
+       }
        Collision_ValidateBrush(brush);
        return brush;
 }
@@ -680,10 +700,11 @@ colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, i
 void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, const colbrushf_t *thatbrush_start, const colbrushf_t *thatbrush_end)
 {
        int nplane, nplane2, fstartsolid, fendsolid, brushsolid;
-       float enterfrac, leavefrac, d1, d2, f, newimpactnormal[3];
+       float enterfrac, leavefrac, d1, d2, f, newimpactnormal[3], enterfrac2;
        const colplanef_t *startplane, *endplane;
 
        enterfrac = -1;
+       enterfrac2 = -1;
        leavefrac = 1;
        fstartsolid = true;
        fendsolid = true;
@@ -742,11 +763,11 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush
                                continue;
                        // enter
                        fstartsolid = false;
-                       f = (d1 - COLLISIONEPSILON) / f;
-                       f = bound(0, f, 1);
+                       f = d1 / (d1 - d2);
                        if (enterfrac < f)
                        {
                                enterfrac = f;
+                               enterfrac2 = (d1 - COLLISIONEPSILON) / (d1 - d2);
                                VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal);
                        }
                }
@@ -759,8 +780,7 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush
                                continue;
                        // leave
                        fendsolid = false;
-                       f = (d1 + COLLISIONEPSILON) / f;
-                       f = bound(0, f, 1);
+                       f = d1 / f;
                        if (leavefrac > f)
                                leavefrac = f;
                }
@@ -782,9 +802,10 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush
        // penetrating line segment is normally zero length if this brush was
        // generated from a polygon (infinitely thin), and could even be slightly
        // positive or negative due to rounding errors in that case.
-       if (brushsolid && enterfrac > -1 && enterfrac < trace->fraction && enterfrac - (1.0f / 1024.0f) <= leavefrac)
+       if (brushsolid && enterfrac > -1 && enterfrac < trace->realfraction && enterfrac - (1.0f / 1024.0f) <= leavefrac)
        {
-               trace->fraction = bound(0, enterfrac, 1);
+               trace->realfraction = bound(0, enterfrac, 1);
+               trace->fraction = bound(0, enterfrac2, 1);  
                VectorCopy(newimpactnormal, trace->plane.normal);
        }
 }
@@ -793,10 +814,11 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush
 void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const colbrushf_t *thatbrush_start, const colbrushf_t *thatbrush_end)
 {
        int nplane, fstartsolid, fendsolid, brushsolid;
-       float enterfrac, leavefrac, d1, d2, f, newimpactnormal[3];
+       float enterfrac, leavefrac, d1, d2, f, newimpactnormal[3], enterfrac2;
        const colplanef_t *startplane, *endplane;
 
        enterfrac = -1;
+       enterfrac2 = -1;
        leavefrac = 1;
        fstartsolid = true;
        fendsolid = true;
@@ -806,7 +828,7 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const
                startplane = thatbrush_start->planes + nplane;
                endplane = thatbrush_end->planes + nplane;
                d1 = DotProduct(startplane->normal, linestart) - startplane->dist;
-               d2 = DotProduct(endplane->normal, lineend) - endplane->dist - COLLISIONEPSILON2;
+               d2 = DotProduct(endplane->normal, lineend) - endplane->dist;
                if (developer.integer)
                {
                        // any brush with degenerate planes is not worth handling
@@ -815,9 +837,12 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const
                                Con_Printf("Collision_TraceLineBrushFloat: degenerate plane!\n");
                                return;
                        }
-                       f = furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints);
-                       if (fabs(f - startplane->dist) > 0.01f)
-                               Con_Printf("startplane->dist %f != calculated %f\n", startplane->dist, f);
+                       if (thatbrush_start->numpoints)
+                       {
+                               f = furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints);
+                               if (fabs(f - startplane->dist) > 0.01f)
+                                       Con_Printf("startplane->dist %f != calculated %f\n", startplane->dist, f);
+                       }
                }
 
                f = d1 - d2;
@@ -830,11 +855,11 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const
                                continue;
                        // enter
                        fstartsolid = false;
-                       f = (d1 - COLLISIONEPSILON) / f;
-                       f = bound(0, f, 1);
+                       f = d1 / (d1 - d2);
                        if (enterfrac < f)
                        {
                                enterfrac = f;
+                               enterfrac2 = (d1 - COLLISIONEPSILON) / (d1 - d2);
                                VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal);
                        }
                }
@@ -847,8 +872,7 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const
                                continue;
                        // leave
                        fendsolid = false;
-                       f = (d1 + COLLISIONEPSILON) / f;
-                       f = bound(0, f, 1);
+                       f = d1 / (d1 - d2);
                        if (leavefrac > f)
                                leavefrac = f;
                }
@@ -870,13 +894,31 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const
        // penetrating line segment is normally zero length if this brush was
        // generated from a polygon (infinitely thin), and could even be slightly
        // positive or negative due to rounding errors in that case.
-       if (brushsolid && enterfrac > -1 && enterfrac < trace->fraction && enterfrac - (1.0f / 1024.0f) <= leavefrac)
+       if (brushsolid && enterfrac > -1 && enterfrac < trace->realfraction && enterfrac - (1.0f / 1024.0f) <= leavefrac)
        {
-               trace->fraction = bound(0, enterfrac, 1);
+               trace->realfraction = bound(0, enterfrac, 1);
+               trace->fraction = bound(0, enterfrac2, 1);
                VectorCopy(newimpactnormal, trace->plane.normal);
        }
 }
 
+void Collision_TracePointBrushFloat(trace_t *trace, const vec3_t point, const colbrushf_t *thatbrush)
+{
+       int nplane;
+       const colplanef_t *plane;
+
+       for (nplane = 0, plane = thatbrush->planes;nplane < thatbrush->numplanes;nplane++, plane++)
+               if (DotProduct(plane->normal, point) > plane->dist)
+                       return;
+
+       trace->startsupercontents |= thatbrush->supercontents;
+       if (trace->hitsupercontentsmask & thatbrush->supercontents)
+       {
+               trace->startsolid = true;
+               trace->allsolid = true;
+       }
+}
+
 static colpointf_t polyf_points[256];
 static colplanef_t polyf_planes[256 + 2];
 static colbrushf_t polyf_brush;
@@ -947,7 +989,10 @@ void Collision_TraceLinePolygonFloat(trace_t *trace, const vec3_t linestart, con
 void Collision_TraceLineTriangleMeshFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numtriangles, const int *element3i, const float *vertex3f, int supercontents, const vec3_t segmentmins, const vec3_t segmentmaxs)
 {
        int i;
-       float facemins[3], facemaxs[3];
+#if 1
+       for (i = 0;i < numtriangles;i++, element3i += 3)
+               Collision_TraceLineTriangleFloat(trace, linestart, lineend, vertex3f + element3i[0] * 3, vertex3f + element3i[1] * 3, vertex3f + element3i[2] * 3);
+#else
        polyf_brush.numpoints = 3;
        polyf_brush.numplanes = 5;
        polyf_brush.points = polyf_points;
@@ -955,6 +1000,7 @@ void Collision_TraceLineTriangleMeshFloat(trace_t *trace, const vec3_t linestart
        polyf_brush.supercontents = supercontents;
        for (i = 0;i < numtriangles;i++, element3i += 3)
        {
+               float facemins[3], facemaxs[3];
                VectorCopy(vertex3f + element3i[0] * 3, polyf_points[0].v);
                VectorCopy(vertex3f + element3i[1] * 3, polyf_points[1].v);
                VectorCopy(vertex3f + element3i[2] * 3, polyf_points[2].v);
@@ -971,6 +1017,7 @@ void Collision_TraceLineTriangleMeshFloat(trace_t *trace, const vec3_t linestart
                        Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush);
                }
        }
+#endif
 }
 
 
@@ -1038,7 +1085,7 @@ void Collision_InitBrushForBox(void)
 
 colbrushf_t *Collision_BrushForBox(const matrix4x4_t *matrix, const vec3_t mins, const vec3_t maxs)
 {
-       int i;
+       int i, j;
        vec3_t v;
        colbrushf_t *brush;
        if (brushforbox_brush[0].numpoints == 0)
@@ -1067,9 +1114,21 @@ colbrushf_t *Collision_BrushForBox(const matrix4x4_t *matrix, const vec3_t mins,
                        v[i >> 1] = i & 1 ? 1 : -1;
                        Matrix4x4_Transform3x3(matrix, v, brush->planes[i].normal);
                        VectorNormalize(brush->planes[i].normal);
-                       brush->planes[i].dist = furthestplanedist_float(brush->planes[i].normal, brush->points, brush->numpoints);
                }
        }
+       for (j = 0;j < brush->numplanes;j++)
+               brush->planes[j].dist = furthestplanedist_float(brush->planes[j].normal, brush->points, brush->numpoints);
+       VectorCopy(brush->points[0].v, brush->mins);
+       VectorCopy(brush->points[0].v, brush->maxs);
+       for (j = 1;j < brush->numpoints;j++)
+       {
+               brush->mins[0] = min(brush->mins[0], brush->points[j].v[0]);
+               brush->mins[1] = min(brush->mins[1], brush->points[j].v[1]);
+               brush->mins[2] = min(brush->mins[2], brush->points[j].v[2]);
+               brush->maxs[0] = max(brush->maxs[0], brush->points[j].v[0]);
+               brush->maxs[1] = max(brush->maxs[1], brush->points[j].v[1]);
+               brush->maxs[2] = max(brush->maxs[2], brush->points[j].v[2]);
+       }
        Collision_ValidateBrush(brush);
        return brush;
 }
@@ -1093,6 +1152,7 @@ void Collision_ClipTrace_BrushBox(trace_t *trace, const vec3_t cmins, const vec3
        memset(trace, 0, sizeof(trace_t));
        trace->hitsupercontentsmask = hitsupercontentsmask;
        trace->fraction = 1;
+       trace->realfraction = 1;
        trace->allsolid = true;
        Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, boxbrush, boxbrush);
 }
@@ -1166,6 +1226,97 @@ float Collision_ClipTrace_Line_Sphere(double *linestart, double *lineend, double
        return impactdist / linelength;
 }
 
+void Collision_TraceLineTriangleFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const float *point0, const float *point1, const float *point2)
+{
+       float d1, d2, d, f, fnudged, impact[3], edgenormal[3], faceplanenormal[3], faceplanedist, edge[3];
+
+       // this code is designed for clockwise triangles, conversion to
+       // counterclockwise would require swapping some things around...
+       // it is easier to simply swap the point0 and point2 parameters to this
+       // function when calling it than it is to rewire the internals.
+
+       // calculate the faceplanenormal of the triangle, this represents the front side
+       TriangleNormal(point0, point1, point2, faceplanenormal);
+       // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
+       if (DotProduct(faceplanenormal, faceplanenormal) < 0.0001f)
+               return;
+       // normalize the normal
+       VectorNormalize(faceplanenormal);
+       // calculate the distance
+       faceplanedist = DotProduct(point0, faceplanenormal);
+
+       // calculate the start distance
+       d1 = DotProduct(faceplanenormal, linestart) - faceplanedist;
+       // if start point is on the back side there is no collision
+       // (we don't care about traces going through the triangle the wrong way)
+       if (d1 < 0)
+               return;
+
+       // calculate the end distance
+       d2 = DotProduct(faceplanenormal, lineend) - faceplanedist;
+       // if both are in front, there is no collision
+       if (d2 >= 0)
+               return;
+
+       // from here on we know d1 is >= 0 and d2 is < 0
+       // this means the line starts infront and ends behind, passing through it
+
+       // calculate the recipricol of the distance delta,
+       // so we can use it multiple times cheaply (instead of division)
+       d = 1.0f / (d1 - d2);
+       // calculate the impact fraction by taking the start distance (> 0)
+       // and subtracting the face plane distance (this is the distance of the
+       // triangle along that same normal)
+       // then multiply by the recipricol distance delta
+       f = d1 * d;
+       // skip out if this impact is further away than previous ones
+       if (f > trace->realfraction)
+               return;
+       // calculate the perfect impact point for classification of insidedness
+       impact[0] = linestart[0] + f * (lineend[0] - linestart[0]);
+       impact[1] = linestart[1] + f * (lineend[1] - linestart[1]);
+       impact[2] = linestart[2] + f * (lineend[2] - linestart[2]);
+
+       // calculate the edge normal and reject if impact is outside triangle
+       // (an edge normal faces away from the triangle, to get the desired normal
+       //  a crossproduct with the faceplanenormal is used, and because of the way
+       // the insidedness comparison is written it does not need to be normalized)
+       
+       VectorSubtract(point2, point0, edge);
+       CrossProduct(edge, faceplanenormal, edgenormal);
+       if (DotProduct(impact, edgenormal) > DotProduct(point0, edgenormal))
+               return;
+
+       VectorSubtract(point0, point1, edge);
+       CrossProduct(edge, faceplanenormal, edgenormal);
+       if (DotProduct(impact, edgenormal) > DotProduct(point1, edgenormal))
+               return;
+
+       VectorSubtract(point1, point2, edge);
+       CrossProduct(edge, faceplanenormal, edgenormal);
+       if (DotProduct(impact, edgenormal) > DotProduct(point2, edgenormal))
+               return;
+
+       // store the new trace fraction
+       trace->realfraction = bound(0, f, 1);
+
+       // calculate a nudged fraction to keep it out of the surface
+       // (the main fraction remains perfect)
+       fnudged = (d1 - COLLISIONEPSILON) * d;
+       trace->fraction = bound(0, fnudged, 1);
+
+       // store the new trace endpos
+       // not needed, it's calculated later when the trace is finished
+       //trace->endpos[0] = linestart[0] + fnudged * (lineend[0] - linestart[0]);
+       //trace->endpos[1] = linestart[1] + fnudged * (lineend[1] - linestart[1]);
+       //trace->endpos[2] = linestart[2] + fnudged * (lineend[2] - linestart[2]);
+
+       // store the new trace plane (because collisions only happen from
+       // the front this is always simply the triangle normal, never flipped)
+       VectorCopy(faceplanenormal, trace->plane.normal);
+       trace->plane.dist = faceplanedist;
+}
+
 typedef struct colbspnode_s
 {
        mplane_t plane;