Implement FTEQW's faster SV_RecursiveHullCheck

This commit is contained in:
cypress 2023-07-22 12:02:04 -04:00
parent 6cc9131eb9
commit fcc75883c2
6 changed files with 172 additions and 120 deletions

View File

@ -52,7 +52,7 @@ void TraceLine (vec3_t start, vec3_t end, vec3_t impact)
trace_t trace;
memset (&trace, 0, sizeof(trace));
SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, start, end, &trace);
VectorCopy (trace.endpos, impact);
}

View File

@ -285,7 +285,7 @@ qboolean TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal)
trace_t trace;
memset (&trace, 0, sizeof(trace));
if (!SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, start, end, &trace))
if (!SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, start, end, &trace))
{
if (trace.fraction < 1)
{

View File

@ -3144,7 +3144,7 @@ void QMB_LaserSight (void)
memset (&trace, 0, sizeof(trace_t));
trace.fraction = 1;
SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, start, dest, &trace);
SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, start, dest, &trace);
start[2]+=cl.crouch;
AddParticle (p_streaktrail, start, 1, 2, 0.02, color, trace.endpos);// draw the line
@ -3207,7 +3207,7 @@ void QMB_LightningBeam (vec3_t start, vec3_t end)
if (qmb_initialized && r_part_sparks.value)
{
memset (&trace, 0, sizeof(trace_t));
if (!SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, start, end, &trace))
if (!SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, start, end, &trace))
{
if (trace.fraction < 1)
{

View File

@ -62,6 +62,15 @@ void _VectorCopy (vec3_t in, vec3_t out);
void vectoangles (vec3_t vec, vec3_t ang);
#define VectorInterpolate(v1, _frac, v2, v) \
do { \
_mathlib_temp_float1 = _frac; \
\
(v)[0] = (v1)[0] + _mathlib_temp_float1 * ((v2)[0] - (v1)[0]);\
(v)[1] = (v1)[1] + _mathlib_temp_float1 * ((v2)[1] - (v1)[1]);\
(v)[2] = (v1)[2] + _mathlib_temp_float1 * ((v2)[2] - (v1)[2]);\
} while(0)
int VectorCompare (vec3_t v1, vec3_t v2);
vec_t Length (vec3_t v);
void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);

View File

@ -586,30 +586,165 @@ LINE TESTING IN HULLS
===============================================================================
*/
enum
{
rht_solid,
rht_empty,
rht_impact
};
struct rhtctx_s
{
unsigned int hitcontents;
vec3_t start, end;
dclipnode_t *clipnodes;
mplane_t *planes;
};
// 1/32 epsilon to keep floating point happy
#define DIST_EPSILON (0.03125)
/*
==================
Q1BSP_RecursiveHullTrace
This does the core traceline/tracebox logic.
This version is from FTE and attempts to be more numerically stable than vanilla.
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.
==================
*/
static int Q1BSP_RecursiveHullTrace (struct rhtctx_s *ctx, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
{
dclipnode_t *node;
mplane_t *plane;
float t1, t2;
vec3_t mid;
int side;
float midf;
int rht;
reenter:
if (num < 0)
{
/*hit a leaf*/
if (num == CONTENTS_SOLID)
{
if (trace->allsolid)
trace->startsolid = true;
return rht_solid;
}
else
{
trace->allsolid = false;
if (num == CONTENTS_EMPTY)
trace->inopen = true;
else if (num != CONTENTS_SOLID)
trace->inwater = true;
return rht_empty;
}
}
/*its a node*/
/*get the node info*/
node = ctx->clipnodes + num;
plane = ctx->planes + node->planenum;
if (plane->type < 3)
{
t1 = p1[plane->type] - plane->dist;
t2 = p2[plane->type] - plane->dist;
}
else
{
t1 = DoublePrecisionDotProduct (plane->normal, p1) - plane->dist;
t2 = DoublePrecisionDotProduct (plane->normal, p2) - plane->dist;
}
/*if its completely on one side, resume on that side*/
if (t1 >= 0 && t2 >= 0)
{
//return Q1BSP_RecursiveHullTrace (hull, node->children[0], p1f, p2f, p1, p2, trace);
num = node->children[0];
goto reenter;
}
if (t1 < 0 && t2 < 0)
{
//return Q1BSP_RecursiveHullTrace (hull, node->children[1], p1f, p2f, p1, p2, trace);
num = node->children[1];
goto reenter;
}
if (plane->type < 3)
{
t1 = ctx->start[plane->type] - plane->dist;
t2 = ctx->end[plane->type] - plane->dist;
}
else
{
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;
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);
}
t1 = DoublePrecisionDotProduct (trace->plane.normal, ctx->start) - trace->plane.dist;
t2 = DoublePrecisionDotProduct (trace->plane.normal, ctx->end) - trace->plane.dist;
midf = (t1 - DIST_EPSILON) / (t1 - t2);
midf = CLAMP(0, midf, 1);
trace->fraction = midf;
VectorCopy (mid, trace->endpos);
VectorInterpolate(ctx->start, midf, ctx->end, trace->endpos);
return rht_impact;
}
/*
==================
SV_RecursiveHullCheck
==================
*/
qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
qboolean SV_RecursiveHullCheck (hull_t *hull, int num, vec3_t p1, vec3_t p2, trace_t *trace)
{
dclipnode_t *node;
mplane_t *plane;
float t1, t2;
float frac;
int i;
vec3_t mid;
int side;
float midf;
// check for empty
if (num < 0)
if (p1[0] == p2[0] && p1[1] == p2[1] && p1[2] == p2[2])
{
if (num != CONTENTS_SOLID)
/*points cannot cross planes, so do it faster*/
int c = SV_HullPointContents(hull, hull->firstclipnode, p1);
//trace->contents = c;
if (c != CONTENTS_SOLID)
{
trace->allsolid = false;
if (num == CONTENTS_EMPTY)
@ -619,112 +754,17 @@ qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec
}
else
trace->startsolid = true;
return true; // empty
}
if (num < hull->firstclipnode || num > hull->lastclipnode)
Sys_Error ("SV_RecursiveHullCheck: bad node number");
//
// find the point distances
//
node = hull->clipnodes + num;
plane = hull->planes + node->planenum;
if (plane->type < 3)
{
t1 = p1[plane->type] - plane->dist;
t2 = p2[plane->type] - plane->dist;
return true;
}
else
{
t1 = DotProduct (plane->normal, p1) - plane->dist;
t2 = DotProduct (plane->normal, p2) - plane->dist;
struct rhtctx_s ctx;
VectorCopy(p1, ctx.start);
VectorCopy(p2, ctx.end);
ctx.clipnodes = hull->clipnodes;
ctx.planes = hull->planes;
return Q1BSP_RecursiveHullTrace(&ctx, hull->firstclipnode, 0, 1, p1, p2, trace) != rht_impact;
}
#if 1
if (t1 >= 0 && t2 >= 0)
return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace);
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 false;
}
#endif
if (SV_HullPointContents (hull, node->children[side^1], mid)
!= 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);
trace->plane.dist = plane->dist;
}
else
{
VectorSubtract (vec3_origin, plane->normal, trace->plane.normal);
trace->plane.dist = -plane->dist;
}
while (SV_HullPointContents (hull, hull->firstclipnode, mid)
== CONTENTS_SOLID)
{ // shouldn't really happen, but does occasionally
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]);
}
trace->fraction = midf;
VectorCopy (mid, trace->endpos);
return false;
}
@ -779,7 +819,7 @@ trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t max
#endif
// trace a line through the apropriate clipping hull
SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace);
SV_RecursiveHullCheck (hull, hull->firstclipnode, start_l, end_l, &trace);
#ifdef QUAKE2
// rotate endpos back to world frame of reference

View File

@ -64,6 +64,9 @@ int SV_TruePointContents (vec3_t p);
edict_t *SV_TestEntityPosition (edict_t *ent);
qboolean SV_RecursiveHullCheck (hull_t *hull, int num, vec3_t p1, vec3_t p2, trace_t *trace);
trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict);
// mins and maxs are reletive