646 lines
15 KiB
C
646 lines
15 KiB
C
//=======================================================================
|
|
// Copyright XashXT Group 2007 ©
|
|
// cm_trace.c - combined tracing
|
|
//=======================================================================
|
|
|
|
#include "cm_local.h"
|
|
#include "basefiles.h"
|
|
|
|
#define DIST_EPSILON (0.03125) // 1/32 epsilon to keep floating point happy
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
CM INTERNAL MATH
|
|
|
|
===============================================================================
|
|
*/
|
|
/*
|
|
================
|
|
RotatePoint
|
|
================
|
|
*/
|
|
void RotatePoint(vec3_t point, const vec3_t matrix[3])
|
|
{
|
|
vec3_t tvec;
|
|
|
|
VectorCopy( point, tvec );
|
|
point[0] = DotProduct( matrix[0], tvec );
|
|
point[1] = DotProduct( matrix[1], tvec );
|
|
point[2] = DotProduct( matrix[2], tvec );
|
|
}
|
|
|
|
/*
|
|
================
|
|
TransposeMatrix
|
|
================
|
|
*/
|
|
void TransposeMatrix( const vec3_t matrix[3], vec3_t transpose[3])
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
for( j = 0; j < 3; j++)
|
|
transpose[i][j] = matrix[j][i];
|
|
}
|
|
|
|
/*
|
|
================
|
|
CreateRotationMatrix
|
|
================
|
|
*/
|
|
void CreateRotationMatrix(const vec3_t angles, vec3_t matrix[3])
|
|
{
|
|
AngleVectors(angles, matrix[0], matrix[1], matrix[2]);
|
|
VectorNegate( matrix[1], matrix[1] );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CM_ProjectPointOntoVector
|
|
================
|
|
*/
|
|
void CM_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vDir, vec3_t vProj )
|
|
{
|
|
vec3_t pVec;
|
|
|
|
VectorSubtract( point, vStart, pVec );
|
|
// project onto the directional vector for this segment
|
|
VectorMA( vStart, DotProduct( pVec, vDir ), vDir, vProj );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CM_DistanceFromLineSquared
|
|
================
|
|
*/
|
|
float CM_DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2, vec3_t dir) {
|
|
vec3_t proj, t;
|
|
int j;
|
|
|
|
CM_ProjectPointOntoVector(p, lp1, dir, proj);
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if ((proj[j] > lp1[j] && proj[j] > lp2[j]) || (proj[j] < lp1[j] && proj[j] < lp2[j]))
|
|
break;
|
|
}
|
|
if (j < 3)
|
|
{
|
|
if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j]))
|
|
{
|
|
VectorSubtract(p, lp1, t);
|
|
}
|
|
else
|
|
{
|
|
VectorSubtract(p, lp2, t);
|
|
}
|
|
return VectorLength2(t);
|
|
}
|
|
VectorSubtract(p, proj, t);
|
|
return VectorLength2( t );
|
|
}
|
|
|
|
/*
|
|
================
|
|
CM_VectorDistanceSquared
|
|
================
|
|
*/
|
|
float CM_VectorDistanceSquared( vec3_t p1, vec3_t p2 )
|
|
{
|
|
vec3_t dir;
|
|
|
|
VectorSubtract(p2, p1, dir);
|
|
return VectorLength2( dir );
|
|
}
|
|
|
|
/*
|
|
================
|
|
SquareRootFloat
|
|
================
|
|
*/
|
|
float SquareRootFloat(float number) {
|
|
long i;
|
|
float x, y;
|
|
const float f = 1.5F;
|
|
|
|
x = number * 0.5F;
|
|
y = number;
|
|
i = * ( long * ) &y;
|
|
i = 0x5f3759df - ( i >> 1 );
|
|
y = * ( float * ) &i;
|
|
y = y * ( f - ( x * y * y ) );
|
|
y = y * ( f - ( x * y * y ) );
|
|
return number * y;
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
BOX TRACING
|
|
|
|
===============================================================================
|
|
*/
|
|
/*
|
|
=============
|
|
CM_BoxLeafnums
|
|
|
|
Fills in a list of all the leafs touched
|
|
=============
|
|
*/
|
|
void CM_BoxLeafnums_r( int nodenum )
|
|
{
|
|
cplane_t *plane;
|
|
cnode_t *node;
|
|
int s;
|
|
|
|
while( 1 )
|
|
{
|
|
if( nodenum < 0 )
|
|
{
|
|
if( leaf.count >= leaf.maxcount )
|
|
{
|
|
MsgDev(D_WARN, "CM_BoxLeafnums_r: overflow\n");
|
|
return;
|
|
}
|
|
leaf.list[leaf.count++] = -1 - nodenum;
|
|
return;
|
|
}
|
|
|
|
node = &cm.nodes[nodenum];
|
|
plane = node->plane;
|
|
s = BoxOnPlaneSide( leaf.mins, leaf.maxs, plane );
|
|
if( s == SIDE_BACK ) nodenum = node->children[0];
|
|
else if( s == SIDE_ON ) nodenum = node->children[1];
|
|
else
|
|
{
|
|
// go down both
|
|
if (leaf.topnode == -1) leaf.topnode = nodenum;
|
|
CM_BoxLeafnums_r (node->children[0]);
|
|
nodenum = node->children[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
int CM_BoxLeafnums_headnode( vec3_t mins, vec3_t maxs, int *list, int listsize, int headnode, int *topnode )
|
|
{
|
|
leaf.list = list;
|
|
leaf.count = 0;
|
|
leaf.maxcount = listsize;
|
|
leaf.mins = mins;
|
|
leaf.maxs = maxs;
|
|
leaf.topnode = -1;
|
|
|
|
CM_BoxLeafnums_r( headnode );
|
|
if( topnode ) *topnode = leaf.topnode;
|
|
|
|
return leaf.count;
|
|
}
|
|
|
|
int CM_BoxLeafnums( vec3_t mins, vec3_t maxs, int *list, int listsize, int *topnode )
|
|
{
|
|
return CM_BoxLeafnums_headnode( mins, maxs, list, listsize, cm.bmodels[0].headnode, topnode);
|
|
}
|
|
|
|
/*
|
|
================
|
|
CM_ClipBoxToBrush
|
|
================
|
|
*/
|
|
void CM_ClipBoxToBrush( vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, trace_t *trace, cbrush_t *brush )
|
|
{
|
|
vec3_t ofs;
|
|
int i, j;
|
|
bool getout, startout;
|
|
cbrushside_t *side, *leadside;
|
|
cplane_t *plane, *clipplane;
|
|
float dist, d1, d2, f, enterfrac, leavefrac;
|
|
|
|
enterfrac = -1;
|
|
leavefrac = 1;
|
|
clipplane = NULL;
|
|
|
|
if(!brush->numsides ) return;
|
|
|
|
getout = false;
|
|
startout = false;
|
|
leadside = NULL;
|
|
|
|
for( i = 0; i < brush->numsides; i++ )
|
|
{
|
|
side = &cm.brushsides[brush->firstbrushside + i];
|
|
plane = side->plane;
|
|
|
|
// FIXME: special case for axial
|
|
if( !maptrace.ispoint )
|
|
{
|
|
// general box case
|
|
// push the plane out apropriately for mins/maxs
|
|
// FIXME: use signbits into 8 way lookup for each mins/maxs
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if(plane->normal[j] < 0) ofs[j] = maxs[j];
|
|
else ofs[j] = mins[j];
|
|
}
|
|
dist = DotProduct( ofs, plane->normal );
|
|
dist = plane->dist - dist;
|
|
}
|
|
else dist = plane->dist; // special point case
|
|
|
|
d1 = DotProduct( p1, plane->normal) - dist;
|
|
d2 = DotProduct( p2, plane->normal) - dist;
|
|
|
|
if(d2 > 0) getout = true; // endpoint is not in solid
|
|
if(d1 > 0) startout = true;
|
|
|
|
// if completely in front of face, no intersection
|
|
if(d1 > 0 && d2 >= d1) return;
|
|
if(d1 <= 0 && d2 <= 0) continue;
|
|
|
|
// crosses face
|
|
if (d1 > d2)
|
|
{
|
|
// enter
|
|
f = (d1 - DIST_EPSILON) / (d1-d2);
|
|
if (f > enterfrac)
|
|
{
|
|
enterfrac = f;
|
|
clipplane = plane;
|
|
leadside = side;
|
|
}
|
|
}
|
|
else
|
|
{ // leave
|
|
f = (d1 + DIST_EPSILON) / (d1-d2);
|
|
if (f < leavefrac) leavefrac = f;
|
|
}
|
|
}
|
|
|
|
if(!startout)
|
|
{
|
|
// original point was inside brush
|
|
trace->startsolid = true;
|
|
if (!getout) trace->allsolid = true;
|
|
return;
|
|
}
|
|
if( enterfrac < leavefrac )
|
|
{
|
|
if( enterfrac > -1 && enterfrac < trace->fraction )
|
|
{
|
|
if (enterfrac < 0) enterfrac = 0;
|
|
trace->fraction = enterfrac;
|
|
trace->plane = *clipplane;
|
|
trace->surface = leadside->surface;
|
|
trace->contents = brush->contents;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CM_TestBoxInBrush
|
|
================
|
|
*/
|
|
void CM_TestBoxInBrush ( vec3_t mins, vec3_t maxs, vec3_t p1, trace_t *trace, cbrush_t *brush )
|
|
{
|
|
int i, j;
|
|
cplane_t *plane;
|
|
vec3_t ofs;
|
|
float dist, d1;
|
|
cbrushside_t *side;
|
|
|
|
if(!brush->numsides) return;
|
|
|
|
for( i = 0; i < brush->numsides; i++)
|
|
{
|
|
side = &cm.brushsides[brush->firstbrushside + i];
|
|
plane = side->plane;
|
|
|
|
// FIXME: special case for axial
|
|
// general box case
|
|
// push the plane out apropriately for mins/maxs
|
|
// FIXME: use signbits into 8 way lookup for each mins/maxs
|
|
for (j = 0; j < 3; j++ )
|
|
{
|
|
if (plane->normal[j] < 0) ofs[j] = maxs[j];
|
|
else ofs[j] = mins[j];
|
|
}
|
|
dist = DotProduct (ofs, plane->normal);
|
|
dist = plane->dist - dist;
|
|
|
|
d1 = DotProduct( p1, plane->normal) - dist;
|
|
|
|
// if completely in front of face, no intersection
|
|
if(d1 > 0) return;
|
|
}
|
|
|
|
// inside this brush
|
|
trace->startsolid = trace->allsolid = true;
|
|
trace->fraction = 0;
|
|
trace->contents = brush->contents;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CM_TraceToLeaf
|
|
================
|
|
*/
|
|
void CM_TraceToLeaf( int leafnum )
|
|
{
|
|
cleaf_t *leaf;
|
|
cbrush_t *b;
|
|
int k, brushnum;
|
|
|
|
leaf = &cm.leafs[leafnum];
|
|
if(!(leaf->contents & maptrace.contents)) return;
|
|
|
|
// trace line against all brushes in the leaf
|
|
for( k = 0; k < leaf->numleafbrushes; k++ )
|
|
{
|
|
brushnum = cm.leafbrushes[leaf->firstleafbrush + k];
|
|
b = &cm.brushes[brushnum];
|
|
// already checked this brush in another leaf
|
|
if( b->checkcount == cm.checkcount ) continue;
|
|
b->checkcount = cm.checkcount;
|
|
if(!(b->contents & maptrace.contents)) continue;
|
|
CM_ClipBoxToBrush(maptrace.mins, maptrace.maxs, maptrace.start, maptrace.end, &maptrace.result, b);
|
|
if(!maptrace.result.fraction) return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CM_TestInLeaf
|
|
================
|
|
*/
|
|
void CM_TestInLeaf( int leafnum )
|
|
{
|
|
int k;
|
|
int brushnum;
|
|
cleaf_t *leaf;
|
|
cbrush_t *b;
|
|
|
|
leaf = &cm.leafs[leafnum];
|
|
if(!(leaf->contents & maptrace.contents)) return;
|
|
|
|
// trace line against all brushes in the leaf
|
|
for( k = 0; k < leaf->numleafbrushes; k++ )
|
|
{
|
|
brushnum = cm.leafbrushes[leaf->firstleafbrush + k];
|
|
b = &cm.brushes[brushnum];
|
|
// already checked this brush in another leaf
|
|
if(b->checkcount == cm.checkcount) continue;
|
|
b->checkcount = cm.checkcount;
|
|
if(!(b->contents & maptrace.contents)) continue;
|
|
CM_TestBoxInBrush(maptrace.mins, maptrace.maxs, maptrace.start, &maptrace.result, b);
|
|
if(!maptrace.result.fraction) return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CM_RecursiveHullCheck
|
|
==================
|
|
*/
|
|
void CM_RecursiveHullCheck( int num, float p1f, float p2f, vec3_t p1, vec3_t p2 )
|
|
{
|
|
cnode_t *node;
|
|
cplane_t *plane;
|
|
float t1, t2, offset;
|
|
float frac, frac2;
|
|
float midf, idist;
|
|
int i, side;
|
|
vec3_t mid;
|
|
|
|
if( maptrace.result.fraction <= p1f ) return; // already hit something nearer
|
|
|
|
// if < 0, we are in a leaf node
|
|
if( num < 0 )
|
|
{
|
|
CM_TraceToLeaf( -1 - num );
|
|
return;
|
|
}
|
|
|
|
// find the point distances to the seperating plane
|
|
// and the offset for the size of the box
|
|
node = cm.nodes + num;
|
|
plane = node->plane;
|
|
|
|
if( plane->type < 3 )
|
|
{
|
|
t1 = p1[plane->type] - plane->dist;
|
|
t2 = p2[plane->type] - plane->dist;
|
|
offset = maptrace.extents[plane->type];
|
|
}
|
|
else
|
|
{
|
|
t1 = DotProduct(plane->normal, p1) - plane->dist;
|
|
t2 = DotProduct(plane->normal, p2) - plane->dist;
|
|
if(maptrace.ispoint) offset = 0;
|
|
else offset = fabs(maptrace.extents[0]*plane->normal[0])+fabs(maptrace.extents[1]*plane->normal[1])+fabs(maptrace.extents[2]*plane->normal[2]);
|
|
}
|
|
|
|
// see which sides we need to consider
|
|
if(t1 >= offset && t2 >= offset)
|
|
{
|
|
CM_RecursiveHullCheck(node->children[0], p1f, p2f, p1, p2);
|
|
return;
|
|
}
|
|
if(t1 < -offset && t2 < -offset)
|
|
{
|
|
CM_RecursiveHullCheck(node->children[1], p1f, p2f, p1, p2);
|
|
return;
|
|
}
|
|
|
|
// put the crosspoint DIST_EPSILON pixels on the near side
|
|
if (t1 < t2)
|
|
{
|
|
idist = 1.0/(t1-t2);
|
|
side = 1;
|
|
frac2 = (t1 + offset + DIST_EPSILON)*idist;
|
|
frac = (t1 - offset + DIST_EPSILON)*idist;
|
|
}
|
|
else if (t1 > t2)
|
|
{
|
|
idist = 1.0/(t1-t2);
|
|
side = 0;
|
|
frac2 = (t1 - offset - DIST_EPSILON)*idist;
|
|
frac = (t1 + offset + DIST_EPSILON)*idist;
|
|
}
|
|
else
|
|
{
|
|
side = 0;
|
|
frac = 1;
|
|
frac2 = 0;
|
|
}
|
|
|
|
// move up to the node
|
|
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]);
|
|
CM_RecursiveHullCheck(node->children[side], p1f, midf, p1, mid);
|
|
|
|
|
|
// go past the node
|
|
if (frac2 < 0) frac2 = 0;
|
|
if (frac2 > 1) frac2 = 1;
|
|
|
|
midf = p1f + (p2f - p1f) * frac2;
|
|
for (i = 0; i < 3; i++) mid[i] = p1[i] + frac2 * (p2[i] - p1[i]);
|
|
CM_RecursiveHullCheck(node->children[side^1], midf, p2f, mid, p2);
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
PUBLIC FUNCTIONS
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
==================
|
|
CM_BoxTrace
|
|
==================
|
|
*/
|
|
trace_t CM_BoxTrace( vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int headnode, int brushmask )
|
|
{
|
|
int i;
|
|
|
|
cm.checkcount++; // for multi-check avoidance
|
|
|
|
// fill in a default trace
|
|
memset(&maptrace.result, 0, sizeof(maptrace.result));
|
|
maptrace.result.surface = &(cm.nullsurface);
|
|
maptrace.result.fraction = 1;
|
|
|
|
if (!cm.numnodes) return maptrace.result; // map not loaded
|
|
|
|
maptrace.contents = brushmask;
|
|
VectorCopy(start, maptrace.start);
|
|
VectorCopy(end, maptrace.end);
|
|
VectorCopy(mins, maptrace.mins);
|
|
VectorCopy(maxs, maptrace.maxs);
|
|
|
|
// check for position test special case
|
|
if( VectorCompare( start, end ))
|
|
{
|
|
vec3_t c1, c2;
|
|
int leafs[1024];
|
|
int i, numleafs, topnode;
|
|
|
|
VectorAdd(start, mins, c1);
|
|
VectorAdd(start, maxs, c2);
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
c1[i] -= 1;
|
|
c2[i] += 1;
|
|
}
|
|
|
|
numleafs = CM_BoxLeafnums_headnode(c1, c2, leafs, 1024, headnode, &topnode);
|
|
for (i = 0; i < numleafs; i++)
|
|
{
|
|
CM_TestInLeaf(leafs[i]);
|
|
if(maptrace.result.allsolid)
|
|
break;
|
|
}
|
|
VectorCopy (start, maptrace.result.endpos);
|
|
return maptrace.result;
|
|
}
|
|
|
|
// check for point special case
|
|
if( VectorIsNull( mins ) && VectorIsNull( maxs ))
|
|
{
|
|
maptrace.ispoint = true;
|
|
VectorClear( maptrace.extents );
|
|
}
|
|
else
|
|
{
|
|
maptrace.ispoint = false;
|
|
maptrace.extents[0] = -mins[0] > maxs[0] ? -mins[0] : maxs[0];
|
|
maptrace.extents[1] = -mins[1] > maxs[1] ? -mins[1] : maxs[1];
|
|
maptrace.extents[2] = -mins[2] > maxs[2] ? -mins[2] : maxs[2];
|
|
}
|
|
|
|
// general sweeping through world
|
|
CM_RecursiveHullCheck( headnode, 0, 1, start, end );
|
|
|
|
if (maptrace.result.fraction == 1)
|
|
{
|
|
VectorCopy(end, maptrace.result.endpos);
|
|
}
|
|
else
|
|
{
|
|
for(i = 0; i < 3; i++)
|
|
maptrace.result.endpos[i] = start[i] + maptrace.result.fraction * (end[i] - start[i]);
|
|
}
|
|
return maptrace.result;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CM_TransformedBoxTrace
|
|
|
|
Handles offseting and rotation of the end points for moving and
|
|
rotating entities
|
|
==================
|
|
*/
|
|
#ifdef _WIN32
|
|
#pragma optimize( "", off )
|
|
#endif
|
|
|
|
trace_t CM_TransformedBoxTrace (vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int headnode, int brushmask, vec3_t origin, vec3_t angles )
|
|
{
|
|
trace_t trace;
|
|
vec3_t start_l, end_l;
|
|
vec3_t forward, right, up;
|
|
vec3_t a, temp;
|
|
bool rotated = false;
|
|
|
|
// subtract origin offset
|
|
VectorSubtract(start, origin, start_l);
|
|
VectorSubtract(end, origin, end_l);
|
|
|
|
// rotate start and end into the models frame of reference
|
|
if (headnode != box.headnode && !VectorIsNull( angles ))
|
|
rotated = true;
|
|
|
|
if( rotated )
|
|
{
|
|
AngleVectors( angles, forward, right, up );
|
|
VectorCopy( start_l, temp );
|
|
start_l[0] = DotProduct( temp, forward );
|
|
start_l[1] = -DotProduct( temp, right );
|
|
start_l[2] = DotProduct( temp, up );
|
|
|
|
VectorCopy( end_l, temp );
|
|
end_l[0] = DotProduct( temp, forward );
|
|
end_l[1] = -DotProduct( temp, right );
|
|
end_l[2] = DotProduct( temp, up );
|
|
}
|
|
|
|
// sweep the box through the model
|
|
trace = CM_BoxTrace( start_l, end_l, mins, maxs, headnode, brushmask );
|
|
|
|
if( rotated && trace.fraction != 1.0)
|
|
{
|
|
// FIXME: figure out how to do this with existing angles
|
|
VectorNegate( angles, a );
|
|
AngleVectors( a, forward, right, up);
|
|
|
|
VectorCopy( trace.plane.normal, temp);
|
|
trace.plane.normal[0] = DotProduct( temp, forward);
|
|
trace.plane.normal[1] = -DotProduct( temp, right);
|
|
trace.plane.normal[2] = DotProduct( temp, up);
|
|
}
|
|
|
|
trace.endpos[0] = start[0] + trace.fraction * (end[0] - start[0]);
|
|
trace.endpos[1] = start[1] + trace.fraction * (end[1] - start[1]);
|
|
trace.endpos[2] = start[2] + trace.fraction * (end[2] - start[2]);
|
|
return trace;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#pragma optimize( "", on )
|
|
#endif |