mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-03-21 18:01:15 +00:00
Make MOD_TraceLine behave "correctly".
MOD_TraceLine now behaves the same as id's SV_RecursiveHullCheck (from WinQuake). This means that even if the trace would escape from solid space into non-solid space, the trace is treated as allsolid if it crosses from one solid space to another before hitting the empty space. trace-id.c is used only for establishing the behaviour of id's code.
This commit is contained in:
parent
c2af7896bd
commit
981622f969
5 changed files with 432 additions and 26 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -131,6 +131,7 @@ core
|
|||
# /libs/image/
|
||||
|
||||
# /libs/models/
|
||||
/libs/models/testclip
|
||||
|
||||
# /libs/models/alias/
|
||||
|
||||
|
|
|
@ -4,9 +4,16 @@ SUBDIRS= alias brush sprite
|
|||
CFLAGS+= @PREFER_PIC@
|
||||
INCLUDES= -I$(top_srcdir)/include
|
||||
|
||||
check_PROGRAMS=testclip
|
||||
|
||||
testclip_SOURCES=testclip.c
|
||||
testclip_LDADD= libQFmodels.la $(top_builddir)/libs/util/libQFutil.la
|
||||
testclip_DEPENDENCIES=
|
||||
|
||||
lib_LTLIBRARIES= libQFmodels.la @VID_MODEL_TARGETS@
|
||||
EXTRA_LTLIBRARIES= \
|
||||
libQFmodels_gl.la libQFmodels_sw.la
|
||||
EXTRA_LTLIBRARIES= libQFmodels_gl.la libQFmodels_sw.la
|
||||
EXTRA_DIST= trace-id.c
|
||||
TESTS=$(check_PROGRAMS)
|
||||
|
||||
models_sources = clip_hull.c model.c trace.c
|
||||
|
||||
|
|
237
libs/models/testclip.c
Normal file
237
libs/models/testclip.c
Normal file
|
@ -0,0 +1,237 @@
|
|||
#include <stdlib.h>
|
||||
#include "QF/va.h"
|
||||
|
||||
#include "getopt.h"
|
||||
#include "world.h"
|
||||
|
||||
#undef DIST_EPSILON
|
||||
#define DIST_EPSILON 0
|
||||
|
||||
#ifdef TEST_ID
|
||||
# include "trace-id.c"
|
||||
#else
|
||||
# include "trace.c"
|
||||
#endif
|
||||
|
||||
mclipnode_t clipnodes0[] = {
|
||||
{ 0, { 1, -1}},
|
||||
{ 1, {-1, -2}},
|
||||
};
|
||||
|
||||
mplane_t planes0[] = {
|
||||
{{1, 0, 0}, 0, 0, 0}, // 0
|
||||
{{0.8, 0, 0.6}, 0, 4, 0}, // 1
|
||||
};
|
||||
|
||||
hull_t hull0 = {
|
||||
clipnodes0,
|
||||
planes0,
|
||||
0,
|
||||
1,
|
||||
{0, 0, 0},
|
||||
{0, 0, 0},
|
||||
};
|
||||
|
||||
mclipnode_t clipnodes1[] = {
|
||||
{ 0, { 1, -2}},
|
||||
{ 1, { 2, -2}},
|
||||
{ 2, {-2, -1}},
|
||||
};
|
||||
|
||||
mplane_t planes1[] = {
|
||||
{{1, 0, 0}, -32, 0, 0},
|
||||
{{1, 0, 0}, 32, 0, 0},
|
||||
{{1, 0, 0}, 48, 0, 0},
|
||||
};
|
||||
|
||||
hull_t hull1 = {
|
||||
clipnodes1,
|
||||
planes1,
|
||||
0,
|
||||
2,
|
||||
{0, 0, 0},
|
||||
{0, 0, 0},
|
||||
};
|
||||
|
||||
mclipnode_t clipnodes2[] = {
|
||||
{ 0, { 2, 1}},
|
||||
{ 1, {-2, -2}},
|
||||
{ 2, {-2, -1}},
|
||||
};
|
||||
|
||||
mplane_t planes2[] = {
|
||||
{{1, 0, 0}, 32, 0, 0},
|
||||
{{1, 0, 0}, -32, 0, 0},
|
||||
{{1, 0, 0}, 48, 0, 0},
|
||||
};
|
||||
|
||||
hull_t hull2 = {
|
||||
clipnodes2,
|
||||
planes2,
|
||||
0,
|
||||
2,
|
||||
{0, 0, 0},
|
||||
{0, 0, 0},
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
vec3_t extents;
|
||||
} box_t;
|
||||
|
||||
typedef struct {
|
||||
const char *desc;
|
||||
box_t *box;
|
||||
hull_t *hull;
|
||||
vec3_t start;
|
||||
vec3_t end;
|
||||
struct {
|
||||
float frac;
|
||||
qboolean allsolid;
|
||||
qboolean startsolid;
|
||||
qboolean inopen;
|
||||
qboolean inwater;
|
||||
} expect;
|
||||
} test_t;
|
||||
|
||||
box_t point = { {0, 0, 0} };
|
||||
box_t player = { {16, 16, 28} };
|
||||
|
||||
test_t tests[] = {
|
||||
{0, &point, &hull1, {-64, 0, 0}, { 64, 0, 0}, { 1, 1, 1, 0, 0}},
|
||||
{0, &point, &hull1, { 0, 0, 0}, { 40, 0, 0}, { 1, 0, 1, 1, 0}},
|
||||
{0, &point, &hull1, { 40, 0, 0}, {-88, 0, 0}, {0.0625, 0, 0, 1, 0}},
|
||||
{0, &point, &hull1, { 0, 0, 0}, { 64, 0, 0}, { 0.75, 0, 1, 1, 0}},
|
||||
|
||||
{0, &point, &hull2, {-64, 0, 0}, { 64, 0, 0}, { 1, 1, 1, 0, 0}},
|
||||
{0, &point, &hull2, { 0, 0, 0}, { 40, 0, 0}, { 1, 0, 1, 1, 0}},
|
||||
{0, &point, &hull2, { 40, 0, 0}, {-88, 0, 0}, {0.0625, 0, 0, 1, 0}},
|
||||
{0, &point, &hull2, { 0, 0, 0}, { 64, 0, 0}, { 0.75, 0, 1, 1, 0}},
|
||||
};
|
||||
#define num_tests (sizeof (tests) / sizeof (tests[0]))
|
||||
|
||||
static int test_enabled[num_tests] = { 0 };
|
||||
|
||||
int verbose = 0;
|
||||
|
||||
static trace_t
|
||||
do_trace (box_t *box, hull_t *hull, vec3_t start, vec3_t end)
|
||||
{
|
||||
trace_t trace;
|
||||
|
||||
trace.allsolid = true;
|
||||
trace.startsolid = false;
|
||||
trace.inopen = false;
|
||||
trace.inwater = false;
|
||||
trace.fraction = 1;
|
||||
VectorCopy (box->extents, trace.extents);
|
||||
trace.isbox = true;
|
||||
VectorCopy (end, trace.endpos);
|
||||
MOD_TraceLine (hull, 0, start, end, &trace);
|
||||
return trace;
|
||||
}
|
||||
|
||||
static int
|
||||
run_test (test_t *test)
|
||||
{
|
||||
const char *desc;
|
||||
vec3_t end;
|
||||
int res = 0;
|
||||
char *expect;
|
||||
char *got;
|
||||
static int output = 0;
|
||||
|
||||
VectorSubtract (test->end, test->start, end);
|
||||
VectorMultAdd (test->start, test->expect.frac, end, end);
|
||||
expect = nva ("expect: (%g %g %g) -> (%g %g %g) => (%g %g %g)"
|
||||
" %3g %d %d %d %d",
|
||||
test->start[0], test->start[1], test->start[2],
|
||||
test->end[0], test->end[1], test->end[2],
|
||||
end[0], end[1], end[2],
|
||||
test->expect.frac,
|
||||
test->expect.allsolid, test->expect.startsolid,
|
||||
test->expect.inopen, test->expect.inwater);
|
||||
trace_t trace = do_trace (test->box, test->hull, test->start, test->end);
|
||||
got = nva (" got: (%g %g %g) -> (%g %g %g) => (%g %g %g)"
|
||||
" %3g %d %d %d %d",
|
||||
test->start[0], test->start[1], test->start[2],
|
||||
test->end[0], test->end[1], test->end[2],
|
||||
trace.endpos[0], trace.endpos[1], trace.endpos[2],
|
||||
trace.fraction,
|
||||
trace.allsolid, trace.startsolid,
|
||||
trace.inopen, trace.inwater);
|
||||
if (VectorCompare (end, trace.endpos)
|
||||
&& test->expect.frac == trace.fraction
|
||||
&& test->expect.allsolid == trace.allsolid
|
||||
&& test->expect.startsolid == trace.startsolid
|
||||
&& test->expect.inopen == trace.inopen
|
||||
&& test->expect.inwater == trace.inwater)
|
||||
res = 1;
|
||||
|
||||
if (test->desc)
|
||||
desc = va ("(%d) %s", (int)(long)(test - tests), test->desc);
|
||||
else
|
||||
desc = va ("test #%d", (int)(long)(test - tests));
|
||||
if (verbose >= 0 || !res) {
|
||||
if (output)
|
||||
puts("");
|
||||
output = 1;
|
||||
puts (expect);
|
||||
puts (got);
|
||||
printf ("%s: %s\n", res ? "PASS" : "FAIL", desc);
|
||||
}
|
||||
free (expect);
|
||||
free (got);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
// vec3_t start, end;
|
||||
int c;
|
||||
size_t i, test;
|
||||
int pass = 1;
|
||||
|
||||
while ((c = getopt (argc, argv, "qvt:")) != EOF) {
|
||||
switch (c) {
|
||||
case 'q':
|
||||
verbose--;
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 't':
|
||||
test = atoi (optarg);
|
||||
if (test < num_tests) {
|
||||
test_enabled[test] = 1;
|
||||
} else {
|
||||
fprintf (stderr, "Bad test number (0 - %zd)\n", num_tests);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf (stderr, "-q (quiet) -v (verbose) and/or -t TEST "
|
||||
"(test number)\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < num_tests; i++)
|
||||
if (test_enabled[i])
|
||||
break;
|
||||
if (i == num_tests) {
|
||||
for (i = 0; i < num_tests; i++)
|
||||
test_enabled[i] = 1;
|
||||
}
|
||||
|
||||
if (verbose > 0)
|
||||
printf ("start -> end => stop frac allsolid startsolid inopen "
|
||||
"inwater\n");
|
||||
for (i = 0; i < num_tests; i++) {
|
||||
if (!test_enabled[i])
|
||||
continue;
|
||||
pass &= run_test (&tests[i]);
|
||||
}
|
||||
|
||||
return !pass;
|
||||
}
|
155
libs/models/trace-id.c
Normal file
155
libs/models/trace-id.c
Normal file
|
@ -0,0 +1,155 @@
|
|||
int SV_HullPointContents (hull_t *hull, int num, const vec3_t p)
|
||||
{
|
||||
float d;
|
||||
mclipnode_t *node;
|
||||
mplane_t *plane;
|
||||
|
||||
while (num >= 0)
|
||||
{
|
||||
if (num < hull->firstclipnode || num > hull->lastclipnode)
|
||||
Sys_Error ("SV_HullPointContents: bad node number");
|
||||
|
||||
node = hull->clipnodes + num;
|
||||
plane = hull->planes + node->planenum;
|
||||
|
||||
if (plane->type < 3)
|
||||
d = p[plane->type] - plane->dist;
|
||||
else
|
||||
d = DotProduct (plane->normal, p) - plane->dist;
|
||||
if (d < 0)
|
||||
num = node->children[1];
|
||||
else
|
||||
num = node->children[0];
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
// 1/32 epsilon to keep floating point happy
|
||||
#ifndef DIST_EPSILON
|
||||
#define DIST_EPSILON (0.03125)
|
||||
#endif
|
||||
|
||||
qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, const vec3_t p1, const vec3_t p2, trace_t *trace)
|
||||
{
|
||||
mclipnode_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 (num != CONTENTS_SOLID)
|
||||
{
|
||||
trace->allsolid = false;
|
||||
if (num == CONTENTS_EMPTY)
|
||||
trace->inopen = true;
|
||||
else
|
||||
trace->inwater = true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
t1 = DotProduct (plane->normal, p1) - plane->dist;
|
||||
t2 = DotProduct (plane->normal, p2) - plane->dist;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
MOD_TraceLine (hull_t *hull, int num,
|
||||
const vec3_t start_point, const vec3_t end_point,
|
||||
trace_t *trace)
|
||||
{
|
||||
SV_RecursiveHullCheck (hull, num, 0, 1, start_point, end_point, trace);
|
||||
}
|
|
@ -50,7 +50,9 @@ static __attribute__ ((used)) const char rcsid[] =
|
|||
/* LINE TESTING IN HULLS */
|
||||
|
||||
// 1/32 epsilon to keep floating point happy
|
||||
#ifndef DIST_EPSILON
|
||||
#define DIST_EPSILON (0.03125)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
vec3_t end;
|
||||
|
@ -92,7 +94,8 @@ MOD_TraceLine (hull_t *hull, int num,
|
|||
{
|
||||
vec_t start_dist, end_dist, frac;
|
||||
vec3_t start, end, dist;
|
||||
int side, empty, solid;
|
||||
int side;
|
||||
qboolean empty, solid;
|
||||
tracestack_t *tstack;
|
||||
tracestack_t tracestack[256];
|
||||
mclipnode_t *node;
|
||||
|
@ -106,40 +109,41 @@ MOD_TraceLine (hull_t *hull, int num,
|
|||
solid = 0;
|
||||
split_plane = 0;
|
||||
|
||||
trace->allsolid = false;
|
||||
trace->startsolid = false;
|
||||
trace->inopen = false;
|
||||
trace->inwater = false;
|
||||
trace->fraction = 1.0;
|
||||
|
||||
while (1) {
|
||||
while (num < 0) {
|
||||
if (!solid && num != CONTENTS_SOLID) {
|
||||
empty = 1;
|
||||
if (num == CONTENTS_SOLID) {
|
||||
if (!empty && !solid) {
|
||||
// this is the first leaf visited, thus the start leaf
|
||||
trace->startsolid = solid = true;
|
||||
} else if (solid) {
|
||||
// if crossing from one solid leaf to another, treat the
|
||||
// whole trace as solid (this is what id does)
|
||||
trace->allsolid = true;
|
||||
return;
|
||||
} else if (empty) {
|
||||
// crossing from an empty leaf to a solid leaf: the trace
|
||||
// has collided.
|
||||
calc_impact (trace, start_point, end_point, split_plane);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
empty = true;
|
||||
solid = false;
|
||||
if (num == CONTENTS_EMPTY)
|
||||
trace->inopen = true;
|
||||
else
|
||||
trace->inwater = true;
|
||||
} else if (!empty && num == CONTENTS_SOLID) {
|
||||
solid = 1;
|
||||
} else if (solid && num != CONTENTS_SOLID) {
|
||||
//FIXME not sure what I want
|
||||
//made it out of the solid and into open space, continue
|
||||
//on as if we were always in empty space
|
||||
empty = 1;
|
||||
solid = 0;
|
||||
trace->startsolid = 1;
|
||||
if (num == CONTENTS_EMPTY)
|
||||
trace->inopen = true;
|
||||
else
|
||||
trace->inwater = true;
|
||||
} else if (empty/* || solid*/) {//FIXME not sure what I want
|
||||
// DONE!
|
||||
trace->allsolid = solid & (num == CONTENTS_SOLID);
|
||||
trace->startsolid = solid;
|
||||
calc_impact (trace, start_point, end_point, split_plane);
|
||||
return;
|
||||
}
|
||||
|
||||
// pop up the stack for a back side
|
||||
if (tstack-- == tracestack) {
|
||||
// we've finished.
|
||||
trace->allsolid = solid & (num == CONTENTS_SOLID);
|
||||
trace->startsolid = solid;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -171,6 +175,8 @@ MOD_TraceLine (hull_t *hull, int num,
|
|||
continue;
|
||||
}
|
||||
|
||||
// cross the plane
|
||||
|
||||
side = start_dist < 0;
|
||||
frac = start_dist / (start_dist - end_dist);
|
||||
frac = bound (0, frac, 1);
|
||||
|
|
Loading…
Reference in a new issue