📄 cm_trace.c
字号:
vec3_t mins, maxs, offset, size[2];
clipHandle_t h;
cmodel_t *cmod;
int i;
// mins maxs of the capsule
CM_ModelBounds(model, mins, maxs);
// offset for capsule center
for ( i = 0 ; i < 3 ; i++ ) {
offset[i] = ( mins[i] + maxs[i] ) * 0.5;
size[0][i] = mins[i] - offset[i];
size[1][i] = maxs[i] - offset[i];
tw->start[i] -= offset[i];
tw->end[i] -= offset[i];
}
// replace the bounding box with the capsule
tw->sphere.use = qtrue;
tw->sphere.radius = ( size[1][0] > size[1][2] ) ? size[1][2]: size[1][0];
tw->sphere.halfheight = size[1][2];
VectorSet( tw->sphere.offset, 0, 0, size[1][2] - tw->sphere.radius );
// replace the capsule with the bounding box
h = CM_TempBoxModel(tw->size[0], tw->size[1], qfalse);
// calculate collision
cmod = CM_ClipHandleToModel( h );
CM_TraceThroughLeaf( tw, &cmod->leaf );
}
//=========================================================================================
/*
==================
CM_TraceThroughTree
Traverse all the contacted leafs from the start to the end position.
If the trace is a point, they will be exactly in order, but for larger
trace volumes it is possible to hit something in a later leaf with
a smaller intercept fraction.
==================
*/
void CM_TraceThroughTree( traceWork_t *tw, 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 idist;
vec3_t mid;
int side;
float midf;
if (tw->trace.fraction <= p1f) {
return; // already hit something nearer
}
// if < 0, we are in a leaf node
if (num < 0) {
CM_TraceThroughLeaf( tw, &cm.leafs[-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;
// adjust the plane distance apropriately for mins/maxs
if ( plane->type < 3 ) {
t1 = p1[plane->type] - plane->dist;
t2 = p2[plane->type] - plane->dist;
offset = tw->extents[plane->type];
} else {
t1 = DotProduct (plane->normal, p1) - plane->dist;
t2 = DotProduct (plane->normal, p2) - plane->dist;
if ( tw->isPoint ) {
offset = 0;
} else {
#if 0 // bk010201 - DEAD
// an axial brush right behind a slanted bsp plane
// will poke through when expanded, so adjust
// by sqrt(3)
offset = fabs(tw->extents[0]*plane->normal[0]) +
fabs(tw->extents[1]*plane->normal[1]) +
fabs(tw->extents[2]*plane->normal[2]);
offset *= 2;
offset = tw->maxOffset;
#endif
// this is silly
offset = 2048;
}
}
// see which sides we need to consider
if ( t1 >= offset + 1 && t2 >= offset + 1 ) {
CM_TraceThroughTree( tw, node->children[0], p1f, p2f, p1, p2 );
return;
}
if ( t1 < -offset - 1 && t2 < -offset - 1 ) {
CM_TraceThroughTree( tw, node->children[1], p1f, p2f, p1, p2 );
return;
}
// put the crosspoint SURFACE_CLIP_EPSILON pixels on the near side
if ( t1 < t2 ) {
idist = 1.0/(t1-t2);
side = 1;
frac2 = (t1 + offset + SURFACE_CLIP_EPSILON)*idist;
frac = (t1 - offset + SURFACE_CLIP_EPSILON)*idist;
} else if (t1 > t2) {
idist = 1.0/(t1-t2);
side = 0;
frac2 = (t1 - offset - SURFACE_CLIP_EPSILON)*idist;
frac = (t1 + offset + SURFACE_CLIP_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;
mid[0] = p1[0] + frac*(p2[0] - p1[0]);
mid[1] = p1[1] + frac*(p2[1] - p1[1]);
mid[2] = p1[2] + frac*(p2[2] - p1[2]);
CM_TraceThroughTree( tw, 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;
mid[0] = p1[0] + frac2*(p2[0] - p1[0]);
mid[1] = p1[1] + frac2*(p2[1] - p1[1]);
mid[2] = p1[2] + frac2*(p2[2] - p1[2]);
CM_TraceThroughTree( tw, node->children[side^1], midf, p2f, mid, p2 );
}
//======================================================================
/*
==================
CM_Trace
==================
*/
void CM_Trace( trace_t *results, const vec3_t start, const vec3_t end, vec3_t mins, vec3_t maxs,
clipHandle_t model, const vec3_t origin, int brushmask, int capsule, sphere_t *sphere ) {
int i;
traceWork_t tw;
vec3_t offset;
cmodel_t *cmod;
cmod = CM_ClipHandleToModel( model );
cm.checkcount++; // for multi-check avoidance
c_traces++; // for statistics, may be zeroed
// fill in a default trace
Com_Memset( &tw, 0, sizeof(tw) );
tw.trace.fraction = 1; // assume it goes the entire distance until shown otherwise
VectorCopy(origin, tw.modelOrigin);
if (!cm.numNodes) {
*results = tw.trace;
return; // map not loaded, shouldn't happen
}
// allow NULL to be passed in for 0,0,0
if ( !mins ) {
mins = vec3_origin;
}
if ( !maxs ) {
maxs = vec3_origin;
}
// set basic parms
tw.contents = brushmask;
// adjust so that mins and maxs are always symetric, which
// avoids some complications with plane expanding of rotated
// bmodels
for ( i = 0 ; i < 3 ; i++ ) {
offset[i] = ( mins[i] + maxs[i] ) * 0.5;
tw.size[0][i] = mins[i] - offset[i];
tw.size[1][i] = maxs[i] - offset[i];
tw.start[i] = start[i] + offset[i];
tw.end[i] = end[i] + offset[i];
}
// if a sphere is already specified
if ( sphere ) {
tw.sphere = *sphere;
}
else {
tw.sphere.use = capsule;
tw.sphere.radius = ( tw.size[1][0] > tw.size[1][2] ) ? tw.size[1][2]: tw.size[1][0];
tw.sphere.halfheight = tw.size[1][2];
VectorSet( tw.sphere.offset, 0, 0, tw.size[1][2] - tw.sphere.radius );
}
tw.maxOffset = tw.size[1][0] + tw.size[1][1] + tw.size[1][2];
// tw.offsets[signbits] = vector to apropriate corner from origin
tw.offsets[0][0] = tw.size[0][0];
tw.offsets[0][1] = tw.size[0][1];
tw.offsets[0][2] = tw.size[0][2];
tw.offsets[1][0] = tw.size[1][0];
tw.offsets[1][1] = tw.size[0][1];
tw.offsets[1][2] = tw.size[0][2];
tw.offsets[2][0] = tw.size[0][0];
tw.offsets[2][1] = tw.size[1][1];
tw.offsets[2][2] = tw.size[0][2];
tw.offsets[3][0] = tw.size[1][0];
tw.offsets[3][1] = tw.size[1][1];
tw.offsets[3][2] = tw.size[0][2];
tw.offsets[4][0] = tw.size[0][0];
tw.offsets[4][1] = tw.size[0][1];
tw.offsets[4][2] = tw.size[1][2];
tw.offsets[5][0] = tw.size[1][0];
tw.offsets[5][1] = tw.size[0][1];
tw.offsets[5][2] = tw.size[1][2];
tw.offsets[6][0] = tw.size[0][0];
tw.offsets[6][1] = tw.size[1][1];
tw.offsets[6][2] = tw.size[1][2];
tw.offsets[7][0] = tw.size[1][0];
tw.offsets[7][1] = tw.size[1][1];
tw.offsets[7][2] = tw.size[1][2];
//
// calculate bounds
//
if ( tw.sphere.use ) {
for ( i = 0 ; i < 3 ; i++ ) {
if ( tw.start[i] < tw.end[i] ) {
tw.bounds[0][i] = tw.start[i] - fabs(tw.sphere.offset[i]) - tw.sphere.radius;
tw.bounds[1][i] = tw.end[i] + fabs(tw.sphere.offset[i]) + tw.sphere.radius;
} else {
tw.bounds[0][i] = tw.end[i] - fabs(tw.sphere.offset[i]) - tw.sphere.radius;
tw.bounds[1][i] = tw.start[i] + fabs(tw.sphere.offset[i]) + tw.sphere.radius;
}
}
}
else {
for ( i = 0 ; i < 3 ; i++ ) {
if ( tw.start[i] < tw.end[i] ) {
tw.bounds[0][i] = tw.start[i] + tw.size[0][i];
tw.bounds[1][i] = tw.end[i] + tw.size[1][i];
} else {
tw.bounds[0][i] = tw.end[i] + tw.size[0][i];
tw.bounds[1][i] = tw.start[i] + tw.size[1][i];
}
}
}
//
// check for position test special case
//
if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) {
if ( model ) {
#ifdef ALWAYS_BBOX_VS_BBOX // bk010201 - FIXME - compile time flag?
if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) {
tw.sphere.use = qfalse;
CM_TestInLeaf( &tw, &cmod->leaf );
}
else
#elif defined(ALWAYS_CAPSULE_VS_CAPSULE)
if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) {
CM_TestCapsuleInCapsule( &tw, model );
}
else
#endif
if ( model == CAPSULE_MODEL_HANDLE ) {
if ( tw.sphere.use ) {
CM_TestCapsuleInCapsule( &tw, model );
}
else {
CM_TestBoundingBoxInCapsule( &tw, model );
}
}
else {
CM_TestInLeaf( &tw, &cmod->leaf );
}
} else {
CM_PositionTest( &tw );
}
} else {
//
// check for point special case
//
if ( tw.size[0][0] == 0 && tw.size[0][1] == 0 && tw.size[0][2] == 0 ) {
tw.isPoint = qtrue;
VectorClear( tw.extents );
} else {
tw.isPoint = qfalse;
tw.extents[0] = tw.size[1][0];
tw.extents[1] = tw.size[1][1];
tw.extents[2] = tw.size[1][2];
}
//
// general sweeping through world
//
if ( model ) {
#ifdef ALWAYS_BBOX_VS_BBOX
if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) {
tw.sphere.use = qfalse;
CM_TraceThroughLeaf( &tw, &cmod->leaf );
}
else
#elif defined(ALWAYS_CAPSULE_VS_CAPSULE)
if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) {
CM_TraceCapsuleThroughCapsule( &tw, model );
}
else
#endif
if ( model == CAPSULE_MODEL_HANDLE ) {
if ( tw.sphere.use ) {
CM_TraceCapsuleThroughCapsule( &tw, model );
}
else {
CM_TraceBoundingBoxThroughCapsule( &tw, model );
}
}
else {
CM_TraceThroughLeaf( &tw, &cmod->leaf );
}
} else {
CM_TraceThroughTree( &tw, 0, 0, 1, tw.start, tw.end );
}
}
// generate endpos from the original, unmodified start/end
if ( tw.trace.fraction == 1 ) {
VectorCopy (end, tw.trace.endpos);
} else {
for ( i=0 ; i<3 ; i++ ) {
tw.trace.endpos[i] = start[i] + tw.trace.fraction * (end[i] - start[i]);
}
}
// If allsolid is set (was entirely inside something solid), the plane is not valid.
// If fraction == 1.0, we never hit anything, and thus the plane is not valid.
// Otherwise, the normal on the plane should have unit length
assert(tw.trace.allsolid ||
tw.trace.fraction == 1.0 ||
VectorLengthSquared(tw.trace.plane.normal) > 0.9999);
*results = tw.trace;
}
/*
==================
CM_BoxTrace
==================
*/
void CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
vec3_t mins, vec3_t maxs,
clipHandle_t model, int brushmask, int capsule ) {
CM_Trace( results, start, end, mins, maxs, model, vec3_origin, brushmask, capsule, NULL );
}
/*
==================
CM_TransformedBoxTrace
Handles offseting and rotation of the end points for moving and
rotating entities
==================
*/
void CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
vec3_t mins, vec3_t maxs,
clipHandle_t model, int brushmask,
const vec3_t origin, const vec3_t angles, int capsule ) {
trace_t trace;
vec3_t start_l, end_l;
qboolean rotated;
vec3_t offset;
vec3_t symetricSize[2];
vec3_t matrix[3], transpose[3];
int i;
float halfwidth;
float halfheight;
float t;
sphere_t sphere;
if ( !mins ) {
mins = vec3_origin;
}
if ( !maxs ) {
maxs = vec3_origin;
}
// adjust so that mins and maxs are always symetric, which
// avoids some complications with plane expanding of rotated
// bmodels
for ( i = 0 ; i < 3 ; i++ ) {
offset[i] = ( mins[i] + maxs[i] ) * 0.5;
symetricSize[0][i] = mins[i] - offset[i];
symetricSize[1][i] = maxs[i] - offset[i];
start_l[i] = start[i] + offset[i];
end_l[i] = end[i] + offset[i];
}
// subtract origin offset
VectorSubtract( start_l, origin, start_l );
VectorSubtract( end_l, origin, end_l );
// rotate start and end into the models frame of reference
if ( model != BOX_MODEL_HANDLE &&
(angles[0] || angles[1] || angles[2]) ) {
rotated = qtrue;
} else {
rotated = qfalse;
}
halfwidth = symetricSize[ 1 ][ 0 ];
halfheight = symetricSize[ 1 ][ 2 ];
sphere.use = capsule;
sphere.radius = ( halfwidth > halfheight ) ? halfheight : halfwidth;
sphere.halfheight = halfheight;
t = halfheight - sphere.radius;
if (rotated) {
// rotation on trace line (start-end) instead of rotating the bmodel
// NOTE: This is still incorrect for bounding boxes because the actual bounding
// box that is swept through the model is not rotated. We cannot rotate
// the bounding box or the bmodel because that would make all the brush
// bevels invalid.
// However this is correct for capsules since a capsule itself is rotated too.
CreateRotationMatrix(angles, matrix);
RotatePoint(start_l, matrix);
RotatePoint(end_l, matrix);
// rotated sphere offset for capsule
sphere.offset[0] = matrix[0][ 2 ] * t;
sphere.offset[1] = -matrix[1][ 2 ] * t;
sphere.offset[2] = matrix[2][ 2 ] * t;
}
else {
VectorSet( sphere.offset, 0, 0, t );
}
// sweep the box through the model
CM_Trace( &trace, start_l, end_l, symetricSize[0], symetricSize[1], model, origin, brushmask, capsule, &sphere );
// if the bmodel was rotated and there was a collision
if ( rotated && trace.fraction != 1.0 ) {
// rotation of bmodel collision plane
TransposeMatrix(matrix, transpose);
RotatePoint(trace.plane.normal, transpose);
}
// re-calculate the end position of the trace because the trace.endpos
// calculated by CM_Trace could be rotated and have an offset
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]);
*results = trace;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -