Replaced SV_RecursiveHullCheck with a more numerically stable and faster alternative.

This commit is contained in:
Spike 2018-07-15 00:52:34 +01:00 committed by Shpoike
parent 8d84bf8d2f
commit 706dc546a3

View file

@ -582,50 +582,72 @@ LINE TESTING IN HULLS
=============================================================================== ===============================================================================
*/ */
enum
{
rht_solid,
rht_empty,
rht_impact
};
struct rhtctx_s
{
vec3_t start, end;
mclipnode_t *clipnodes;
mplane_t *planes;
};
#define VectorNegate(a,b) ((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2])
#define FloatInterpolate(a, bness, b, c) ((c) = (a) + (b - a)*bness)
#define VectorInterpolate(a, bness, b, c) FloatInterpolate((a)[0], bness, (b)[0], (c)[0]),FloatInterpolate((a)[1], bness, (b)[1], (c)[1]),FloatInterpolate((a)[2], bness, (b)[2], (c)[2])
/* /*
================== ==================
SV_RecursiveHullCheck Q1BSP_RecursiveHullTrace
Spike -- note that the pointcontents in this function are completely redundant. This does the core traceline/tracebox logic.
This function should instead return the state of the contents of the mid position. This version is from FTE and attempts to be more numerically stable than vanilla.
This would avoid all redundant recursion. This is achieved by recursing at the actual decision points instead of vanilla's habit of vanilla's habit of using points that are outside of the child's volume.
It also uses itself to test solidity on the other side of the node, which ensures consistent precision.
The actual collision point is (still) biased by an epsilon, so the end point shouldn't be inside walls either way.
FTE's version 'should' be more compatible with vanilla than DP's (which doesn't take care with allsolid).
ezQuake also has a version of this logic, but I trust mine more.
================== ==================
*/ */
qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) static int Q1BSP_RecursiveHullTrace (struct rhtctx_s *ctx, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
{ {
mclipnode_t *node; //johnfitz -- was dclipnode_t mclipnode_t *node;
mplane_t *plane; mplane_t *plane;
float t1, t2; float t1, t2;
float frac;
int i;
vec3_t mid; vec3_t mid;
int side; int side;
float midf; float midf;
int rht;
reenter:
// check for empty
if (num < 0) if (num < 0)
{ {
if (num != CONTENTS_SOLID) /*hit a leaf*/
if (num == CONTENTS_SOLID)
{
if (trace->allsolid)
trace->startsolid = true;
return rht_solid;
}
else
{ {
trace->allsolid = false; trace->allsolid = false;
if (num == CONTENTS_EMPTY) if (num == CONTENTS_EMPTY)
trace->inopen = true; trace->inopen = true;
else else
trace->inwater = true; trace->inwater = true;
return rht_empty;
} }
else
trace->startsolid = true;
return true; // empty
} }
if (num < hull->firstclipnode || num > hull->lastclipnode) /*its a node*/
Sys_Error ("SV_RecursiveHullCheck: bad node number");
// /*get the node info*/
// find the point distances node = ctx->clipnodes + num;
// plane = ctx->planes + node->planenum;
node = hull->clipnodes + num;
plane = hull->planes + node->planenum;
if (plane->type < 3) if (plane->type < 3)
{ {
@ -634,95 +656,114 @@ qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec
} }
else else
{ {
t1 = DoublePrecisionDotProduct (plane->normal, p1) - plane->dist; t1 = DotProduct (plane->normal, p1) - plane->dist;
t2 = DoublePrecisionDotProduct (plane->normal, p2) - plane->dist; t2 = DotProduct (plane->normal, p2) - plane->dist;
} }
#if 1 /*if its completely on one side, resume on that side*/
if (t1 >= 0 && t2 >= 0) if (t1 >= 0 && t2 >= 0)
return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); {
//return Q1BSP_RecursiveHullTrace (hull, node->children[0], p1f, p2f, p1, p2, trace);
num = node->children[0];
goto reenter;
}
if (t1 < 0 && t2 < 0) if (t1 < 0 && t2 < 0)
return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace);
#else
if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) )
return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace);
if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) )
return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace);
#endif
// put the crosspoint DIST_EPSILON pixels on the near side
if (t1 < 0)
frac = (t1 + DIST_EPSILON)/(t1-t2);
else
frac = (t1 - DIST_EPSILON)/(t1-t2);
if (frac < 0)
frac = 0;
if (frac > 1)
frac = 1;
midf = p1f + (p2f - p1f)*frac;
for (i=0 ; i<3 ; i++)
mid[i] = p1[i] + frac*(p2[i] - p1[i]);
side = (t1 < 0);
// move up to the node
if (!SV_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) )
return false;
#ifdef PARANOID
if (SV_HullPointContents (sv_hullmodel, mid, node->children[side])
== CONTENTS_SOLID)
{ {
Con_Printf ("mid PointInHullSolid\n"); //return Q1BSP_RecursiveHullTrace (hull, node->children[1], p1f, p2f, p1, p2, trace);
return false; num = node->children[1];
goto reenter;
} }
#endif
if (SV_HullPointContents (hull, node->children[side^1], mid) if (plane->type < 3)
!= CONTENTS_SOLID)
// go past the node
return SV_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace);
if (trace->allsolid)
return false; // never got out of the solid area
//==================
// the other side of the node is solid, this is the impact point
//==================
if (!side)
{ {
VectorCopy (plane->normal, trace->plane.normal); t1 = ctx->start[plane->type] - plane->dist;
trace->plane.dist = plane->dist; t2 = ctx->end[plane->type] - plane->dist;
} }
else else
{ {
VectorSubtract (vec3_origin, plane->normal, trace->plane.normal); t1 = DotProduct (plane->normal, ctx->start) - plane->dist;
t2 = DotProduct (plane->normal, ctx->end) - plane->dist;
}
side = t1 < 0;
midf = t1 / (t1 - t2);
if (midf < p1f) midf = p1f;
if (midf > p2f) midf = p2f;
VectorInterpolate(ctx->start, midf, ctx->end, mid);
rht = Q1BSP_RecursiveHullTrace(ctx, node->children[side], p1f, midf, p1, mid, trace);
if (rht != rht_empty && !trace->allsolid)
return rht;
rht = Q1BSP_RecursiveHullTrace(ctx, node->children[side^1], midf, p2f, mid, p2, trace);
if (rht != rht_solid)
return rht;
if (side)
{
/*we impacted the back of the node, so flip the plane*/
trace->plane.dist = -plane->dist; trace->plane.dist = -plane->dist;
VectorNegate(plane->normal, trace->plane.normal);
midf = (t1 + DIST_EPSILON) / (t1 - t2);
}
else
{
/*we impacted the front of the node*/
trace->plane.dist = plane->dist;
VectorCopy(plane->normal, trace->plane.normal);
midf = (t1 - DIST_EPSILON) / (t1 - t2);
} }
while (SV_HullPointContents (hull, hull->firstclipnode, mid) t1 = DotProduct (trace->plane.normal, ctx->start) - trace->plane.dist;
== CONTENTS_SOLID) t2 = DotProduct (trace->plane.normal, ctx->end) - trace->plane.dist;
{ // shouldn't really happen, but does occasionally midf = (t1 - DIST_EPSILON) / (t1 - t2);
frac -= 0.1;
if (frac < 0)
{
trace->fraction = midf;
VectorCopy (mid, trace->endpos);
Con_DPrintf ("backup past 0\n");
return false;
}
midf = p1f + (p2f - p1f)*frac;
for (i=0 ; i<3 ; i++)
mid[i] = p1[i] + frac*(p2[i] - p1[i]);
}
midf = CLAMP(0, midf, 1);
trace->fraction = midf; trace->fraction = midf;
VectorCopy (mid, trace->endpos); VectorCopy (mid, trace->endpos);
VectorInterpolate(ctx->start, midf, ctx->end, trace->endpos);
return false; return rht_impact;
} }
/*
==================
SV_RecursiveHullCheck
Decides if its a simple point test, or does a slightly more expensive check.
==================
*/
qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
{
if (p1[0]==p2[0] && p1[1]==p2[1] && p1[2]==p2[2])
{
/*points cannot cross planes, so do it faster*/
switch(SV_HullPointContents(hull, num, p1))
{
case CONTENTS_SOLID:
trace->startsolid = true;
break;
case CONTENTS_EMPTY:
trace->allsolid = false;
trace->inopen = true;
break;
default:
trace->allsolid = false;
trace->inwater = true;
break;
}
return true;
}
else
{
struct rhtctx_s ctx;
VectorCopy(p1, ctx.start);
VectorCopy(p2, ctx.end);
ctx.clipnodes = hull->clipnodes;
ctx.planes = hull->planes;
return Q1BSP_RecursiveHullTrace(&ctx, num, p1f, p2f, p1, p2, trace) != rht_impact;
}
}
/* /*
================== ==================