jedi-academy/code/qcommon/cm_trace.cpp
2013-04-23 15:21:39 +10:00

1244 lines
28 KiB
C++

#include "cm_local.h"
#ifdef _XBOX
#include "../renderer/tr_local.h"
#endif
/*
===============================================================================
POSITION TESTING
===============================================================================
*/
extern cvar_t *com_terrainPhysics;
void VectorAdvance( const vec3_t veca, const float scale, const vec3_t vecb, vec3_t vecc);
/*
================
CM_TestBoxInBrush
================
*/
void CM_TestBoxInBrush( traceWork_t *tw, cbrush_t *brush ) {
int i;
cplane_t *plane;
float dist;
float d1;
cbrushside_t *side;
if (!brush->numsides) {
return;
}
// special test for axial
if ( tw->bounds[0][0] > brush->bounds[1][0]
|| tw->bounds[0][1] > brush->bounds[1][1]
|| tw->bounds[0][2] > brush->bounds[1][2]
|| tw->bounds[1][0] < brush->bounds[0][0]
|| tw->bounds[1][1] < brush->bounds[0][1]
|| tw->bounds[1][2] < brush->bounds[0][2]
) {
return;
}
// the first six planes are the axial planes, so we only
// need to test the remainder
for ( i = 6 ; i < brush->numsides ; i++ ) {
side = brush->sides + i;
#ifdef _XBOX
plane = &cmg.planes[side->planeNum.GetValue()];
#else
plane = side->plane;
#endif
// adjust the plane distance apropriately for mins/maxs
dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal );
d1 = DotProduct( tw->start, plane->normal ) - dist;
// if completely in front of face, no intersection
if ( d1 > 0 ) {
return;
}
}
// inside this brush
tw->trace.startsolid = tw->trace.allsolid = qtrue;
tw->trace.fraction = 0;
tw->trace.contents = brush->contents;
}
/*
================
CM_PlaneCollision
Returns false for a quick getout
================
*/
bool CM_PlaneCollision(traceWork_t *tw, cbrushside_t *side)
{
float dist, f;
float d1, d2;
#ifdef _XBOX
cplane_t *plane = &cmg.planes[side->planeNum.GetValue()];
#else
cplane_t *plane = side->plane;
#endif
// adjust the plane distance apropriately for mins/maxs
dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal );
d1 = DotProduct( tw->start, plane->normal ) - dist;
d2 = DotProduct( tw->end, plane->normal ) - dist;
if (d2 > 0.0f)
{
// endpoint is not in solid
tw->getout = true;
}
if (d1 > 0.0f)
{
// startpoint is not in solid
tw->startout = true;
}
// if completely in front of face, no intersection with the entire brush
if ((d1 > 0.0f) && ( (d2 >= SURFACE_CLIP_EPSILON) || (d2 >= d1) ) )
{
return(false);
}
// if it doesn't cross the plane, the plane isn't relevent
if ((d1 <= 0.0f) && (d2 <= 0.0f))
{
return(true);
}
// crosses face
if (d1 > d2)
{ // enter
f = (d1 - SURFACE_CLIP_EPSILON);
if ( f < 0.0f )
{
f = 0.0f;
if (f > tw->enterFrac)
{
tw->enterFrac = f;
tw->clipplane = plane;
tw->leadside = side;
}
}
else if (f > tw->enterFrac * (d1 - d2) )
{
tw->enterFrac = f / (d1 - d2);
tw->clipplane = plane;
tw->leadside = side;
}
}
else
{ // leave
f = (d1 + SURFACE_CLIP_EPSILON);
if ( f < (d1 - d2) )
{
f = 1.0f;
if (f < tw->leaveFrac)
{
tw->leaveFrac = f;
}
}
else if (f > tw->leaveFrac * (d1 - d2) )
{
tw->leaveFrac = f / (d1 - d2);
}
}
return(true);
}
/*
================
CM_TraceThroughBrush
================
*/
void CM_TraceThroughBrush( traceWork_t *tw, trace_t &trace, cbrush_t *brush, bool infoOnly )
{
int i;
cbrushside_t *side;
tw->enterFrac = -1.0f;
tw->leaveFrac = 1.0f;
tw->clipplane = NULL;
if ( !brush->numsides )
{
return;
}
tw->getout = false;
tw->startout = false;
tw->leadside = NULL;
//
// compare the trace against all planes of the brush
// find the latest time the trace crosses a plane towards the interior
// and the earliest time the trace crosses a plane towards the exterior
//
for (i = 0; i < brush->numsides; i++)
{
side = brush->sides + i;
if(!CM_PlaneCollision(tw, side))
{
return;
}
}
//
// all planes have been checked, and the trace was not
// completely outside the brush
//
if (!tw->startout)
{
if(!infoOnly)
{
// original point was inside brush
trace.startsolid = qtrue;
if (!tw->getout)
{
trace.allsolid = qtrue;
trace.fraction = 0.0f;
}
}
tw->enterFrac = 0.0f;
return;
}
if (tw->enterFrac < tw->leaveFrac)
{
if ((tw->enterFrac > -1.0f) && (tw->enterFrac < trace.fraction))
{
if (tw->enterFrac < 0.0f)
{
tw->enterFrac = 0.0f;
}
if(!infoOnly)
{
trace.fraction = tw->enterFrac;
trace.plane = *tw->clipplane;
trace.surfaceFlags = cmg.shaders[tw->leadside->shaderNum].surfaceFlags;
// tw->trace.sideNum = tw->leadside - cmg.brushsides;
trace.contents = brush->contents;
}
}
}
}
#ifndef BSPC
#ifndef _XBOX // Removing terrain from Xbox
void CM_TraceThroughTerrain( traceWork_t *tw, trace_t &trace, cbrush_t *brush )
{
CCMLandScape *landscape;
vec3_t tBegin, tEnd, tDistance, tStep;
vec3_t baseStart;
vec3_t baseEnd;
int count;
int i;
float fraction;
// At this point we know we may be colliding with a terrain brush (and we know we have a valid terrain structure)
landscape = cmg.landScape;
if (!landscape)
{
assert(landscape);
Com_Error(ERR_FATAL,"Brush had surfaceparm terrain, but there is no Terrain entity on this map!");
}
// Check for absolutely no connection
if(!CM_GenericBoxCollide(tw->bounds, landscape->GetBounds()))
{
return;
}
// Now we know that at least some part of the trace needs to collide with the terrain
// The regular brush collision is handled elsewhere, so advance the ray to an edge in the terrain brush
CM_TraceThroughBrush( tw, trace, brush, true );
// Remember the base entering and leaving fractions
tw->baseEnterFrac = tw->enterFrac;
tw->baseLeaveFrac = tw->leaveFrac;
// Reset to full spread within the brush
tw->enterFrac = -1.0f;
tw->leaveFrac = 1.0f;
// Work out the corners of the AABB when the trace first hits the terrain brush and when it leaves
VectorAdvance(tw->start, tw->baseEnterFrac, tw->end, tBegin);
VectorAdvance(tw->start, tw->baseLeaveFrac, tw->end, tEnd);
VectorSubtract(tEnd, tBegin, tDistance);
// Calculate number of iterations to process
count = ceilf(VectorLength(tDistance) / (landscape->GetPatchScalarSize() * TERRAIN_STEP_MAGIC));
count = 1;
fraction = trace.fraction;
VectorScale(tDistance, 1.0f / count, tStep);
// Save the base start and end vectors
VectorCopy ( tw->start, baseStart );
VectorCopy ( tw->end, baseEnd );
// Use the terrain vectors. Start both at the beginning since the
// step will be added to the end as the first step of the loop
VectorCopy ( tBegin, tw->start );
VectorCopy ( tBegin, tw->end );
// Step thru terrain patches moving on about 1 patch at a time
for ( i = 0; i < count; i ++ )
{
// Add the step to the end
VectorAdd(tw->end, tStep, tw->end);
CM_CalcExtents(tBegin, tw->end, tw, tw->localBounds);
landscape->PatchCollide(tw, trace, tw->start, tw->end, brush->checkcount);
// If collision with something closer than water then just stop here
if ( trace.fraction < fraction )
{
// Convert the fraction of this sub tract into the full trace's fraction
trace.fraction = i * (1.0f / count) + (1.0f / count) * trace.fraction;
break;
}
// Move the end to the start so the next trace starts
// where this one left off
VectorCopy(tw->end, tw->start);
}
// Put the original start and end back
VectorCopy ( baseStart, tw->start );
VectorCopy ( baseEnd, tw->end );
// Convert to global fraction only if something was hit along the way
if ( trace.fraction != 1.0 )
{
trace.fraction = tw->baseEnterFrac + ((tw->baseLeaveFrac - tw->baseEnterFrac) * trace.fraction);
trace.contents = brush->contents;
}
// Collide with any water
if ( tw->contents & CONTENTS_WATER )
{
fraction = landscape->WaterCollide(tw->start, tw->end, trace.fraction);
if( fraction < trace.fraction )
{
VectorSet(trace.plane.normal, 0.0f, 0.0f, 1.0f);
trace.contents = landscape->GetWaterContents();
trace.fraction = fraction;
trace.surfaceFlags = landscape->GetWaterSurfaceFlags();
}
}
}
#endif // _XBOX
#endif
#ifdef _XBOX
static int CM_GetSurfaceIndex(int firstLeafSurface)
{
if(!tr.world ||
firstLeafSurface > tr.world->nummarksurfaces ||
firstLeafSurface < 0) {
return cmg.leafsurfaces[ firstLeafSurface ] ;
} else {
return tr.world->marksurfaces[firstLeafSurface] - tr.world->surfaces;
}
}
#endif
/*
================
CM_TestInLeaf
================
*/
void CM_TestInLeaf( traceWork_t *tw, cLeaf_t *leaf, clipMap_t *local ) {
int k;
int brushnum;
cbrush_t *b;
cPatch_t *patch;
// test box position 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;
}
#ifndef BSPC
#ifndef _XBOX // Removing terrain from Xbox
if (com_terrainPhysics->integer && cmg.landScape && (b->contents & CONTENTS_TERRAIN) )
{
// Invalidate the checkcount for terrain as the terrain brush has to be processed
// many times.
b->checkcount--;
CM_TraceThroughTerrain( tw, tw->trace, b );
// If inside a terrain brush don't bother with regular brush collision
continue;
}
#endif
#endif
CM_TestBoxInBrush( tw, b );
if ( tw->trace.allsolid ) {
return;
}
}
// test against all patches
#ifdef BSPC
if (1) {
#else
if ( !cm_noCurves->integer ) {
#endif //BSPC
for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) {
#ifdef _XBOX
int index = CM_GetSurfaceIndex(leaf->firstLeafSurface + k);
patch = cmg.surfaces[ index ];
#else
patch = local->surfaces[ local->leafsurfaces[ leaf->firstLeafSurface + k ] ];
#endif
if ( !patch ) {
continue;
}
if ( patch->checkcount == local->checkcount ) {
continue; // already checked this brush in another leaf
}
patch->checkcount = local->checkcount;
if ( !(patch->contents & tw->contents)) {
continue;
}
if ( CM_PositionTestInPatchCollide( tw, patch->pc ) ) {
tw->trace.startsolid = tw->trace.allsolid = qtrue;
tw->trace.fraction = 0;
tw->trace.contents = patch->contents;
return;
}
}
}
}
/*
==================
CM_PositionTest
==================
*/
#define MAX_POSITION_LEAFS 1024
void CM_PositionTest( traceWork_t *tw ) {
int leafs[MAX_POSITION_LEAFS];
int i;
leafList_t ll;
// identify the leafs we are touching
VectorAdd( tw->start, tw->size[0], ll.bounds[0] );
VectorAdd( tw->start, tw->size[1], ll.bounds[1] );
for (i=0 ; i<3 ; i++) {
ll.bounds[0][i] -= 1;
ll.bounds[1][i] += 1;
}
ll.count = 0;
ll.maxcount = MAX_POSITION_LEAFS;
ll.list = leafs;
ll.storeLeafs = CM_StoreLeafs;
ll.lastLeaf = 0;
ll.overflowed = qfalse;
cmg.checkcount++;
CM_BoxLeafnums_r( &ll, 0 );
cmg.checkcount++;
// test the contents of the leafs
for (i=0 ; i < ll.count ; i++) {
CM_TestInLeaf( tw, &cmg.leafs[leafs[i]], &cmg );
if ( tw->trace.allsolid ) {
break;
}
}
}
/*
===============================================================================
BOX TRACING
===============================================================================
*/
/*
================
CM_TraceThroughPatch
================
*/
void CM_TraceThroughPatch( traceWork_t *tw, cPatch_t *patch ) {
float oldFrac;
c_patch_traces++;
oldFrac = tw->trace.fraction;
CM_TraceThroughPatchCollide( tw, patch->pc );
if ( tw->trace.fraction < oldFrac ) {
tw->trace.surfaceFlags = patch->surfaceFlags;
tw->trace.contents = patch->contents;
}
}
/*
================
CM_TraceThroughBrush
================
*/
void CM_TraceThroughBrush( traceWork_t *tw, cbrush_t *brush ) {
int i;
cplane_t *plane, *clipplane;
float dist;
float enterFrac, leaveFrac;
float d1, d2;
qboolean getout, startout;
float f;
cbrushside_t *side, *leadside;
enterFrac = -1.0;
leaveFrac = 1.0;
clipplane = NULL;
if ( !brush->numsides ) {
return;
}
// I'm not sure if test is strictly correct. Are all
// bboxes axis aligned? Do I care? It seems to work
// good enough...
if ( tw->bounds[0][0] > brush->bounds[1][0]
|| tw->bounds[0][1] > brush->bounds[1][1]
|| tw->bounds[0][2] > brush->bounds[1][2]
|| tw->bounds[1][0] < brush->bounds[0][0]
|| tw->bounds[1][1] < brush->bounds[0][1]
|| tw->bounds[1][2] < brush->bounds[0][2]
) {
return;
}
c_brush_traces++;
getout = qfalse;
startout = qfalse;
leadside = NULL;
//
// compare the trace against all planes of the brush
// find the latest time the trace crosses a plane towards the interior
// and the earliest time the trace crosses a plane towards the exterior
//
for (i=0 ; i<brush->numsides ; i++) {
side = brush->sides + i;
#ifdef _XBOX
plane = &cmg.planes[side->planeNum.GetValue()];
#else
plane = side->plane;
#endif
// adjust the plane distance apropriately for mins/maxs
dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal );
d1 = DotProduct( tw->start, plane->normal ) - dist;
d2 = DotProduct( tw->end, plane->normal ) - dist;
if (d2 > 0) {
getout = qtrue; // endpoint is not in solid
}
if (d1 > 0) {
startout = qtrue;
}
// if completely in front of face, no intersection with the entire brush
if (d1 > 0 && ( d2 >= SURFACE_CLIP_EPSILON || d2 >= d1 ) ) {
return;
}
// if it doesn't cross the plane, the plane isn't relevent
if (d1 <= 0 && d2 <= 0 ) {
continue;
}
// crosses face
if (d1 > d2) { // enter
f = (d1-SURFACE_CLIP_EPSILON) / (d1-d2);
if ( f < 0 ) {
f = 0;
}
if (f > enterFrac) {
enterFrac = f;
clipplane = plane;
leadside = side;
}
} else { // leave
f = (d1+SURFACE_CLIP_EPSILON) / (d1-d2);
if ( f > 1 ) {
f = 1;
}
if (f < leaveFrac) {
leaveFrac = f;
}
}
}
//
// all planes have been checked, and the trace was not
// completely outside the brush
//
if (!startout) { // original point was inside brush
tw->trace.startsolid = qtrue;
tw->trace.contents |= brush->contents; //note, we always want to know the contents of something we're inside of
if (!getout)
{ //endpoint was inside brush
tw->trace.allsolid = qtrue;
tw->trace.fraction = 0;
}
return;
}
if (enterFrac < leaveFrac) {
if (enterFrac > -1 && enterFrac < tw->trace.fraction) {
if (enterFrac < 0) {
enterFrac = 0;
}
tw->trace.fraction = enterFrac;
tw->trace.plane = *clipplane;
tw->trace.surfaceFlags = cmg.shaders[leadside->shaderNum].surfaceFlags;
tw->trace.contents = brush->contents;
}
}
}
/*
================
CM_PatchCollide
By the time we get here we know the AABB is within the patch AABB ie there is a chance of collision
The collision data is made up of bounds, 2 triangle planes
There is an BB check for the terxel check to see if it is worth checking the planes.
Collide with both triangles to find the shortest fraction
================
*/
void CM_HandlePatchCollision(struct traceWork_s *tw, trace_t &trace, const vec3_t tStart, const vec3_t tEnd, CCMPatch *patch, int checkcount)
{
int numBrushes, i;
cbrush_t *brush;
// Get the collision data
brush = patch->GetCollisionData();
numBrushes = patch->GetNumBrushes();
for(i = 0; i < numBrushes; i++, brush++)
{
if(brush->checkcount == checkcount)
{
return;
}
// Generic collision of terxel bounds to line segment bounds
if(!CM_GenericBoxCollide(brush->bounds, tw->localBounds))
{
continue;
}
brush->checkcount = checkcount;
//CM_TraceThroughBrush(tw, trace, brush, false );
CM_TraceThroughBrush(tw, brush);
if (trace.fraction <= 0.0)
{
break;
}
}
}
/*
================
CM_GenericBoxCollide
================
*/
bool CM_GenericBoxCollide(const vec3pair_t abounds, const vec3pair_t bbounds)
{
int i;
// Check for completely no intersection
for(i = 0; i < 3; i++)
{
if(abounds[1][i] < bbounds[0][i])
{
return(false);
}
if(abounds[0][i] > bbounds[1][i])
{
return(false);
}
}
return(true);
}
/*
================
CM_TraceToLeaf
================
*/
void CM_TraceToLeaf( traceWork_t *tw, 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;
}
#ifndef BSPC
#ifndef _XBOX // Removing terrain from Xbox
if ( com_terrainPhysics->integer && cmg.landScape && (b->contents & CONTENTS_TERRAIN) )
{
// Invalidate the checkcount for terrain as the terrain brush has to be processed
// many times.
b->checkcount--;
CM_TraceThroughTerrain( tw, tw->trace, b );
// If inside a terrain brush don't bother with regular brush collision
continue;
}
#endif
#endif
//if (b->contents & CONTENTS_PLAYERCLIP) continue;
CM_TraceThroughBrush( tw, b );
if ( !tw->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++ ) {
#ifdef _XBOX
int index = CM_GetSurfaceIndex(leaf->firstLeafSurface + k);
patch = cmg.surfaces[ index ];
#else
patch = local->surfaces[ local->leafsurfaces[ leaf->firstLeafSurface + k ] ];
#endif
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, patch );
if ( !tw->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, 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;
#ifdef _XBOX
if(!tr.world) {
return;
}
#endif
if (tw->trace.fraction <= p1f) {
return; // already hit something nearer
}
// if < 0, we are in a leaf node
if (num < 0) {
CM_TraceToLeaf( tw, &local->leafs[-1-num], local );
return;
}
//
// find the point distances to the seperating plane
// and the offset for the size of the box
//
node = local->nodes + num;
#ifdef _XBOX
plane = cmg.planes + tr.world->nodes[num].planeNum;
#else mnode_s
plane = node->plane;
#endif
#if 0
// uncomment this to test against every leaf in the world for debugging
CM_TraceThroughTree( tw, local, node->children[0], p1f, p2f, p1, p2 );
CM_TraceThroughTree( tw, local, node->children[1], p1f, p2f, p1, p2 );
return;
#endif
// 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 = fabs(tw->extents[0]*plane->normal[0]) +
fabs(tw->extents[1]*plane->normal[1]) +
fabs(tw->extents[2]*plane->normal[2]);
offset *= 2;
#if 0
CM_TraceThroughTree( tw, local, node->children[0], p1f, p2f, p1, p2 );
CM_TraceThroughTree( tw, local, node->children[1], p1f, p2f, p1, p2 );
return;
#endif
offset = tw->maxOffset;
offset = 2048;
}
}
// see which sides we need to consider
if ( t1 >= offset + 1 && t2 >= offset + 1 ) {
CM_TraceThroughTree( tw, local, node->children[0], p1f, p2f, p1, p2 );
return;
}
if ( t1 < -offset - 1 && t2 < -offset - 1 ) {
CM_TraceThroughTree( tw, 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, 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, local, node->children[side^1], midf, p2f, mid, p2 );
}
void CM_CalcExtents(const vec3_t start, const vec3_t end, const traceWork_t *tw, vec3pair_t bounds)
{
int i;
for ( i = 0 ; i < 3 ; i++ )
{
if ( start[i] < end[i] )
{
bounds[0][i] = start[i] + tw->size[0][i];
bounds[1][i] = end[i] + tw->size[1][i];
}
else
{
bounds[0][i] = end[i] + tw->size[0][i];
bounds[1][i] = start[i] + tw->size[1][i];
}
}
}
//======================================================================
/*
==================
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;
}
/*
==================
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,
const vec3_t mins, const vec3_t maxs,
clipHandle_t model, int brushmask,
const vec3_t origin, const vec3_t angles) {
trace_t trace;
vec3_t start_l, end_l;
vec3_t a;
vec3_t forward, right, up;
vec3_t temp;
qboolean rotated;
vec3_t offset;
vec3_t symetricSize[2];
int i;
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;
}
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
CM_BoxTrace( &trace, start_l, end_l, symetricSize[0], symetricSize[1], model, 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]);
*results = trace;
}
/*
=================
CM_CullBox
Returns true if culled out
=================
*/
bool CM_CullBox(const cplane_t *frustum, const vec3_t transformed[8])
{
int i, j;
const cplane_t *frust;
// check against frustum planes
for (i=0, frust=frustum; i<4 ; i++, frust++)
{
for (j=0 ; j<8 ; j++)
{
if (DotProduct(transformed[j], frust->normal) > frust->dist)
{ // a point is in front
break;
}
}
if (j == 8)
{ // all points were behind one of the planes
return true;
}
}
return false;
}
/*
=================
CM_CullWorldBox
Returns true if culled out
=================
*/
bool CM_CullWorldBox (const cplane_t *frustum, const vec3pair_t bounds)
{
int i;
vec3_t transformed[8];
for (i = 0 ; i < 8 ; i++)
{
transformed[i][0] = bounds[i & 1][0];
transformed[i][1] = bounds[(i >> 1) & 1][1];
transformed[i][2] = bounds[(i >> 2) & 1][2];
}
//rwwFIXMEFIXME: Was not ! before. But that seems the way it should be and it works that way. Why?
return(!CM_CullBox(frustum, transformed));
}