/* ================ CM_TraceBoundingBoxThroughCapsule bounding box vs. capsule collision ================ */ static void CM_TraceBoundingBoxThroughCapsule(traceWork_t *tw, clipHandle_t model) { 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_TraceBoundingBoxThroughCapsule bounding box vs. capsule collision ================ */ void CM_TraceBoundingBoxThroughCapsule( traceWork_t *tw, clipHandle_t model ) { vec3_t mins, maxs, offset, bboxSize[2]; clipHandle_t h; cmodel_t *cmod; int i; // save size of the bounding box VectorCopy(tw->size[0], bboxSize[0]); VectorCopy(tw->size[1], bboxSize[1]); // 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; tw->size[0][i] = mins[i] - offset[i]; tw->size[1][i] = maxs[i] - offset[i]; tw->start[i] -= offset[i]; tw->end[i] -= offset[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]; } } // replace the bounding box with the capsule tw->type = TT_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 ); // replace the capsule with the bounding box h = CM_TempBoxModel(bboxSize[0], bboxSize[1], qfalse, capsule_contents); // calculate collision cmod = CM_ClipHandleToModel( h ); CM_TraceThroughLeaf( tw, &cmod->leaf ); }
/* ================== CM_Trace ================== */ static void CM_Trace(trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const 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; qboolean positionTest; cmod = CM_ClipHandleToModel(model); cm.checkcount++; // for multi-check avoidance c_traces++; // for statistics, may be zeroed // fill in a default trace memset(&tw, 0, sizeof(tw)); tw.trace.fraction = 1.0f; // 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); } positionTest = (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]); 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]; // check for point special case if (tw.size[0][0] == 0.0f && tw.size[0][1] == 0.0f && tw.size[0][2] == 0.0f) { 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]; } if (positionTest) { CM_CalcTraceBounds(&tw, qfalse); } else { vec3_t dir; VectorSubtract(tw.end, tw.start, dir); VectorCopy(dir, tw.dir); VectorNormalize(dir); MakeNormalVectors(dir, tw.tracePlane1.normal, tw.tracePlane2.normal); tw.tracePlane1.dist = DotProduct(tw.tracePlane1.normal, tw.start); tw.tracePlane2.dist = DotProduct(tw.tracePlane2.normal, tw.start); if (tw.isPoint) { tw.traceDist1 = tw.traceDist2 = 1.0f; } else { float dist; tw.traceDist1 = tw.traceDist2 = 0.0f; for (i = 0; i < 8; i++) { dist = Q_fabs(DotProduct(tw.tracePlane1.normal, tw.offsets[i]) - tw.tracePlane1.dist); if (dist > tw.traceDist1) { tw.traceDist1 = dist; } dist = Q_fabs(DotProduct(tw.tracePlane2.normal, tw.offsets[i]) - tw.tracePlane2.dist); if (dist > tw.traceDist2) { tw.traceDist2 = dist; } } // expand for epsilon tw.traceDist1 += 1.0f; tw.traceDist2 += 1.0f; } CM_CalcTraceBounds(&tw, qtrue); } // check for position test special case if (positionTest) { if (model) { #ifdef ALWAYS_BBOX_VS_BBOX 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 #else // this is dead code when ALWAYS_BBOX_VS_BBOX or ALWAYS_CAPSULE_VS_CAPSULE are active if (model == CAPSULE_MODEL_HANDLE) { if (tw.sphere.use) { CM_TestCapsuleInCapsule(&tw, model); } else { CM_TestBoundingBoxInCapsule(&tw, model); } } else #endif { CM_TestInLeaf(&tw, &cmod->leaf); } } else { CM_PositionTest(&tw); } } else { // 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 #else // this is dead code when ALWAYS_BBOX_VS_BBOX or ALWAYS_CAPSULE_VS_CAPSULE are active if (model == CAPSULE_MODEL_HANDLE) { if (tw.sphere.use) { CM_TraceCapsuleThroughCapsule(&tw, model); } else { CM_TraceBoundingBoxThroughCapsule(&tw, model); } } else #endif { 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]); } } *results = tw.trace; }
/* ================== 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. ================== */ static 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 { /* // an axial brush right behind a slanted bsp plane // will poke through when expanded, so adjust // by sqrt(3) offset = Q_fabs(tw->extents[0]*plane->normal[0]) + Q_fabs(tw->extents[1]*plane->normal[1]) + Q_fabs(tw->extents[2]*plane->normal[2]); offset *= 2; offset = tw->maxOffset; */ offset = tw->maxOffset; } } // 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, const vec3_t mins, const 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; const cmodel_t* 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_BiSphereTrace ================== */ void CM_BiSphereTrace( trace_t *results, const vec3_t start, const vec3_t end, float startRad, float endRad, clipHandle_t model, int mask ) { int i; traceWork_t tw; float largestRadius = startRad > endRad ? startRad : endRad; 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.0f; // assume it goes the entire distance until shown otherwise VectorCopy( vec3_origin, tw.modelOrigin ); tw.type = TT_BISPHERE; tw.testLateralCollision = qtrue; tw.trace.lateralFraction = 1.0f; if( !cm.numNodes ) { *results = tw.trace; return; // map not loaded, shouldn't happen } // set basic parms tw.contents = mask; VectorCopy( start, tw.start ); VectorCopy( end, tw.end ); tw.biSphere.startRadius = startRad; tw.biSphere.endRadius = endRad; // // calculate bounds // for( i = 0 ; i < 3 ; i++ ) { if( tw.start[ i ] < tw.end[ i ] ) { tw.bounds[ 0 ][ i ] = tw.start[ i ] - tw.biSphere.startRadius; tw.bounds[ 1 ][ i ] = tw.end[ i ] + tw.biSphere.endRadius; } else { tw.bounds[ 0 ][ i ] = tw.end[ i ] + tw.biSphere.endRadius; tw.bounds[ 1 ][ i ] = tw.start[ i ] - tw.biSphere.startRadius; } } tw.isPoint = qfalse; tw.extents[ 0 ] = largestRadius; tw.extents[ 1 ] = largestRadius; tw.extents[ 2 ] = largestRadius; // // general sweeping through world // if( model ) CM_TraceThroughLeaf( &tw, &cmod->leaf ); else CM_TraceThroughTree( &tw, 0, 0.0f, 1.0f, tw.start, tw.end ); // generate endpos from the original, unmodified start/end if( tw.trace.fraction == 1.0f ) { 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; }
// 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, vector3 *p1, vector3 *p2) { cNode_t *node; cplane_t *plane; float t1, t2, offset; float frac, frac2; float idist; vector3 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 appropriately for mins/maxs if ( plane->type < 3 ) { t1 = p1->data[plane->type] - plane->dist; t2 = p2->data[plane->type] - plane->dist; offset = tw->extents.data[plane->type]; } else { t1 = DotProduct (&plane->normal, p1) - plane->dist; t2 = DotProduct (&plane->normal, p2) - plane->dist; if ( tw->isPoint ) { offset = 0; } else { // 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.0f/(t1-t2); side = 1; frac2 = (t1 + offset + SURFACE_CLIP_EPSILON)*idist; frac = (t1 - offset + SURFACE_CLIP_EPSILON)*idist; } else if (t1 > t2) { idist = 1.0f/(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; VectorLerp( p1, frac, p2, &mid ); 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; VectorLerp( p1, frac, p2, &mid ); CM_TraceThroughTree( tw, node->children[side^1], midf, p2f, &mid, p2 ); }
/* ================ CM_TraceToLeaf ================ */ void CM_TraceToLeaf( traceWork_t *tw, trace_t &trace, cLeaf_t *leaf, clipMap_t *local ) { int k; int brushnum; cbrush_t *b; cPatch_t *patch; // trace line against all brushes in the leaf for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) { brushnum = local->leafbrushes[leaf->firstLeafBrush + k]; b = &local->brushes[brushnum]; if ( b->checkcount == local->checkcount ) { continue; // already checked this brush in another leaf } b->checkcount = local->checkcount; if ( !(b->contents & tw->contents) ) { continue; } CM_TraceThroughBrush( tw, trace, b, false); if ( !trace.fraction ) { return; } } // trace line against all patches in the leaf #ifdef BSPC if (1) { #else if ( !cm_noCurves->integer ) { #endif for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) { patch = local->surfaces[ local->leafsurfaces[ leaf->firstLeafSurface + k ] ]; if ( !patch ) { continue; } if ( patch->checkcount == local->checkcount ) { continue; // already checked this patch in another leaf } patch->checkcount = local->checkcount; if ( !(patch->contents & tw->contents) ) { continue; } CM_TraceThroughPatch( tw, trace, patch ); if ( !trace.fraction ) { return; } } } } /* ================== 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, trace_t &trace, clipMap_t *local, 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 (trace.fraction <= p1f) { return; // already hit something nearer } // if < 0, we are in a leaf node if (num < 0) { CM_TraceThroughLeaf( tw, trace, local, &local->leafs[-1-num] ); return; } // // find the point distances to the seperating plane // and the offset for the size of the box // node = local->nodes + num; plane = node->plane; // adjust the plane distance appropriately 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, trace, local, node->children[0], p1f, p2f, p1, p2 ); return; } if ( t1 < -offset - 1 && t2 < -offset - 1 ) { CM_TraceThroughTree( tw, trace, local, 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, trace, local, 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, trace, local, node->children[side^1], midf, p2f, mid, p2 ); }