#include "quakedef.h"
#include "polygon.h"
-#define COLLISION_SNAPSCALE (8.0f)
+#define COLLISION_SNAPSCALE (32.0f)
#define COLLISION_SNAP (1.0f / COLLISION_SNAPSCALE)
+#define COLLISION_SNAP2 (2.0f / COLLISION_SNAPSCALE)
+#define COLLISION_PLANE_DIST_EPSILON (2.0f / COLLISION_SNAPSCALE)
cvar_t collision_impactnudge = {0, "collision_impactnudge", "0.03125", "how much to back off from the impact"};
cvar_t collision_startnudge = {0, "collision_startnudge", "0", "how much to bias collision trace start"};
cvar_t collision_endnudge = {0, "collision_endnudge", "0", "how much to bias collision trace end"};
cvar_t collision_enternudge = {0, "collision_enternudge", "0", "how much to bias collision entry fraction"};
cvar_t collision_leavenudge = {0, "collision_leavenudge", "0", "how much to bias collision exit fraction"};
+cvar_t collision_prefernudgedfraction = {0, "collision_prefernudgedfraction", "1", "whether to sort collision events by nudged fraction (1) or real fraction (0)"};
void Collision_Init (void)
{
Cvar_RegisterVariable(&collision_endnudge);
Cvar_RegisterVariable(&collision_enternudge);
Cvar_RegisterVariable(&collision_leavenudge);
+ Cvar_RegisterVariable(&collision_prefernudgedfraction);
}
for (k = 0;k < brush->numplanes;k++)
{
d = DotProduct(brush->points[j].v, brush->planes[k].normal) - brush->planes[k].dist;
- if (d > (1.0f / 8.0f))
+ if (d > COLLISION_PLANE_DIST_EPSILON)
{
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.125f)
+ if (fabs(d) > COLLISION_PLANE_DIST_EPSILON)
pointsoffplanes++;
else
pointonplanes++;
float nearestplanedist_float(const float *normal, const colpointf_t *points, int numpoints)
{
float dist, bestdist;
+ if (!numpoints)
+ return 0;
bestdist = DotProduct(points->v, normal);
points++;
while(--numpoints)
float furthestplanedist_float(const float *normal, const colpointf_t *points, int numpoints)
{
float dist, bestdist;
+ if (!numpoints)
+ return 0;
bestdist = DotProduct(points->v, normal);
points++;
while(--numpoints)
}
-colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalplanes, const colplanef_t *originalplanes)
+colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalplanes, const colplanef_t *originalplanes, int supercontents)
{
// TODO: planesbuf could be replaced by a remapping table
- int j, k, m, w;
+ int j, k, m, w, xyzflags;
int numpointsbuf = 0, maxpointsbuf = 256, numplanesbuf = 0, maxplanesbuf = 256, numelementsbuf = 0, maxelementsbuf = 256;
+ double maxdist;
colbrushf_t *brush;
colpointf_t pointsbuf[256];
colplanef_t planesbuf[256];
memset(polypointbuf, 0, sizeof(polypointbuf));
memset(p, 0, sizeof(p));
#endif
+ // figure out how large a bounding box we need to properly compute this brush
+ maxdist = 0;
+ for (j = 0;j < numoriginalplanes;j++)
+ maxdist = max(maxdist, fabs(originalplanes[j].dist));
+ // now make it large enough to enclose the entire brush, and round it off to a reasonable multiple of 1024
+ maxdist = floor(maxdist * (4.0 / 1024.0) + 2) * 1024.0;
// 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)
// check if there are too many and skip the brush
if (numplanesbuf >= maxplanesbuf)
{
- Con_Print("Collision_NewBrushFromPlanes: failed to build collision brush: too many planes for buffer\n");
+ Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many planes for buffer\n");
return NULL;
}
+ // add the new plane
+ VectorCopy(originalplanes[j].normal, planesbuf[numplanesbuf].normal);
+ planesbuf[numplanesbuf].dist = originalplanes[j].dist;
+ planesbuf[numplanesbuf].q3surfaceflags = originalplanes[j].q3surfaceflags;
+ planesbuf[numplanesbuf].texture = originalplanes[j].texture;
+ numplanesbuf++;
+
// create a large polygon from the plane
w = 0;
- PolygonD_QuadForPlane(p[w], originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist, 1024.0*1024.0*1024.0);
+ PolygonD_QuadForPlane(p[w], originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist, maxdist);
pnumpoints = 4;
// clip it by all other planes
- for (k = 0;k < numoriginalplanes && pnumpoints && pnumpoints <= pmaxpoints;k++)
+ for (k = 0;k < numoriginalplanes && pnumpoints >= 3 && pnumpoints <= pmaxpoints;k++)
{
+ // skip the plane this polygon
+ // (nothing happens if it is processed, this is just an optimization)
if (k != j)
{
// we want to keep the inside of the brush plane so we flip
// the cutting plane
- PolygonD_Divide(pnumpoints, p[w], -originalplanes[k].normal[0], -originalplanes[k].normal[1], -originalplanes[k].normal[2], -originalplanes[k].dist, 1.0/32.0, pmaxpoints, p[!w], &pnumpoints, 0, NULL, NULL, NULL);
+ PolygonD_Divide(pnumpoints, p[w], -originalplanes[k].normal[0], -originalplanes[k].normal[1], -originalplanes[k].normal[2], -originalplanes[k].dist, COLLISION_PLANE_DIST_EPSILON, pmaxpoints, p[!w], &pnumpoints, 0, NULL, NULL, NULL);
w = !w;
}
}
+
// if nothing is left, skip it
if (pnumpoints < 3)
{
- //Con_Printf("Collision_NewBrushFromPlanes: warning: polygon for plane %f %f %f %f clipped away\n", originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist);
+ //Con_DPrintf("Collision_NewBrushFromPlanes: warning: polygon for plane %f %f %f %f clipped away\n", originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist);
continue;
}
int l, m;
m = 0;
for (l = 0;l < numoriginalplanes;l++)
- if (fabs(DotProduct(&p[w][k*3], originalplanes[l].normal) - originalplanes[l].dist) < 1.0/8.0)
+ if (fabs(DotProduct(&p[w][k*3], originalplanes[l].normal) - originalplanes[l].dist) < COLLISION_PLANE_DIST_EPSILON)
m++;
if (m < 3)
break;
}
if (k < pnumpoints)
{
- Con_Printf("Collision_NewBrushFromPlanes: warning: polygon point does not lie on at least 3 planes\n");
+ Con_DPrintf("Collision_NewBrushFromPlanes: warning: polygon point does not lie on at least 3 planes\n");
//return NULL;
}
// check if there are too many polygon vertices for buffer
if (pnumpoints > pmaxpoints)
{
- Con_Print("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
+ Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
return NULL;
}
// check if there are too many triangle elements for buffer
if (numelementsbuf + (pnumpoints - 2) * 3 > maxelementsbuf)
{
- Con_Print("Collision_NewBrushFromPlanes: failed to build collision brush: too many triangle elements for buffer\n");
+ Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many triangle elements for buffer\n");
return NULL;
}
for (k = 0;k < pnumpoints;k++)
{
+ float v[3];
+ // downgrade to float precision before comparing
+ VectorCopy(&p[w][k*3], v);
+
// check if there is already a matching point (no duplicates)
for (m = 0;m < numpointsbuf;m++)
- if (VectorDistance2(&p[w][k*3], pointsbuf[m].v) < COLLISION_SNAP)
+ if (VectorDistance2(v, pointsbuf[m].v) < COLLISION_SNAP2)
break;
// if there is no match, add a new one
// check if there are too many and skip the brush
if (numpointsbuf >= maxpointsbuf)
{
- Con_Print("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
+ Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
return NULL;
}
// add the new one
elementsbuf[numelementsbuf++] = polypointbuf[k + 1];
elementsbuf[numelementsbuf++] = polypointbuf[k + 2];
}
+ }
- // add the new plane
- VectorCopy(originalplanes[j].normal, planesbuf[numplanesbuf].normal);
- planesbuf[numplanesbuf].dist = originalplanes[j].dist;
- planesbuf[numplanesbuf].supercontents = originalplanes[j].supercontents;
- planesbuf[numplanesbuf].q3surfaceflags = originalplanes[j].q3surfaceflags;
- planesbuf[numplanesbuf].texture = originalplanes[j].texture;
- numplanesbuf++;
+ // if nothing is left, there's nothing to allocate
+ if (numplanesbuf < 4)
+ {
+ Con_DPrintf("Collision_NewBrushFromPlanes: failed to build collision brush: %i triangles, %i planes (input was %i planes), %i vertices\n", numelementsbuf / 3, numplanesbuf, numoriginalplanes, numpointsbuf);
+ return NULL;
}
+ // if no triangles or points could be constructed, then this routine failed but the brush is not discarded
+ if (numelementsbuf < 12 || numpointsbuf < 4)
+ Con_DPrintf("Collision_NewBrushFromPlanes: unable to rebuild triangles/points for collision brush: %i triangles, %i planes (input was %i planes), %i vertices\n", numelementsbuf / 3, numplanesbuf, numoriginalplanes, numpointsbuf);
+
// validate plane distances
for (j = 0;j < numplanesbuf;j++)
{
float d = furthestplanedist_float(planesbuf[j].normal, pointsbuf, numpointsbuf);
- if (fabs(planesbuf[j].dist - d) > (1.0f/32.0f))
- Con_Printf("plane %f %f %f %f mismatches dist %f\n", planesbuf[j].normal[0], planesbuf[j].normal[1], planesbuf[j].normal[2], planesbuf[j].dist, d);
- }
-
- // if nothing is left, there's nothing to allocate
- if (numelementsbuf < 12 || numplanesbuf < 4 || numpointsbuf < 4)
- {
- Con_Printf("Collision_NewBrushFromPlanes: failed to build collision brush: %i triangles, %i planes (input was %i planes), %i vertices\n", numelementsbuf / 3, numplanesbuf, numoriginalplanes, numpointsbuf);
- return NULL;
+ if (fabs(planesbuf[j].dist - d) > COLLISION_PLANE_DIST_EPSILON)
+ Con_DPrintf("plane %f %f %f %f mismatches dist %f\n", planesbuf[j].normal[0], planesbuf[j].normal[1], planesbuf[j].normal[2], planesbuf[j].dist, d);
}
// allocate the brush and copy to it
brush = (colbrushf_t *)Mem_Alloc(mempool, sizeof(colbrushf_t) + sizeof(colpointf_t) * numpointsbuf + sizeof(colplanef_t) * numplanesbuf + sizeof(int) * numelementsbuf);
+ brush->supercontents = supercontents;
brush->numplanes = numplanesbuf;
brush->numpoints = numpointsbuf;
brush->numtriangles = numelementsbuf / 3;
brush->planes[j].normal[1] = planesbuf[j].normal[1];
brush->planes[j].normal[2] = planesbuf[j].normal[2];
brush->planes[j].dist = planesbuf[j].dist;
- brush->planes[j].supercontents = planesbuf[j].supercontents;
brush->planes[j].q3surfaceflags = planesbuf[j].q3surfaceflags;
brush->planes[j].texture = planesbuf[j].texture;
- brush->supercontents |= brush->planes[j].supercontents;
}
for (j = 0;j < brush->numtriangles * 3;j++)
brush->elements[j] = elementsbuf[j];
- VectorCopy(brush->points[0].v, brush->mins);
- VectorCopy(brush->points[0].v, brush->maxs);
- for (j = 1;j < brush->numpoints;j++)
+
+ xyzflags = 0;
+ VectorClear(brush->mins);
+ VectorClear(brush->maxs);
+ for (j = 0;j < min(6, numoriginalplanes);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]);
+ if (originalplanes[j].normal[0] == 1) {xyzflags |= 1;brush->maxs[0] = originalplanes[j].dist;}
+ else if (originalplanes[j].normal[0] == -1) {xyzflags |= 2;brush->mins[0] = -originalplanes[j].dist;}
+ else if (originalplanes[j].normal[1] == 1) {xyzflags |= 4;brush->maxs[1] = originalplanes[j].dist;}
+ else if (originalplanes[j].normal[1] == -1) {xyzflags |= 8;brush->mins[1] = -originalplanes[j].dist;}
+ else if (originalplanes[j].normal[2] == 1) {xyzflags |= 16;brush->maxs[2] = originalplanes[j].dist;}
+ else if (originalplanes[j].normal[2] == -1) {xyzflags |= 32;brush->mins[2] = -originalplanes[j].dist;}
+ }
+ // if not all xyzflags were set, then this is not a brush from q3map/q3map2, and needs reconstruction of the bounding box
+ // (this case works for any brush with valid points, but sometimes brushes are not reconstructed properly and hence the points are not valid, so this is reserved as a fallback case)
+ if (xyzflags != 63)
+ {
+ 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]);
+ }
}
brush->mins[0] -= 1;
brush->mins[1] -= 1;
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)
+ if (developer.integer >= 100)
{
// validation code
#if 0
}
}
- if (developer.integer)
+ if (developer.integer >= 100)
{
// validity check - will be disabled later
Collision_ValidateBrush(brush);
{
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))
+ if (DotProduct(p->v, brush->planes[i].normal) > brush->planes[i].dist + COLLISION_PLANE_DIST_EPSILON)
Con_Printf("Error in brush plane generation, plane %i\n", i);
}
}
// NOTE: start and end of each brush pair must have same numplanes/numpoints
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 = true, fendsolid = true, brushsolid, hitsupercontents = 0, hitq3surfaceflags = 0;
+ int nplane, nplane2, hitq3surfaceflags = 0;
float enterfrac = -1, leavefrac = 1, d1, d2, f, imove, newimpactnormal[3], enterfrac2 = -1;
const colplanef_t *startplane, *endplane;
texture_t *hittexture = NULL;
nplane2 -= thatbrush_start->numplanes;
startplane = thisbrush_start->planes + nplane2;
endplane = thisbrush_end->planes + nplane2;
- if (developer.integer)
+ if (developer.integer >= 100)
{
// any brush with degenerate planes is not worth handling
if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f)
return;
}
f = furthestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints);
- if (fabs(f - startplane->dist) > 0.125f)
+ if (fabs(f - startplane->dist) > COLLISION_PLANE_DIST_EPSILON)
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) - collision_startnudge.value;
{
startplane = thatbrush_start->planes + nplane2;
endplane = thatbrush_end->planes + nplane2;
- if (developer.integer)
+ if (developer.integer >= 100)
{
// any brush with degenerate planes is not worth handling
if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f)
return;
}
f = furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints);
- if (fabs(f - startplane->dist) > 0.125f)
+ if (fabs(f - startplane->dist) > COLLISION_PLANE_DIST_EPSILON)
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 - collision_startnudge.value;
}
//Con_Printf("%c%i: d1 = %f, d2 = %f, d1 / (d1 - d2) = %f\n", nplane2 != nplane ? 'b' : 'a', nplane2, d1, d2, d1 / (d1 - d2));
- if(d1 > d2)
+ if (d1 > d2)
{
// moving into brush
- if(d2 > 0)
+ if (d2 >= collision_enternudge.value)
return;
- if(d1 > 0)
+ if (d1 > 0)
{
// enter
- fstartsolid = false;
imove = 1 / (d1 - d2);
f = (d1 - collision_enternudge.value) * imove;
+ if (f < 0)
+ f = 0;
+ // check if this will reduce the collision time range
if (enterfrac < f)
{
+ // reduced collision time range
enterfrac = f;
- enterfrac2 = f - collision_impactnudge.value * imove;
+ // if the collision time range is now empty, no collision
+ if (enterfrac > leavefrac)
+ return;
+ // if the collision would be further away than the trace's
+ // existing collision data, we don't care about this
+ // collision
+ if (enterfrac > trace->realfraction)
+ return;
+ // calculate the nudged fraction and impact normal we'll
+ // need if we accept this collision later
+ enterfrac2 = (d1 - collision_impactnudge.value) * imove;
VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal);
- hitsupercontents = startplane->supercontents;
hitq3surfaceflags = startplane->q3surfaceflags;
hittexture = startplane->texture;
}
else
{
// moving out of brush
- if(d1 > 0)
+ if (d1 > 0)
return;
- if(d2 > 0)
+ if (d2 > 0)
{
// leave
- fendsolid = false;
f = (d1 + collision_leavenudge.value) / (d1 - d2);
+ if (f > 1)
+ f = 1;
+ // check if this will reduce the collision time range
if (leavefrac > f)
+ {
+ // reduced collision time range
leavefrac = f;
+ // if the collision time range is now empty, no collision
+ if (enterfrac > leavefrac)
+ return;
+ }
}
}
}
- brushsolid = trace->hitsupercontentsmask & thatbrush_start->supercontents;
- if (fstartsolid)
- {
- trace->startsupercontents |= thatbrush_start->supercontents;
- if (brushsolid)
- {
- trace->startsolid = true;
- if (fendsolid)
- trace->allsolid = true;
- }
- }
+ // at this point we know the trace overlaps the brush because it was not
+ // rejected at any point in the loop above
- // LordHavoc: we need an epsilon nudge here because for a point trace the
- // 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->realfraction && enterfrac - (1.0f / 1024.0f) <= leavefrac)
+ // see if the trace started outside the brush or not
+ if (enterfrac > -1)
{
-#if 0
- // broken
- if (thatbrush_start->ispolygon)
- {
- d1 = nearestplanedist_float(thatbrush_start->planes[0].normal, thisbrush_start->points, thisbrush_start->numpoints) - thatbrush_start->planes[0].dist - collision_startnudge.value;
- d2 = nearestplanedist_float(thatbrush_end->planes[0].normal, thisbrush_end->points, thisbrush_end->numpoints) - thatbrush_end->planes[0].dist - collision_endnudge.value;
- move = d1 - d2;
- if (move <= 0 || d2 > collision_enternudge.value || d1 < 0)
- return;
- // enter
- imove = 1 / move;
- enterfrac = (d1 - collision_enternudge.value) * imove;
- if (enterfrac < trace->realfraction)
- {
- enterfrac2 = enterfrac - collision_impactnudge.value * imove;
- trace->hitsupercontents = thatbrush_start->planes[0].supercontents;
- trace->hitq3surfaceflags = thatbrush_start->planes[0].q3surfaceflags;
- trace->hittexture = thatbrush_start->planes[0].texture;
- trace->realfraction = bound(0, enterfrac, 1);
- trace->fraction = bound(0, enterfrac2, 1);
- VectorLerp(thatbrush_start->planes[0].normal, enterfrac, thatbrush_end->planes[0].normal, trace->plane.normal);
- }
- }
- else
-#endif
+ // started outside, and overlaps, therefore there is a collision here
+ // store out the impact information
+ if (trace->hitsupercontentsmask & thatbrush_start->supercontents)
{
- trace->hitsupercontents = hitsupercontents;
+ trace->hitsupercontents = thatbrush_start->supercontents;
trace->hitq3surfaceflags = hitq3surfaceflags;
trace->hittexture = hittexture;
trace->realfraction = bound(0, enterfrac, 1);
trace->fraction = bound(0, enterfrac2, 1);
+ if (collision_prefernudgedfraction.integer)
+ trace->realfraction = trace->fraction;
VectorCopy(newimpactnormal, trace->plane.normal);
}
}
+ else
+ {
+ // started inside, update startsolid and friends
+ trace->startsupercontents |= thatbrush_start->supercontents;
+ if (trace->hitsupercontentsmask & thatbrush_start->supercontents)
+ {
+ trace->startsolid = true;
+ if (leavefrac < 1)
+ trace->allsolid = true;
+ }
+ }
}
// 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 = true, fendsolid = true, brushsolid, hitsupercontents = 0, hitq3surfaceflags = 0;
+ int nplane, hitq3surfaceflags = 0;
float enterfrac = -1, leavefrac = 1, d1, d2, f, imove, newimpactnormal[3], enterfrac2 = -1;
const colplanef_t *startplane, *endplane;
texture_t *hittexture = NULL;
endplane = thatbrush_end->planes + nplane;
d1 = DotProduct(startplane->normal, linestart) - startplane->dist - collision_startnudge.value;
d2 = DotProduct(endplane->normal, lineend) - endplane->dist - collision_endnudge.value;
- if (developer.integer)
+ if (developer.integer >= 100)
{
// any brush with degenerate planes is not worth handling
if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f)
if (thatbrush_start->numpoints)
{
f = furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints);
- if (fabs(f - startplane->dist) > 0.125f)
+ if (fabs(f - startplane->dist) > COLLISION_PLANE_DIST_EPSILON)
Con_Printf("startplane->dist %f != calculated %f\n", startplane->dist, f);
}
}
if (d1 > d2)
{
// moving into brush
- if (d2 > 0)
+ if (d2 >= collision_enternudge.value)
return;
if (d1 > 0)
{
// enter
- fstartsolid = false;
imove = 1 / (d1 - d2);
f = (d1 - collision_enternudge.value) * imove;
+ if (f < 0)
+ f = 0;
+ // check if this will reduce the collision time range
if (enterfrac < f)
{
+ // reduced collision time range
enterfrac = f;
- enterfrac2 = f - collision_impactnudge.value * imove;
+ // if the collision time range is now empty, no collision
+ if (enterfrac > leavefrac)
+ return;
+ // if the collision would be further away than the trace's
+ // existing collision data, we don't care about this
+ // collision
+ if (enterfrac > trace->realfraction)
+ return;
+ // calculate the nudged fraction and impact normal we'll
+ // need if we accept this collision later
+ enterfrac2 = (d1 - collision_impactnudge.value) * imove;
VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal);
- hitsupercontents = startplane->supercontents;
hitq3surfaceflags = startplane->q3surfaceflags;
hittexture = startplane->texture;
}
if (d2 > 0)
{
// leave
- fendsolid = false;
f = (d1 + collision_leavenudge.value) / (d1 - d2);
+ // check if this will reduce the collision time range
if (leavefrac > f)
+ {
+ // reduced collision time range
leavefrac = f;
+ // if the collision time range is now empty, no collision
+ if (enterfrac > leavefrac)
+ return;
+ }
}
}
}
- brushsolid = trace->hitsupercontentsmask & thatbrush_start->supercontents;
- if (fstartsolid)
- {
- trace->startsupercontents |= thatbrush_start->supercontents;
- if (brushsolid)
- {
- trace->startsolid = true;
- if (fendsolid)
- trace->allsolid = true;
- }
- }
+ // at this point we know the trace overlaps the brush because it was not
+ // rejected at any point in the loop above
- // LordHavoc: we need an epsilon nudge here because for a point trace the
- // 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->realfraction && enterfrac <= leavefrac)
+ // see if the trace started outside the brush or not
+ if (enterfrac > -1)
{
-#if 0
- // broken
- if (thatbrush_start->ispolygon)
- {
- d1 = DotProduct(thatbrush_start->planes[0].normal, linestart) - thatbrush_start->planes[0].dist - collision_startnudge.value;
- d2 = DotProduct(thatbrush_end->planes[0].normal, lineend) - thatbrush_end->planes[0].dist - collision_endnudge.value;
- move = d1 - d2;
- if (move <= 0 || d2 > collision_enternudge.value || d1 < 0)
- return;
- // enter
- imove = 1 / move;
- enterfrac = (d1 - collision_enternudge.value) * imove;
- if (enterfrac < trace->realfraction)
- {
- enterfrac2 = enterfrac - collision_impactnudge.value * imove;
- trace->hitsupercontents = hitsupercontents;
- trace->hitq3surfaceflags = hitq3surfaceflags;
- trace->hittexture = hittexture;
- trace->realfraction = bound(0, enterfrac, 1);
- trace->fraction = bound(0, enterfrac2, 1);
- VectorLerp(thatbrush_start->planes[0].normal, enterfrac, thatbrush_end->planes[0].normal, trace->plane.normal);
- }
- }
- else
-#endif
+ // started outside, and overlaps, therefore there is a collision here
+ // store out the impact information
+ if (trace->hitsupercontentsmask & thatbrush_start->supercontents)
{
- trace->hitsupercontents = hitsupercontents;
+ trace->hitsupercontents = thatbrush_start->supercontents;
trace->hitq3surfaceflags = hitq3surfaceflags;
trace->hittexture = hittexture;
trace->realfraction = bound(0, enterfrac, 1);
trace->fraction = bound(0, enterfrac2, 1);
+ if (collision_prefernudgedfraction.integer)
+ trace->realfraction = trace->fraction;
VectorCopy(newimpactnormal, trace->plane.normal);
}
}
+ else
+ {
+ // started inside, update startsolid and friends
+ trace->startsupercontents |= thatbrush_start->supercontents;
+ if (trace->hitsupercontentsmask & thatbrush_start->supercontents)
+ {
+ trace->startsolid = true;
+ if (leavefrac < 1)
+ trace->allsolid = true;
+ }
+ }
}
-void Collision_TracePointBrushFloat(trace_t *trace, const vec3_t point, const colbrushf_t *thatbrush)
+qboolean Collision_PointInsideBrushFloat(const vec3_t point, const colbrushf_t *brush)
{
int nplane;
const colplanef_t *plane;
- for (nplane = 0, plane = thatbrush->planes;nplane < thatbrush->numplanes;nplane++, plane++)
+ if (!BoxesOverlap(point, point, brush->mins, brush->maxs))
+ return false;
+ for (nplane = 0, plane = brush->planes;nplane < brush->numplanes;nplane++, plane++)
if (DotProduct(plane->normal, point) > plane->dist)
- return;
+ return false;
+ return true;
+}
+
+void Collision_TracePointBrushFloat(trace_t *trace, const vec3_t point, const colbrushf_t *thatbrush)
+{
+ if (!Collision_PointInsideBrushFloat(point, thatbrush))
+ return;
trace->startsupercontents |= thatbrush->supercontents;
if (trace->hitsupercontentsmask & thatbrush->supercontents)
void Collision_SnapCopyPoints(int numpoints, const colpointf_t *in, colpointf_t *out, float fractionprecision, float invfractionprecision)
{
- while (numpoints--)
+ int i;
+ for (i = 0;i < numpoints;i++)
{
- out->v[0] = floor(in->v[0] * fractionprecision + 0.5f) * invfractionprecision;
- out->v[1] = floor(in->v[1] * fractionprecision + 0.5f) * invfractionprecision;
- out->v[2] = floor(in->v[2] * fractionprecision + 0.5f) * invfractionprecision;
+ out[i].v[0] = floor(in[i].v[0] * fractionprecision + 0.5f) * invfractionprecision;
+ out[i].v[1] = floor(in[i].v[1] * fractionprecision + 0.5f) * invfractionprecision;
+ out[i].v[2] = floor(in[i].v[2] * fractionprecision + 0.5f) * invfractionprecision;
}
}
polyf_brush.planes = polyf_planes;
polyf_brush.supercontents = supercontents;
polyf_brush.points = polyf_points;
- Collision_SnapCopyPoints(numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
+ Collision_SnapCopyPoints(polyf_brush.numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
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, int q3surfaceflags, texture_t *texture, 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.supercontents = supercontents;
for (i = 0;i < polyf_brush.numplanes;i++)
{
- polyf_brush.planes[i].supercontents = supercontents;
polyf_brush.planes[i].q3surfaceflags = q3surfaceflags;
polyf_brush.planes[i].texture = texture;
}
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);
- Collision_SnapCopyPoints(3, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
- 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))
+ if (TriangleOverlapsBox(vertex3f + element3i[0]*3, vertex3f + element3i[1]*3, vertex3f + element3i[2]*3, segmentmins, segmentmaxs))
{
+ 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);
+ Collision_SnapCopyPoints(polyf_brush.numpoints, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
//Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush);
polyf_brush.numplanes = numpoints + 2;
//polyf_brush.points = (colpointf_t *)points;
polyf_brush.points = polyf_points;
- Collision_SnapCopyPoints(numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
+ Collision_SnapCopyPoints(polyf_brush.numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
polyf_brush.planes = polyf_planes;
polyf_brush.supercontents = supercontents;
Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
}
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);
- Collision_SnapCopyPoints(numpoints, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
- 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))
+ if (TriangleOverlapsBox(vertex3f + element3i[0]*3, vertex3 + [element3i[1]*3, vertex3f + element3i[2]*3, segmentmins, segmentmaxs))
{
+ 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);
+ Collision_SnapCopyPoints(polyf_brush.numpoints, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
//Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush);
Matrix4x4_Transform(polygonmatrixend, points + i * 3, polyf_brushend.points[i].v);
for (i = 0;i < polyf_brushstart.numplanes;i++)
{
- polyf_brushstart.planes[i].supercontents = supercontents;
polyf_brushstart.planes[i].q3surfaceflags = q3surfaceflags;
polyf_brushstart.planes[i].texture = texture;
}
- Collision_SnapCopyPoints(numpoints, polyf_pointsstart, polyf_pointsstart, COLLISION_SNAPSCALE, COLLISION_SNAP);
- Collision_SnapCopyPoints(numpoints, polyf_pointsend, polyf_pointsend, COLLISION_SNAPSCALE, COLLISION_SNAP);
+ Collision_SnapCopyPoints(polyf_brushstart.numpoints, polyf_pointsstart, polyf_pointsstart, COLLISION_SNAPSCALE, COLLISION_SNAP);
+ Collision_SnapCopyPoints(polyf_brushend.numpoints, polyf_pointsend, polyf_pointsend, COLLISION_SNAPSCALE, COLLISION_SNAP);
Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushstart);
Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushend);
#define MAX_BRUSHFORBOX 16
-static int brushforbox_index = 0;
+static unsigned int brushforbox_index = 0;
+// note: this relies on integer overflow to be consistent with modulo
+// MAX_BRUSHFORBOX, or in other words, MAX_BRUSHFORBOX must be a power of two!
static colpointf_t brushforbox_point[MAX_BRUSHFORBOX*8];
static colplanef_t brushforbox_plane[MAX_BRUSHFORBOX*6];
static colbrushf_t brushforbox_brush[MAX_BRUSHFORBOX];
int i;
for (i = 0;i < MAX_BRUSHFORBOX;i++)
{
- brushforbox_brush[i].supercontents = SUPERCONTENTS_SOLID;
brushforbox_brush[i].numpoints = 8;
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;
brush->supercontents = supercontents;
for (j = 0;j < brush->numplanes;j++)
{
- brush->planes[j].supercontents = supercontents;
brush->planes[j].q3surfaceflags = q3surfaceflags;
brush->planes[j].texture = texture;
brush->planes[j].dist = furthestplanedist_float(brush->planes[j].normal, brush->points, brush->numpoints);
if (deviationdist > sphereradius*sphereradius)
return 1; // miss (off to the side)
// nudge back to find the correct impact distance
- impactdist += deviationdist - sphereradius;
+ impactdist -= sphereradius - deviationdist/sphereradius;
if (impactdist >= linelength)
return 1; // miss (not close enough)
if (impactdist < 0)
// (the main fraction remains perfect)
trace->fraction = f - collision_impactnudge.value * d;
+ if (collision_prefernudgedfraction.integer)
+ trace->realfraction = trace->fraction;
+
// store the new trace plane (because collisions only happen from
// the front this is always simply the triangle normal, never flipped)
d = 1.0 / sqrt(faceplanenormallength2);
maxs[2] += 1;
}
+//===========================================
+
+void Collision_ClipToGenericEntity(trace_t *trace, model_t *model, int frame, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask)
+{
+ float tempnormal[3], starttransformed[3], endtransformed[3];
+
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = trace->realfraction = 1;
+ VectorCopy(end, trace->endpos);
+
+ Matrix4x4_Transform(inversematrix, start, starttransformed);
+ Matrix4x4_Transform(inversematrix, end, endtransformed);
+#if COLLISIONPARANOID >= 3
+ Con_Printf("trans(%f %f %f -> %f %f %f, %f %f %f -> %f %f %f)", start[0], start[1], start[2], starttransformed[0], starttransformed[1], starttransformed[2], end[0], end[1], end[2], endtransformed[0], endtransformed[1], endtransformed[2]);
+#endif
+
+ if (model && model->TraceBox)
+ model->TraceBox(model, bound(0, frame, (model->numframes - 1)), trace, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask);
+ else
+ Collision_ClipTrace_Box(trace, bodymins, bodymaxs, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
+ trace->fraction = bound(0, trace->fraction, 1);
+ trace->realfraction = bound(0, trace->realfraction, 1);
+
+ if (trace->fraction < 1)
+ {
+ VectorLerp(start, trace->fraction, end, trace->endpos);
+ VectorCopy(trace->plane.normal, tempnormal);
+ Matrix4x4_Transform3x3(matrix, tempnormal, trace->plane.normal);
+ // FIXME: should recalc trace->plane.dist
+ }
+}
+
+void Collision_ClipToWorld(trace_t *trace, model_t *model, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontents)
+{
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = trace->realfraction = 1;
+ if (model && model->TraceBox)
+ model->TraceBox(model, 0, trace, start, mins, maxs, end, hitsupercontents);
+ trace->fraction = bound(0, trace->fraction, 1);
+ trace->realfraction = bound(0, trace->realfraction, 1);
+ VectorLerp(start, trace->fraction, end, trace->endpos);
+}
+
+void Collision_CombineTraces(trace_t *cliptrace, const trace_t *trace, void *touch, qboolean isbmodel)
+{
+ // take the 'best' answers from the new trace and combine with existing data
+ if (trace->allsolid)
+ cliptrace->allsolid = true;
+ if (trace->startsolid)
+ {
+ if (isbmodel)
+ cliptrace->bmodelstartsolid = true;
+ cliptrace->startsolid = true;
+ if (cliptrace->realfraction == 1)
+ cliptrace->ent = touch;
+ }
+ // don't set this except on the world, because it can easily confuse
+ // monsters underwater if there's a bmodel involved in the trace
+ // (inopen && inwater is how they check water visibility)
+ //if (trace->inopen)
+ // cliptrace->inopen = true;
+ if (trace->inwater)
+ cliptrace->inwater = true;
+ if (trace->realfraction < cliptrace->realfraction)
+ {
+ cliptrace->fraction = trace->fraction;
+ cliptrace->realfraction = trace->realfraction;
+ VectorCopy(trace->endpos, cliptrace->endpos);
+ cliptrace->plane = trace->plane;
+ cliptrace->ent = touch;
+ cliptrace->hitsupercontents = trace->hitsupercontents;
+ cliptrace->hitq3surfaceflags = trace->hitq3surfaceflags;
+ cliptrace->hittexture = trace->hittexture;
+ }
+ cliptrace->startsupercontents |= trace->startsupercontents;
+}