// bounding box vs. capsule collision void CM_TraceBoundingBoxThroughCapsule( traceWork_t *tw, clipHandle_t model ) { vector3 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.data[i] = ( mins.data[i] + maxs.data[i] ) * 0.5f; size[0].data[i] = mins.data[i] - offset.data[i]; size[1].data[i] = maxs.data[i] - offset.data[i]; tw->start.data[i] -= offset.data[i]; tw->end.data[i] -= offset.data[i]; } // replace the bounding box with the capsule tw->sphere.use = qtrue; tw->sphere.radius = ( size[1].x > size[1].z ) ? size[1].z: size[1].x; tw->sphere.halfheight = size[1].z; VectorSet( &tw->sphere.offset, 0, 0, size[1].z - 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_TestBoundingBoxInCapsule bounding box inside capsule check ================== */ void CM_TestBoundingBoxInCapsule(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_TestInLeaf(tw, &cmod->leaf); }
/* ================== CM_PointContents ================== */ int CM_PointContents(const vec3_t p, clipHandle_t model) { int leafnum; int i, k; int brushnum; cLeaf_t *leaf; cbrush_t *b; int contents; float d; cmodel_t *clipm; if(!cm.numNodes) // map not loaded { return 0; } if(model) { clipm = CM_ClipHandleToModel(model); leaf = &clipm->leaf; } else { leafnum = CM_PointLeafnum_r(p, 0); leaf = &cm.leafs[leafnum]; } contents = 0; for(k = 0 ; k < leaf->numLeafBrushes ; k++) { brushnum = cm.leafbrushes[leaf->firstLeafBrush + k]; b = &cm.brushes[brushnum]; // see if the point is in the brush for(i = 0 ; i < b->numsides ; i++) { d = DotProduct(p, b->sides[i].plane->normal); // FIXME test for Cash // if ( d >= b->sides[i].plane->dist ) { if(d > b->sides[i].plane->dist) { break; } } if(i == b->numsides) { contents |= b->contents; } } return contents; }
/* ================== CM_TestBoundingBoxInCapsule bounding box inside capsule check ================== */ void CM_TestBoundingBoxInCapsule( 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_TestInLeaf( 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_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; }
/* ================== CM_PointContents ================== */ int CM_PointContents( const vec3_t p, clipHandle_t model ) { int leafnum; int i, k; int brushnum; cLeaf_t *leaf; cbrush_t *b; int contents; float d; cmodel_t *clipm; if ( !cm.numNodes ) { // map not loaded return 0; } if ( model ) { clipm = CM_ClipHandleToModel( model ); leaf = &clipm->leaf; } else { leafnum = CM_PointLeafnum_r( p, 0 ); leaf = &cm.leafs[ leafnum ]; } // XreaL BEGIN if ( leaf->area == -1 ) { // RB: added this optimization // p is in the void and we should return solid so particles can be removed from the void return CONTENTS_SOLID; } // XreaL END contents = 0; for ( k = 0; k < leaf->numLeafBrushes; k++ ) { brushnum = cm.leafbrushes[ leaf->firstLeafBrush + k ]; b = &cm.brushes[ brushnum ]; // XreaL BEGIN if ( !CM_BoundsIntersectPoint( b->bounds[ 0 ], b->bounds[ 1 ], p ) ) { continue; } // XreaL END // see if the point is in the brush for ( i = 0; i < b->numsides; i++ ) { d = DotProduct( p, b->sides[ i ].plane->normal ); // FIXME test for Cash // if ( d >= b->sides[i].plane->dist ) { if ( d > b->sides[ i ].plane->dist ) { break; } } if ( i == b->numsides ) { contents |= b->contents; } } return contents; }
/* ================== CM_BoxTrace ================== */ void CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask) { int i; traceWork_t tw; vec3_t offset; cmodel_t *cmod; clipMap_t *local = 0; cmod = CM_ClipHandleToModel( model, &local ); local->checkcount++; // for multi-check avoidance c_traces++; // for statistics, may be zeroed // fill in a default trace memset( &tw, 0, sizeof(tw) - sizeof(tw.trace.G2CollisionMap)); tw.trace.fraction = 1; // assume it goes the entire distance until shown otherwise if (!local->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]; } 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 // 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 ) { CM_TestInLeaf( &tw, &cmod->leaf, local ); } 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 ) { CM_TraceToLeaf( &tw, &cmod->leaf, local ); } else { CM_TraceThroughTree( &tw, local, 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; }