technically, what I am about to post is a valid line of code...if you don't believe me, you can either die, or try it yourself. Here we go:
EDIT:
and just to show off, here's some of my ray tracing code for colliding particles against brushes in a binary space partitioned world!
Code:
#include "Defines.h"
#include "Quake3BSP.h"
#include "LoadTexture.h"
#include "Glext.h"
//#include "GLee.h"
#include "Camera.h"
#include "Frustum.h"
#include "FPS.h"
#include "trace.h"
#include "GAMEEXPORT.H"
#include "WorldObject.h"
#include "Asteroid.h"
#include <fstream>
#include "WorldObject.h"
extern VWorldObject *berttracker;
extern std::ofstream trace;
extern GameExport Export;
#define HITTOL (.01) //how close we have to come to an entity to run its touch function
#define MINMAG (3) //minimum length to pass to brush collision program
/*
-11 22 03: ALL NEW RAY TRACING STUFF
For right now Trace will be performed, and instead of returning a structure the calling routine
will have to access BSP.mBSPCOLINFO (speed issues)...I don't care how ugly it looks
*/
int debug(0);
VWorldObject *caller=NULL;
void BSP::TraceRay(VWorldObject*caller,Vector3 *Start, Velocity *to, Vector3 *Mins, Vector3 *Maxs, int flags)
{
if(to->Mag == 0)
{
MakeZeroTrace();
return;
}
InitBSPColInfo(Start, to, Mins, Maxs);
//this was for enlarging before finding leaves touched (BAD)
// mBSPColInfo.BSPEnd = mBSPColInfo.BSPStart + (to->Dir*(to->Mag*2));
FindLeavesTouched(0);
//Undo extensions that were made in leaf finding code
mBSPColInfo.mfCurrMaxs=*Maxs;
mBSPColInfo.mfCurrMins=*Mins;
mBSPColInfo.BSPEnd = mBSPColInfo.BSPStart + (to->Dir*to->Mag);
//Enlarge for brush testing to MINMAG units
if(to->Mag < MINMAG)
{
mBSPColInfo.BSPEnd = mBSPColInfo.BSPStart + (to->Dir*MINMAG);
}
int size = mBSPColInfo.TouchedLeafs.size();
int i(0);
if(flags & HITWORLD) //Test for collision against the world model brushes
{
for( i = 0; i < size; i++)
{
BBoxCheckLeafForCollision(mBSPColInfo.TouchedLeafs.at(i));
}
}
if(flags & HITENTS) //Test for collisions against entities
{
for( i = 0; i < size; i++)
{
tBSPLeaf *temp = &mpLeafs[mBSPColInfo.TouchedLeafs.at(i)];
int w = temp->entitylist.size();
for(int x = 0; x < w; x++)
{
BBoxCheckEntity(temp->entitylist.at(x));
}
}
size = mBSPColInfo.HitEntList.size();
if(size)
{
EntCollisionInfo *closest = &mBSPColInfo.HitEntList.at(0);
for(int i = 0; i < size; i++)
{
if(mBSPColInfo.HitEntList.at(i).fraction<closest->fraction)
{
closest = &mBSPColInfo.HitEntList.at(i);
}
}
mBSPColInfo.mpClosestEnt = closest->HitEnt;
}
}
if(mBSPColInfo.closestFraction == 1.0f)
{
mBSPColInfo.mIntersect = mBSPColInfo.BSPStart + (to->Dir * to->Mag);
return; //Wouldn't have hit anything anyway so don't worry about the other garbage
}
else
{
if(to->Mag < 1) //Here, if the original move was small enough, we don't actually have to move it at all
mBSPColInfo.mIntersect = mBSPColInfo.BSPStart;
else
//Closest fraction here is smaller than what the real fraction would have been but you won't go through walls
//And you won't 'fudge' into them
mBSPColInfo.mIntersect = mBSPColInfo.BSPStart + (to->Dir * to->Mag * mBSPColInfo.closestFraction);
}
/*
-This gets kind of tricky because neighbors don't run their touch functions (such as doors
that have been teamed together do not re-open when they hit each other)
*/
if(flags & RUNTOUCH)
{
if(!(flags&HITENTS) || !mBSPColInfo.mpClosestEnt)
return; //Never tested for collision against entities, or you hit the world first so fugazi !¿½¡
if(caller)
{
if(caller->mpNeighbor)
{
if(caller->mpNeighbor!=mBSPColInfo.mpClosestEnt)
{
mBSPColInfo.mpClosestEnt->VWOTouchFunc();
}
}
else //Caller doesn't even have a neighbor
{
mBSPColInfo.mpClosestEnt->VWOTouchFunc();
}
}
else //Caller didn't specify itself so we call touchfunc
{
mBSPColInfo.mpClosestEnt->VWOTouchFunc();
}
//Only call if we passed in caller and we're not neighbors
}
}
/*
-Tested: all aspects of this work perfectly all of the time (tested with randomly generated start and end points
and their hulls)
*/
void BSP::EncloseVolumeWithBox(Vector3*start, Vector3*end, Vector3*mins, Vector3*maxs, BSPBBox*bbox,int paranoia)
{
//New bounding box data
Vector3 newmins(0,0,0);
Vector3 newmaxs(0,0,0);
Vector3 newpos(0,0,0);
Vector3 disp = *end-*start;
if(disp.x > 0)
{
newmaxs.x = disp.x + maxs->x;
newmins.x = mins->x;
}
else
{
newmaxs.x = maxs->x;
newmins.x = mins->x + disp.x;
}
if(disp.y > 0)
{
newmaxs.y = disp.y + maxs->y;
newmins.y = mins->y;
}
else
{
newmaxs.y = maxs->y;
newmins.y = mins->y + disp.y;
}
if(disp.z > 0)
{
newmaxs.z = disp.z + maxs->z;
newmins.z = mins->z;
}
else
{
newmaxs.z = maxs->z;
newmins.z = mins->z + disp.z;
}
newmaxs += *start;
newmins += *start;
bbox->pos = (newmaxs + newmins) * .5;
bbox->objmaxs = (newmaxs - bbox->pos)+paranoia;
bbox->objmins = (newmins - bbox->pos)-paranoia;
}
/*
-Don't modify the starting position because that seems to screw up the fractions and allows
you to go through walls.
-When you get here, it assumes that the starting point and ending point aren't the same, so it
doesn't bother to determine when the length is zero (and results in an infinite loop if its length is zero)
*/
void BSP::ExtendMove(float paranoia)
{
Vector3 disp = mBSPColInfo.BSPEnd - mBSPColInfo.BSPStart;
float basic_length = (disp.x * disp.x) + (disp.y * disp.y) + (disp.z * disp.z);
float basic_wanted = paranoia * paranoia;
int numtries(0);
while(basic_length <= basic_wanted)
{
numtries++;
disp = disp * 5; //Don't want this too big because when you use super long traces it 'fudges' into the walls
basic_length = (disp.x * disp.x) + (disp.y * disp.y) + (disp.z * disp.z);
}
//Now you know disp has a length of a bare minimum of PARANOIA units
mBSPColInfo.BSPEnd = mBSPColInfo.BSPStart + (disp*-1);
}
void BSP::FindLeavesTouched(int node)
{
if(node < 0)
{
mBSPColInfo.TouchedLeafs.push_back(~node);
return;
}
float offset;
Vector3 veceff;
Vector3 *normal = &mpPlanes[mpNodes[node].plane].vNormal;
if(normal->x < 0)
veceff.x = mBSPColInfo.mfCurrMaxs.x;
else
veceff.x = mBSPColInfo.mfCurrMins.x;
if(normal->y < 0)
veceff.y = mBSPColInfo.mfCurrMaxs.y;
else
veceff.y = mBSPColInfo.mfCurrMins.y;
if(normal->z < 0)
veceff.z = mBSPColInfo.mfCurrMaxs.z;
else
veceff.z = mBSPColInfo.mfCurrMins.z;
//FIXME: why doesn't this work with extents? That would be a lot more efficient
offset = fabs(DotProduct(&veceff, normal));
float startdist = DotProduct(normal, &mBSPColInfo.BSPStart) - mpPlanes[mpNodes[node].plane].d;
float enddist = DotProduct(normal, &mBSPColInfo.BSPEnd) - mpPlanes[mpNodes[node].plane].d;
if(startdist >= offset && enddist >= offset)
{
FindLeavesTouched(mpNodes[node].front);
return;
}
if(startdist < -offset && enddist < -offset)
{
FindLeavesTouched(mpNodes[node].back);
return;
}
/*
-Spans the node so touch the front and back
*/
FindLeavesTouched(mpNodes[node].front);
FindLeavesTouched(mpNodes[node].back);
}
void BSP::InitBSPColInfo(Vector3 *Start, Velocity *to, Vector3 *Mins, Vector3 *Maxs)
{
mBSPColInfo.BSPStart = *Start;
mBSPColInfo.BSPEnd = *Start + (to->Dir * to->Mag);
mBSPColInfo.mfCurrMaxs = *Maxs+100.0f;
mBSPColInfo.mfCurrMins = *Mins-100.0f;
mBSPColInfo.mpClosestEnt = 0;
if(-Mins->x > Maxs->x)
mBSPColInfo.mAbsMaxs.x = -Mins->x;
else
mBSPColInfo.mAbsMaxs.x = Maxs->x;
if(-Mins->y > Maxs->y)
mBSPColInfo.mAbsMaxs.y = -Mins->y;
else
mBSPColInfo.mAbsMaxs.y = Maxs->y;
if(-Mins->z > Maxs->z)
mBSPColInfo.mAbsMaxs.z = -Mins->z;
else
mBSPColInfo.mAbsMaxs.z = Maxs->z;
mBSPColInfo.HitEntList.clear(); //Clear hit entity list before using
mBSPColInfo.closestFraction = 1.0f;
mBSPColInfo.hitNormal = Vector3(0,0,0);
mBSPColInfo.hitNormalDist = 0;
mBSPColInfo.TouchedLeafs.clear(); //Leafs we passed through
}
void BSP::MakeZeroTrace()
{
mBSPColInfo.closestFraction = 0.0f;
mBSPColInfo.hitNormal = Vector3(0,0,0);
mBSPColInfo.hitNormalDist = 0;
mBSPColInfo.mpClosestEnt = 0;
}