X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=collision.c;h=e069fd8caa1f8c06d8e2895b6906b53b3aee3da9;hb=1d1f619196e21c77e48612ddd2ec9bee0387ad9a;hp=20e1e7670a24b85ac2c533fd99b6c7a4061595ec;hpb=eebd18fb1a1b617f7d8f454e4efbb80838331101;p=xonotic%2Fdarkplaces.git diff --git a/collision.c b/collision.c index 20e1e767..e069fd8c 100644 --- a/collision.c +++ b/collision.c @@ -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) +{ +} @@ -289,40 +317,85 @@ void Collision_PrintBrushAsQHull(colbrushf_t *brush, const char *name) void Collision_ValidateBrush(colbrushf_t *brush) { - int j, k; + int j, k, pointsoffplanes, printbrush; + float d; + printbrush = false; if (!brush->numpoints) { Con_Printf("Collision_ValidateBrush: brush with no points!\n"); - Collision_PrintBrushAsQHull(brush, "unnamed"); - return; + printbrush = true; } +#if 0 // it's ok for a brush to have one point and no planes... if (brush->numplanes == 0 && brush->numpoints != 1) { Con_Printf("Collision_ValidateBrush: brush with no planes and more than one point!\n"); - Collision_PrintBrushAsQHull(brush, "unnamed"); - return; + printbrush = true; } - for (k = 0;k < brush->numplanes;k++) +#endif + if (brush->numplanes) { - for (j = 0;j < brush->numpoints;j++) + pointsoffplanes = 0; + for (k = 0;k < brush->numplanes;k++) { - if (DotProduct(brush->points[j].v, brush->planes[k].normal) - brush->planes[k].dist > (1.0f / 8.0f)) + if (DotProduct(brush->planes[k].normal, brush->planes[k].normal) < 0.0001f) + Con_Printf("Collision_ValidateBrush: plane #%i (%f %f %f %f) is degenerate\n", k, brush->planes[k].normal[0], brush->planes[k].normal[1], brush->planes[k].normal[2], brush->planes[k].dist); + for (j = 0;j < brush->numpoints;j++) { - Con_Printf("Collision_NewBrushFromPlanes: point #%i (%f %f %f) infront of plane #%i (%f %f %f %f)\n", j, brush->points[j].v[0], brush->points[j].v[1], brush->points[j].v[2], k, brush->planes[k].normal[0], brush->planes[k].normal[1], brush->planes[k].normal[2], brush->planes[k].dist); - Collision_PrintBrushAsQHull(brush, "unnamed"); - return; + d = DotProduct(brush->points[j].v, brush->planes[k].normal) - brush->planes[k].dist; + if (d > (1.0f / 8.0f)) + { + Con_Printf("Collision_ValidateBrush: point #%i (%f %f %f) infront of plane #%i (%f %f %f %f)\n", j, brush->points[j].v[0], brush->points[j].v[1], brush->points[j].v[2], k, brush->planes[k].normal[0], brush->planes[k].normal[1], brush->planes[k].normal[2], brush->planes[k].dist); + printbrush = true; + } + if (fabs(d) > 0.01f) + pointsoffplanes++; } } + if (pointsoffplanes == 0) // all points are on all planes + { + Con_Printf("Collision_ValidateBrush: all points lie on all planes (degenerate, no brush volume!)\n"); + printbrush = true; + } + } + if (printbrush) + Collision_PrintBrushAsQHull(brush, "unnamed"); +} + +float nearestplanedist_float(const float *normal, const colpointf_t *points, int numpoints) +{ + float dist, bestdist; + bestdist = DotProduct(points->v, normal); + points++; + while(--numpoints) + { + dist = DotProduct(points->v, normal); + bestdist = min(bestdist, dist); + points++; + } + return bestdist; +} + +float furthestplanedist_float(const float *normal, const colpointf_t *points, int numpoints) +{ + float dist, bestdist; + bestdist = DotProduct(points->v, normal); + points++; + while(--numpoints) + { + dist = DotProduct(points->v, normal); + bestdist = max(bestdist, dist); + points++; } + return bestdist; } -colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalplanes, const mplane_t *originalplanes, int supercontents) +colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalplanes, const mplane_t *originalplanes, int supercontents, winding_t *temp1, winding_t *temp2) { int j, k, m; int numpoints, maxpoints, numplanes, maxplanes, numelements, maxelements, numtriangles, numpolypoints, maxpolypoints; - winding_t *w; + winding_t *w, *temp, *othertemp; colbrushf_t *brush; colpointf_t pointsbuf[256]; colplanef_t planesbuf[256]; @@ -353,19 +426,24 @@ colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalpla } // create a large polygon from the plane - w = Winding_NewFromPlane(originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist); + w = temp1; + othertemp = temp2; + BufWinding_NewFromPlane(w, originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist); // clip it by all other planes - for (k = 0;k < numoriginalplanes && w;k++) + for (k = 0;k < numoriginalplanes && w->numpoints;k++) { if (k != j) { // we want to keep the inside of the brush plane so we flip // the cutting plane - w = Winding_Clip(w, -originalplanes[k].normal[0], -originalplanes[k].normal[1], -originalplanes[k].normal[2], -originalplanes[k].dist, true); + BufWinding_Divide(w, -originalplanes[k].normal[0], -originalplanes[k].normal[1], -originalplanes[k].normal[2], -originalplanes[k].dist, othertemp, NULL, NULL, NULL); + temp = w; + w = othertemp; + othertemp = temp; } } // if nothing is left, skip it - if (!w) + if (!w->numpoints) continue; // copy off the number of points for later when the winding is freed @@ -410,8 +488,9 @@ colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalpla // store the index into a buffer polypointbuf[k] = m; } - Winding_Free(w); w = NULL; + othertemp = NULL; + temp = NULL; // add the triangles for the polygon // (this particular code makes a triangle fan) @@ -438,6 +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])); + // 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; } @@ -461,48 +554,130 @@ colbrushf_t *Collision_AllocBrushFloat(mempool_t *mempool, int numpoints, int nu void Collision_CalcPlanesForPolygonBrushFloat(colbrushf_t *brush) { int i; - float edge0[3], edge1[3], normal[3], dist, bestdist; + float edge0[3], edge1[3], edge2[3], normal[3], dist, bestdist, temp[3]; colpointf_t *p, *p2; - // choose best surface normal for polygon's plane - bestdist = 0; - for (i = 0, p = brush->points + 1;i < brush->numpoints - 2;i++, p++) + if (brush->numpoints == 3) { - VectorSubtract(p[-1].v, p[0].v, edge0); - VectorSubtract(p[1].v, p[0].v, edge1); - CrossProduct(edge0, edge1, normal); - dist = DotProduct(normal, normal); - if (i == 0 || bestdist < dist) + // optimized triangle case + TriangleNormal(brush->points[0].v, brush->points[1].v, brush->points[2].v, brush->planes[0].normal); + if (DotProduct(brush->planes[0].normal, brush->planes[0].normal) < 0.0001f) + { + // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out) + brush->numplanes = 0; + return; + } + else { - bestdist = dist; - VectorCopy(normal, brush->planes->normal); + brush->numplanes = 5; + VectorNormalize(brush->planes[0].normal); + brush->planes[0].dist = DotProduct(brush->points->v, brush->planes[0].normal); + VectorNegate(brush->planes[0].normal, brush->planes[1].normal); + brush->planes[1].dist = -brush->planes[0].dist; + VectorSubtract(brush->points[2].v, brush->points[0].v, edge0); + VectorSubtract(brush->points[0].v, brush->points[1].v, edge1); + VectorSubtract(brush->points[1].v, brush->points[2].v, edge2); + CrossProduct(edge0, brush->planes->normal, brush->planes[2].normal); + CrossProduct(edge1, brush->planes->normal, brush->planes[3].normal); + CrossProduct(edge2, brush->planes->normal, brush->planes[4].normal); + VectorNormalize(brush->planes[2].normal); + VectorNormalize(brush->planes[3].normal); + VectorNormalize(brush->planes[4].normal); + brush->planes[2].dist = DotProduct(brush->points[2].v, brush->planes[2].normal); + brush->planes[3].dist = DotProduct(brush->points[0].v, brush->planes[3].normal); + brush->planes[4].dist = DotProduct(brush->points[1].v, brush->planes[4].normal); + + if (developer.integer) + { + // validation code + //VectorSubtract(brush->points[0].v, brush->points[1].v, edge0); + //VectorSubtract(brush->points[2].v, brush->points[1].v, edge1); + CrossProduct(edge1, edge0, normal); + VectorNormalize(normal); + VectorSubtract(normal, brush->planes[0].normal, temp); + if (VectorLength(temp) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: TriangleNormal gave wrong answer (%f %f %f != correct answer %f %f %f)\n", brush->planes->normal[0], brush->planes->normal[1], brush->planes->normal[2], normal[0], normal[1], normal[2]); + if (fabs(DotProduct(brush->planes[1].normal, brush->planes[0].normal) - -1.0f) > 0.01f || fabs(brush->planes[1].dist - -brush->planes[0].dist) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 1 (%f %f %f %f) is not opposite plane 0 (%f %f %f %f)\n", brush->planes[1].normal[0], brush->planes[1].normal[1], brush->planes[1].normal[2], brush->planes[1].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[0].dist); + if (fabs(DotProduct(brush->planes[2].normal, brush->planes[0].normal)) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 2 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[2].dist); + if (fabs(DotProduct(brush->planes[3].normal, brush->planes[0].normal)) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 3 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[3].dist); + if (fabs(DotProduct(brush->planes[4].normal, brush->planes[0].normal)) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 4 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[4].dist); + if (fabs(DotProduct(brush->planes[2].normal, edge0)) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 2 (%f %f %f %f) is not perpendicular to edge 0 (%f %f %f to %f %f %f)\n", brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist, brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2]); + if (fabs(DotProduct(brush->planes[3].normal, edge1)) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 3 (%f %f %f %f) is not perpendicular to edge 1 (%f %f %f to %f %f %f)\n", brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist, brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2]); + if (fabs(DotProduct(brush->planes[4].normal, edge2)) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 4 (%f %f %f %f) is not perpendicular to edge 2 (%f %f %f to %f %f %f)\n", brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist, brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2]); + if (fabs(DotProduct(brush->points[0].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edges (%f %f %f to %f %f %f to %f %f %f) off front plane 0 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[0].dist); + if (fabs(DotProduct(brush->points[0].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edges (%f %f %f to %f %f %f to %f %f %f) off back plane 1 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[1].normal[0], brush->planes[1].normal[1], brush->planes[1].normal[2], brush->planes[1].dist); + if (fabs(DotProduct(brush->points[2].v, brush->planes[2].normal) - brush->planes[2].dist) > 0.01f || fabs(DotProduct(brush->points[0].v, brush->planes[2].normal) - brush->planes[2].dist) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist); + if (fabs(DotProduct(brush->points[0].v, brush->planes[3].normal) - brush->planes[3].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[3].normal) - brush->planes[3].dist) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist); + if (fabs(DotProduct(brush->points[1].v, brush->planes[4].normal) - brush->planes[4].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[4].normal) - brush->planes[4].dist) > 0.01f) + Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist); + } } } - - VectorNormalize(brush->planes->normal); - brush->planes->dist = DotProduct(brush->points->v, brush->planes->normal); - - // negate plane to create other side - VectorNegate(brush->planes[0].normal, brush->planes[1].normal); - brush->planes[1].dist = -brush->planes[0].dist; - for (i = 0, p = brush->points + (brush->numpoints - 1), p2 = brush->points;i < brush->numpoints;i++, p = p2, p2++) + else { - VectorSubtract(p->v, p2->v, edge0); - CrossProduct(edge0, brush->planes->normal, brush->planes[i + 2].normal); - VectorNormalize(brush->planes[i + 2].normal); - brush->planes[i + 2].dist = DotProduct(p->v, brush->planes[i + 2].normal); + // choose best surface normal for polygon's plane + bestdist = 0; + for (i = 0, p = brush->points + 1;i < brush->numpoints - 2;i++, p++) + { + VectorSubtract(p[-1].v, p[0].v, edge0); + VectorSubtract(p[1].v, p[0].v, edge1); + CrossProduct(edge0, edge1, normal); + //TriangleNormal(p[-1].v, p[0].v, p[1].v, normal); + dist = DotProduct(normal, normal); + if (i == 0 || bestdist < dist) + { + bestdist = dist; + VectorCopy(normal, brush->planes->normal); + } + } + if (bestdist < 0.0001f) + { + // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out) + brush->numplanes = 0; + return; + } + else + { + brush->numplanes = brush->numpoints + 2; + VectorNormalize(brush->planes->normal); + brush->planes->dist = DotProduct(brush->points->v, brush->planes->normal); + + // negate plane to create other side + VectorNegate(brush->planes[0].normal, brush->planes[1].normal); + brush->planes[1].dist = -brush->planes[0].dist; + for (i = 0, p = brush->points + (brush->numpoints - 1), p2 = brush->points;i < brush->numpoints;i++, p = p2, p2++) + { + VectorSubtract(p->v, p2->v, edge0); + CrossProduct(edge0, brush->planes->normal, brush->planes[i + 2].normal); + VectorNormalize(brush->planes[i + 2].normal); + brush->planes[i + 2].dist = DotProduct(p->v, brush->planes[i + 2].normal); + } + } } -#if 1 - // validity check - will be disabled later - for (i = 0;i < brush->numplanes;i++) + if (developer.integer) { - int j; - for (j = 0, p = brush->points;j < brush->numpoints;j++, p++) - if (DotProduct(p->v, brush->planes[i].normal) > brush->planes[i].dist + (1.0 / 32.0)) - Con_Printf("Error in brush plane generation, plane %i\n", i); + // validity check - will be disabled later + Collision_ValidateBrush(brush); + for (i = 0;i < brush->numplanes;i++) + { + int j; + for (j = 0, p = brush->points;j < brush->numpoints;j++, p++) + if (DotProduct(p->v, brush->planes[i].normal) > brush->planes[i].dist + (1.0 / 32.0)) + Con_Printf("Error in brush plane generation, plane %i\n", i); + } } -#endif } colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, int numpoints, float *points, int supercontents) @@ -518,34 +693,6 @@ colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, i return brush; } -float nearestplanedist_float(const float *normal, const colpointf_t *points, int numpoints) -{ - float dist, bestdist; - bestdist = DotProduct(points->v, normal); - points++; - while(--numpoints) - { - dist = DotProduct(points->v, normal); - bestdist = min(bestdist, dist); - points++; - } - return bestdist; -} - -float furthestplanedist_float(const float *normal, const colpointf_t *points, int numpoints) -{ - float dist, bestdist; - bestdist = DotProduct(points->v, normal); - points++; - while(--numpoints) - { - dist = DotProduct(points->v, normal); - bestdist = max(bestdist, dist); - points++; - } - return bestdist; -} - #define COLLISIONEPSILON (1.0f / 32.0f) #define COLLISIONEPSILON2 0//(1.0f / 32.0f) @@ -553,10 +700,11 @@ float furthestplanedist_float(const float *normal, const colpointf_t *points, in 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; @@ -569,14 +717,40 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush nplane2 -= thatbrush_start->numplanes; startplane = thisbrush_start->planes + nplane2; endplane = thisbrush_end->planes + nplane2; + if (developer.integer) + { + // any brush with degenerate planes is not worth handling + if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f) + { + Con_Printf("Collision_TraceBrushBrushFloat: degenerate thisbrush plane!\n"); + return; + } + f = furthestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints); + if (fabs(f - startplane->dist) > 0.01f) + Con_Printf("startplane->dist %f != calculated %f (thisbrush_start)\n", startplane->dist, f); + } + d1 = nearestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints) - furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints); + d2 = nearestplanedist_float(endplane->normal, thisbrush_end->points, thisbrush_end->numpoints) - furthestplanedist_float(endplane->normal, thatbrush_end->points, thatbrush_end->numpoints) - COLLISIONEPSILON2; } else { startplane = thatbrush_start->planes + nplane2; endplane = thatbrush_end->planes + nplane2; + if (developer.integer) + { + // any brush with degenerate planes is not worth handling + if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f) + { + Con_Printf("Collision_TraceBrushBrushFloat: degenerate thatbrush 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 (thatbrush_start)\n", startplane->dist, f); + } + d1 = nearestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints) - startplane->dist; + d2 = nearestplanedist_float(endplane->normal, thisbrush_end->points, thisbrush_end->numpoints) - startplane->dist - COLLISIONEPSILON2; } - d1 = nearestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints) - furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints); - d2 = nearestplanedist_float(endplane->normal, thisbrush_end->points, thisbrush_end->numpoints) - furthestplanedist_float(endplane->normal, thatbrush_end->points, thatbrush_end->numpoints) - COLLISIONEPSILON2; //Con_Printf("%c%i: d1 = %f, d2 = %f, d1 / (d1 - d2) = %f\n", nplane2 != nplane ? 'b' : 'a', nplane2, d1, d2, d1 / (d1 - d2)); f = d1 - d2; @@ -589,15 +763,15 @@ 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; - VectorBlend(startplane->normal, endplane->normal, enterfrac, newimpactnormal); + enterfrac2 = (d1 - COLLISIONEPSILON) / (d1 - d2); + VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal); } } - else if (f < 0) + else { // moving out of brush if (d1 > 0) @@ -606,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; } @@ -629,21 +802,23 @@ 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); } } -// NOTE: start and end of brush pair must have same numplanes/numpoints +// NOTE: start and end brush pair must have same numplanes/numpoints 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; @@ -654,6 +829,21 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const endplane = thatbrush_end->planes + nplane; d1 = DotProduct(startplane->normal, linestart) - startplane->dist; d2 = DotProduct(endplane->normal, lineend) - endplane->dist; + if (developer.integer) + { + // any brush with degenerate planes is not worth handling + if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f) + { + Con_Printf("Collision_TraceLineBrushFloat: degenerate plane!\n"); + return; + } + 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; if (f >= 0) @@ -665,15 +855,15 @@ 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; - VectorBlend(startplane->normal, endplane->normal, enterfrac, newimpactnormal); + enterfrac2 = (d1 - COLLISIONEPSILON) / (d1 - d2); + VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal); } } - else if (f < 0) + else { // moving out of brush if (d1 > 0) @@ -682,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; } @@ -705,17 +894,36 @@ 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; -void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points) +void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, int supercontents) { if (numpoints > 256) { @@ -726,16 +934,98 @@ void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbru polyf_brush.numplanes = numpoints + 2; polyf_brush.points = (colpointf_t *)points; polyf_brush.planes = polyf_planes; + polyf_brush.supercontents = supercontents; Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush); //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush"); Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush); } +void Collision_TraceBrushTriangleMeshFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, 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]; + polyf_brush.numpoints = 3; + polyf_brush.numplanes = 5; + polyf_brush.points = polyf_points; + polyf_brush.planes = polyf_planes; + polyf_brush.supercontents = supercontents; + for (i = 0;i < numtriangles;i++, element3i += 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); + facemins[0] = min(polyf_points[0].v[0], min(polyf_points[1].v[0], polyf_points[2].v[0])); + facemins[1] = min(polyf_points[0].v[1], min(polyf_points[1].v[1], polyf_points[2].v[1])); + facemins[2] = min(polyf_points[0].v[2], min(polyf_points[1].v[2], polyf_points[2].v[2])); + facemaxs[0] = max(polyf_points[0].v[0], max(polyf_points[1].v[0], polyf_points[2].v[0])); + facemaxs[1] = max(polyf_points[0].v[1], max(polyf_points[1].v[1], polyf_points[2].v[1])); + facemaxs[2] = max(polyf_points[0].v[2], max(polyf_points[1].v[2], polyf_points[2].v[2])); + if (BoxesOverlap(segmentmins, segmentmaxs, facemins, facemaxs)) + { + Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush); + //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush"); + Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush); + } + } +} + +void Collision_TraceLinePolygonFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numpoints, const float *points, int supercontents) +{ + if (numpoints > 256) + { + Con_Printf("Polygon with more than 256 points not supported yet (fixme!)\n"); + return; + } + polyf_brush.numpoints = numpoints; + polyf_brush.numplanes = numpoints + 2; + polyf_brush.points = (colpointf_t *)points; + polyf_brush.planes = polyf_planes; + polyf_brush.supercontents = supercontents; + Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush); + //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush"); + Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush); +} + +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; +#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; + polyf_brush.planes = polyf_planes; + 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); + facemins[0] = min(polyf_points[0].v[0], min(polyf_points[1].v[0], polyf_points[2].v[0])); + facemins[1] = min(polyf_points[0].v[1], min(polyf_points[1].v[1], polyf_points[2].v[1])); + facemins[2] = min(polyf_points[0].v[2], min(polyf_points[1].v[2], polyf_points[2].v[2])); + facemaxs[0] = max(polyf_points[0].v[0], max(polyf_points[1].v[0], polyf_points[2].v[0])); + facemaxs[1] = max(polyf_points[0].v[1], max(polyf_points[1].v[1], polyf_points[2].v[1])); + facemaxs[2] = max(polyf_points[0].v[2], max(polyf_points[1].v[2], polyf_points[2].v[2])); + if (BoxesOverlap(segmentmins, segmentmaxs, facemins, facemaxs)) + { + Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush); + //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush"); + Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush); + } + } +#endif +} + + static colpointf_t polyf_pointsstart[256], polyf_pointsend[256]; static colplanef_t polyf_planesstart[256 + 2], polyf_planesend[256 + 2]; static colbrushf_t polyf_brushstart, polyf_brushend; -void Collision_TraceBrushPolygonTransformFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, const matrix4x4_t *polygonmatrixstart, const matrix4x4_t *polygonmatrixend) +void Collision_TraceBrushPolygonTransformFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, const matrix4x4_t *polygonmatrixstart, const matrix4x4_t *polygonmatrixend, int supercontents) { int i; if (numpoints > 256) @@ -747,12 +1037,14 @@ void Collision_TraceBrushPolygonTransformFloat(trace_t *trace, const colbrushf_t polyf_brushstart.numplanes = numpoints + 2; polyf_brushstart.points = polyf_pointsstart;//(colpointf_t *)points; polyf_brushstart.planes = polyf_planesstart; + polyf_brushstart.supercontents = supercontents; for (i = 0;i < numpoints;i++) Matrix4x4_Transform(polygonmatrixstart, points + i * 3, polyf_brushstart.points[i].v); polyf_brushend.numpoints = numpoints; polyf_brushend.numplanes = numpoints + 2; polyf_brushend.points = polyf_pointsend;//(colpointf_t *)points; polyf_brushend.planes = polyf_planesend; + polyf_brushend.supercontents = supercontents; for (i = 0;i < numpoints;i++) Matrix4x4_Transform(polygonmatrixend, points + i * 3, polyf_brushend.points[i].v); Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushstart); @@ -771,6 +1063,7 @@ static int brushforbox_index = 0; static colpointf_t brushforbox_point[MAX_BRUSHFORBOX*8]; static colplanef_t brushforbox_plane[MAX_BRUSHFORBOX*6]; static colbrushf_t brushforbox_brush[MAX_BRUSHFORBOX]; +static colbrushf_t brushforpoint_brush[MAX_BRUSHFORBOX]; void Collision_InitBrushForBox(void) { @@ -782,33 +1075,59 @@ void Collision_InitBrushForBox(void) brushforbox_brush[i].numplanes = 6; brushforbox_brush[i].points = brushforbox_point + i * 8; brushforbox_brush[i].planes = brushforbox_plane + i * 6; + brushforpoint_brush[i].supercontents = SUPERCONTENTS_SOLID; + brushforpoint_brush[i].numpoints = 1; + brushforpoint_brush[i].numplanes = 0; + brushforpoint_brush[i].points = brushforbox_point + i * 8; + brushforpoint_brush[i].planes = brushforbox_plane + i * 6; } } 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) Collision_InitBrushForBox(); - brush = brushforbox_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX); - // FIXME: optimize - for (i = 0;i < 8;i++) + if (VectorCompare(mins, maxs)) { - v[0] = i & 1 ? maxs[0] : mins[0]; - v[1] = i & 2 ? maxs[1] : mins[1]; - v[2] = i & 4 ? maxs[2] : mins[2]; - Matrix4x4_Transform(matrix, v, brush->points[i].v); + // point brush + brush = brushforpoint_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX); + VectorCopy(mins, brush->points->v); } - // FIXME: optimize! - for (i = 0;i < 6;i++) + else + { + brush = brushforbox_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX); + // FIXME: optimize + for (i = 0;i < 8;i++) + { + v[0] = i & 1 ? maxs[0] : mins[0]; + v[1] = i & 2 ? maxs[1] : mins[1]; + v[2] = i & 4 ? maxs[2] : mins[2]; + Matrix4x4_Transform(matrix, v, brush->points[i].v); + } + // FIXME: optimize! + for (i = 0;i < 6;i++) + { + VectorClear(v); + v[i >> 1] = i & 1 ? 1 : -1; + Matrix4x4_Transform3x3(matrix, v, brush->planes[i].normal); + VectorNormalize(brush->planes[i].normal); + } + } + 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++) { - VectorClear(v); - 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); + 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; @@ -833,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); } @@ -906,3 +1226,163 @@ 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; + struct colbspnode_s *children[2]; + // the node is reallocated or split if max is reached + int numcolbrushf; + int maxcolbrushf; + colbrushf_t **colbrushflist; + //int numcolbrushd; + //int maxcolbrushd; + //colbrushd_t **colbrushdlist; +} +colbspnode_t; + +typedef struct colbsp_s +{ + mempool_t *mempool; + colbspnode_t *nodes; +} +colbsp_t; + +colbsp_t *Collision_CreateCollisionBSP(mempool_t *mempool) +{ + colbsp_t *bsp; + bsp = Mem_Alloc(mempool, sizeof(colbsp_t)); + bsp->mempool = mempool; + bsp->nodes = Mem_Alloc(bsp->mempool, sizeof(colbspnode_t)); + return bsp; +} + +void Collision_FreeCollisionBSPNode(colbspnode_t *node) +{ + if (node->children[0]) + Collision_FreeCollisionBSPNode(node->children[0]); + if (node->children[1]) + Collision_FreeCollisionBSPNode(node->children[1]); + while (--node->numcolbrushf) + Mem_Free(node->colbrushflist[node->numcolbrushf]); + //while (--node->numcolbrushd) + // Mem_Free(node->colbrushdlist[node->numcolbrushd]); + Mem_Free(node); +} + +void Collision_FreeCollisionBSP(colbsp_t *bsp) +{ + Collision_FreeCollisionBSPNode(bsp->nodes); + Mem_Free(bsp); +} + +void Collision_BoundingBoxOfBrushTraceSegment(const colbrushf_t *start, const colbrushf_t *end, vec3_t mins, vec3_t maxs, float startfrac, float endfrac) +{ + int i; + colpointf_t *ps, *pe; + float tempstart[3], tempend[3]; + VectorLerp(start->points[0].v, startfrac, end->points[0].v, mins); + VectorCopy(mins, maxs); + for (i = 0, ps = start->points, pe = end->points;i < start->numpoints;i++, ps++, pe++) + { + VectorLerp(ps->v, startfrac, pe->v, tempstart); + VectorLerp(ps->v, endfrac, pe->v, tempend); + mins[0] = min(mins[0], min(tempstart[0], tempend[0])); + mins[1] = min(mins[1], min(tempstart[1], tempend[1])); + mins[2] = min(mins[2], min(tempstart[2], tempend[2])); + maxs[0] = min(maxs[0], min(tempstart[0], tempend[0])); + maxs[1] = min(maxs[1], min(tempstart[1], tempend[1])); + maxs[2] = min(maxs[2], min(tempstart[2], tempend[2])); + } +} +