X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=collision.c;h=1c4a4fe6b323149fe10be628f71c5b312c99c416;hb=0db6270f9fdf1da680391fe0389d7ad6cfc48517;hp=20e1e7670a24b85ac2c533fd99b6c7a4061595ec;hpb=eebd18fb1a1b617f7d8f454e4efbb80838331101;p=xonotic%2Fdarkplaces.git diff --git a/collision.c b/collision.c index 20e1e767..1c4a4fe6 100644 --- a/collision.c +++ b/collision.c @@ -289,32 +289,77 @@ 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; } @@ -328,6 +373,7 @@ 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) @@ -429,6 +475,25 @@ 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; @@ -438,6 +503,8 @@ 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); Collision_ValidateBrush(brush); return brush; } @@ -461,48 +528,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) { - bestdist = dist; - VectorCopy(normal, brush->planes->normal); + // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out) + brush->numplanes = 0; + return; + } + else + { + 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 +667,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) @@ -569,14 +690,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; @@ -594,10 +741,10 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush if (enterfrac < f) { enterfrac = f; - VectorBlend(startplane->normal, endplane->normal, enterfrac, newimpactnormal); + VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal); } } - else if (f < 0) + else { // moving out of brush if (d1 > 0) @@ -636,7 +783,7 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush } } -// 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; @@ -653,7 +800,19 @@ 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; + d2 = DotProduct(endplane->normal, lineend) - endplane->dist - COLLISIONEPSILON2; + 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; + } + 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) @@ -670,10 +829,10 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const if (enterfrac < f) { enterfrac = f; - VectorBlend(startplane->normal, endplane->normal, enterfrac, newimpactnormal); + VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal); } } - else if (f < 0) + else { // moving out of brush if (d1 > 0) @@ -712,10 +871,11 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const } } +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 +886,93 @@ 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; + 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_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush); + } + } +} + + 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 +984,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 +1010,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,6 +1022,11 @@ 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; } } @@ -792,23 +1037,32 @@ colbrushf_t *Collision_BrushForBox(const matrix4x4_t *matrix, const vec3_t mins, 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 { - 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 = 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); + brush->planes[i].dist = furthestplanedist_float(brush->planes[i].normal, brush->points, brush->numpoints); + } } Collision_ValidateBrush(brush); return brush; @@ -906,3 +1160,72 @@ float Collision_ClipTrace_Line_Sphere(double *linestart, double *lineend, double return impactdist / linelength; } +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])); + } +} +