class RTrackVRML | .h |
constructor | RTrackVRML() : RTrack(), nodes(0), flags(0) |
destructor | ~RTrackVRML() |
Flat functions |
OptimizeModel | static void OptimizeModel(DGeode *geode) Make sure the geode paints fast for VRML tracks |
class RTrackVRML | .h |
AddNode | bool AddNode(const RTV_Node *newNode) |
BuildCuller | void BuildCuller() Create culling structure |
DestroyCuller | void DestroyCuller() |
PaintNode | void PaintNode(int n) Paint sector #n |
Paint | void Paint() Given the current graphics matrix, paint the track |
Flat functions |
SetGLColor | static void SetGLColor(QColor *color) Local function to convert rgba to float |
PaintRope | static void PaintRope(DVector3 *v1,DVector3 *v2,QColor *col) Paint rope with poles |
class RTrackVRML | .h |
PaintHidden | void PaintHidden() Paint hidden stuff |
Load | bool Load() Loads a track in the TrackFlat format (very simple) Calls the application back if a function is supplied using SetLoadCallback() (this function may take some time). |
Save | bool Save() |
Flat functions |
Unproject | static void Unproject(DVector3 *vWin,DVector3 *vObj) Unprojects a window coordinate (vWin) into a 3D coordinate (vObj) vWin->z defines the depth into the display Is not that fast, since it retrieves matrix state information, and it inverts the model*proj matrix. For some easy picking, it will do though. |
Project | static void Project(DVector3 *vObj,DVector3 *vWin) !! Text must be updated Projects a window coordinate (vWin) into a 3D coordinate (vObj) vWin->z defines the depth into the display Is not that fast, since it retrieves matrix state information, and it inverts the model*proj matrix. For some easy picking, it will do though. |
RaySphereIntersect | static bool RaySphereIntersect(const DVector3 *origin,const DVector3 *dir, const DVector3 *center,dfloat radius,DVector3 *intersect) Determines whether a ray intersects the sphere From 'Real-Time Rendering', page 299. Calculates intersection point in 'intersect', if wished. Otherwise, pass 0 for 'intersect' (is quite a bit faster). |
RayTriangleIntersect | static int RayTriangleIntersect(const dfloat orig[3],const dfloat dir[3], const dfloat vert0[3],const dfloat vert1[3],const dfloat vert2[3], dfloat *t,dfloat *u,dfloat *v) From Real-Time Rendering, page 305 Returns 0 if not hit is found If a hit is found, t contains the distance along the ray (dir) and u/v contain u/v coordinates into the triangle (like texture coordinates). |
FindGeodeTriangle | static bool FindGeodeTriangle(DVector3 *origin,DVector3 *dir,DGeode *geode, RSurfaceInfo *si,DVector3 *inter) The ray is traced and tested if it hits any triangle in 'geode'. Returns index of triangle. If a triangle is hit, the info is saved as cache info in 'si'. |
CacheCheckTriangle | static bool CacheCheckTriangle(const DVector3 *origin,const DVector3 *dir, const DGeode *geode,const RSurfaceInfo *si,DVector3 *inter) Check if the cached triangle still hits If so, it returs TRUE and the intersection in 'inter' |
class RTrackVRML | .h |
GetSurfaceInfo | void GetSurfaceInfo(DVector3 *pos,DVector3 *dir, RSurfaceInfo *si) Determines what the surface looks like under 'pos' 'dir' is the direction of the ray to trace (often like (0,-1,0)) Currently returns valid (si->): x,y,z |
Flat functions |
vOrigin.DbgPrint | vOrigin.DbgPrint("origin"); vDir.DbgPrint("dir"); #endif // Store cache info that wasn't already stored // by FindGeodeTriangle() si->lastNode=i; #ifdef OBS // Don't seek further break; #endif } } } dbg("--\n"); } |
/*
* RTrackVRML - VRML/SCGT-type tracks
* 12-11-00: Created!
* 10-03-01: Optimized a bit in the ray intersection parts.
* NOTES:
* - Currently uses .wrl files, although this will probably change
* into .dof files for speed later on.
* (c) Dolphinity/Ruud van Gaal
*/
#include <racer/racer.h>
#include <qlib/debug.h>
#pragma hdrstop
#include <racer/trackv.h>
#include <d3/culler.h>
#include <qlib/app.h>
#include <math.h>
DEBUG_ENABLE
// File to contain geode names
#define FILE_GEOMETRY "geometry.ini"
// Macro to avoid calling BuildCuller() at every track trace
#define QUICK_BUILD_CULLER if(!(flags&CULLER_READY))BuildCuller()
#undef DBG_CLASS
#define DBG_CLASS "RTrackVRML"
RTrackVRML::RTrackVRML()
: RTrack(), nodes(0), flags(0)
{
DBG_C("ctor")
int i;
for(i=0;i<MAX_NODE;i++)
{ node[i]=0;
}
culler=new DCullerSphereList();
}
RTrackVRML::~RTrackVRML()
{
int i;
for(i=0;i<MAX_NODE;i++)
{ if(node[i])
{ if(node[i]->model)delete node[i]->model;
delete node[i];
}
}
delete culler;
}
/*****************
* BASIC BUILDING *
*****************/
static void OptimizeModel(DGeode *geode)
// Make sure the geode paints fast for VRML tracks
{
int i;
// Make sure geode has no normals (no lighting used)
geode->DestroyNormals();
// All materials are largely equal, just a decal-ed texture.
// Avoid state changes in the geode's materials
for(i=0;i<geode->materials;i++)
{
geode->material[i]->Enable(
/*DMaterial::NO_TEXTURE_ENABLING|*/
DMaterial::NO_MATERIAL_PROPERTIES|
DMaterial::NO_BLEND_PROPERTIES);
}
// SCGT VRML nodes are never drawn at a different position
// This may change in the future, since I'd really like
// reusing of grandstands, little houses etc.
// So this optimization may disappear in the future (or be
// done only if the position is (0,0,0))
geode->Set2D();
// All geodes are part of one big scene
geode->EnableCSG();
}
bool RTrackVRML::AddNode(const RTV_Node *newNode)
{
int i;
RTV_Node *nn;
// Out of space?
if(nodes==MAX_NODE)
{
qwarn("RTrackVRML:AddNodes(); out of nodes");
return FALSE;
}
//node[nodes]=(RTV_Node*)qcalloc(sizeof(RTV_Node));
node[nodes]=new RTV_Node();
// Copy parameters
nn=node[nodes];
nn->type=newNode->type;
nn->fileName=newNode->fileName;
nn->model=newNode->model;
// Prepare model for fast batch drawing
OptimizeModel(newNode->model);
// Node added
nodes++;
// Track was modified
DestroyCuller();
return TRUE;
}
/**********
* Culling *
**********/
void RTrackVRML::BuildCuller()
// Create culling structure
{
int i;
if(flags&CULLER_READY)return;
qdbg("RTrackVRML:Paint(); create culler\n");
for(i=0;i<nodes;i++)
{
culler->AddGeode(node[i]->model);
//qdbg("add geode %p\n",node[i]->model);
}
flags|=CULLER_READY;
}
void RTrackVRML::DestroyCuller()
{
culler->Destroy();
flags&=~CULLER_READY;
}
/***********
* PAINTING *
***********/
void RTrackVRML::PaintNode(int n)
// Paint sector #n
{
DBG_C("PaintNode")
DBG_ARG_I(n)
RTV_Node *pNode;
pNode=node[n];
//qdbg("RTrackVRML:PaintNode(%d)=%p\n",n,pNode);
pNode->model->EnableCSG();
pNode->model->Paint();
}
void RTrackVRML::Paint()
// Given the current graphics matrix, paint the track
{
DBG_C("Paint")
int i;
QUICK_BUILD_CULLER;
// Update culler (if viewpoint has changed)
culler->CalcFrustumEquations();
// Set OpenGL state
glEnable(GL_CULL_FACE);
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glShadeModel(GL_FLAT);
//glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// Leave painting to culler
culler->Paint();
#ifdef OBS_DONE_BY_CULLER
for(i=0;i<nodes;i++)
{
PaintNode(i);
}
#endif
}
static void SetGLColor(QColor *color)
// Local function to convert rgba to float
{
GLfloat cr,cg,cb,ca;
cr=(GLfloat)color->GetR()/255;
cg=(GLfloat)color->GetG()/255;
cb=(GLfloat)color->GetB()/255;
ca=(GLfloat)color->GetA()/255;
glColor4f(cr,cg,cb,ca);
}
static void PaintRope(DVector3 *v1,DVector3 *v2,QColor *col)
// Paint rope with poles
{
SetGLColor(col);
glBegin(GL_LINES);
// Both
glVertex3f(v1->x,v1->y,v1->z);
glVertex3f(v1->x,v1->y+1,v1->z);
glVertex3f(v2->x,v2->y,v2->z);
glVertex3f(v2->x,v2->y+1,v2->z);
// Connection
glVertex3f(v1->x,v1->y+1,v1->z);
glVertex3f(v2->x,v2->y+1,v2->z);
glEnd();
}
void RTrackVRML::PaintHidden()
// Paint hidden stuff
{
DBG_C("PaintHidden")
int i;
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glDisable(GL_TEXTURE_2D);
// Paint timelines
for(i=0;i<GetTimeLines();i++)
{
RTimeLine *t=GetTimeLine(i);
QColor col(255,255,0);
PaintRope(&t->from,&t->to,&col);
}
for(i=0;i<GetGridPosCount();i++)
{
RCarPos *p=GetGridPos(i);
QColor col(0,255,0);
PaintRope(&p->from,&p->to,&col);
}
for(i=0;i<GetPitPosCount();i++)
{
RCarPos *p=GetPitPos(i);
QColor col(255,0,255);
PaintRope(&p->from,&p->to,&col);
}
}
/**********
* LOADING *
**********/
bool RTrackVRML::Load()
// Loads a track in the TrackFlat format (very simple)
// Calls the application back if a function is supplied
// using SetLoadCallback() (this function may take some time).
{
QInfo *infoTrk;
QInfoIterator *ii;
qstring s,fileName;
RTV_Node node;
DGeode *model;
char buf[256];
int current,total;
// First load generic data
if(!RTrack::Load())return FALSE;
// Select OpenGL context to create textures in
if(!QCV)
{ qerr("RTrackVRML: can't load track without a canvas");
return FALSE;
}
sprintf(buf,"%s/%s",(cstring)trackDir,FILE_GEOMETRY);
infoTrk=new QInfo(buf);
// Count #objects to come (for the callback function)
ii=new QInfoIterator(infoTrk,"objects");
for(total=0;ii->GetNext(s);total++);
delete ii;
// Actually load the objects
current=0;
ii=new QInfoIterator(infoTrk,"objects");
while(ii->GetNext(s))
{
//qdbg("object '%s'\n",(cstring)s);
node.type=0;
sprintf(buf,"%s.file",(cstring)s);
infoTrk->GetString(buf,fileName);
// Use last part of the info path as the name
node.fileName=QFileExtension(s)+1;
// Create textures in QCV (!)
QCV->Select();
model=new DGeode(fileName);
{
sprintf(buf,"%s/%s",(cstring)trackDir,(cstring)fileName);
QFile f(RFindFile(fileName,trackDir));
//qdbg(" importing '%s'\n",RFindFile(fileName,trackDir));
model->SetMapPath(RFindFile(".",trackDir));
//qdbg(" map path='%s'\n",RFindFile(".",trackDir));
if(!model->ImportDOF(&f))
{
qwarn("Can't import DOF '%s'",(cstring)fileName);
continue;
}
}
// Transfer model to node (don't free geode)
node.model=model;
AddNode(&node);
// Notify application of progress
current++;
if(cbLoad)
if(!cbLoad(current,total,node.fileName))break;
}
delete ii;
delete infoTrk;
return TRUE;
}
/*********
* Saving *
*********/
bool RTrackVRML::Save()
{
int i;
QInfo *infoTrk;
char buf[256],buf2[256];
// First save generic data
if(!RTrack::Save())return FALSE;
sprintf(buf,"%s/%s",(cstring)trackDir,FILE_GEOMETRY);
infoTrk=new QInfo(buf);
// Write geode names
for(i=0;i<nodes;i++)
{
//qdbg("Save node '%s'\n",(cstring)node[i]->fileName);
sprintf(buf,"objects.%s.file",(cstring)node[i]->fileName);
sprintf(buf2,"%s.dof",(cstring)node[i]->fileName);
infoTrk->SetString(buf,buf2);
}
delete infoTrk;
return TRUE;
}
/********************
* Surface detection *
********************/
/********************
* Manual projection *
********************/
static void Unproject(DVector3 *vWin,DVector3 *vObj)
// Unprojects a window coordinate (vWin) into a 3D coordinate (vObj)
// vWin->z defines the depth into the display
// Is not that fast, since it retrieves matrix state information,
// and it inverts the model*proj matrix.
// For some easy picking, it will do though.
{
double mModel[16],mPrj[16];
int viewport[4];
GLdouble wx,wy,wz;
GLdouble ox,oy,oz;
int i;
//qdbg("Unprj: glCtx: %p\n",glXGetCurrentContext());
// Retrieve matrices and viewport settings
glGetDoublev(GL_MODELVIEW_MATRIX,mModel);
glGetDoublev(GL_PROJECTION_MATRIX,mPrj);
glGetIntegerv(GL_VIEWPORT,viewport);
#ifdef OBS
for(i=0;i<16;i++)
{
qdbg("mModel[%d]=%g\n",i,mModel[i]);
}
for(i=0;i<16;i++)
{
qdbg("mPrj[%d]=%g\n",i,mPrj[i]);
}
#endif
// Reverse projection
wx=vWin->x;
wy=vWin->y;
wz=vWin->z;
if(!gluUnProject(wx,wy,wz,mModel,mPrj,viewport,&ox,&oy,&oz))
{
qwarn("Unproject failed");
vObj->SetToZero();
return;
}
vObj->x=(dfloat)ox;
vObj->y=(dfloat)oy;
vObj->z=(dfloat)oz;
if(!gluProject(ox,oy,oz,mModel,mPrj,viewport,&wx,&wy,&wz))
{
qwarn("Project failed");
}
//qdbg("gluProject: %f,%f,%f\n",wx,wy,wz);
}
static void Project(DVector3 *vObj,DVector3 *vWin)
// !! Text must be updated
// Projects a window coordinate (vWin) into a 3D coordinate (vObj)
// vWin->z defines the depth into the display
// Is not that fast, since it retrieves matrix state information,
// and it inverts the model*proj matrix.
// For some easy picking, it will do though.
{
double mModel[16],mPrj[16];
int viewport[4],i;
GLdouble wx,wy,wz;
GLdouble ox,oy,oz;
// Retrieve matrices and viewport settings
glGetDoublev(GL_MODELVIEW_MATRIX,mModel);
glGetDoublev(GL_PROJECTION_MATRIX,mPrj);
glGetIntegerv(GL_VIEWPORT,viewport);
#ifdef OBS
for(i=0;i<16;i++)
{
qdbg("mModel[%d]=%g\n",i,mModel[i]);
}
for(i=0;i<16;i++)
{
qdbg("mPrj[%d]=%g\n",i,mPrj[i]);
}
#endif
// Reverse projection
ox=vObj->x;
oy=vObj->y;
oz=vObj->z;
if(!gluProject(ox,oy,oz,mModel,mPrj,viewport,&wx,&wy,&wz))
{
qwarn("gluProject failed");
vWin->SetToZero();
return;
}
vWin->x=(dfloat)wx;
vWin->y=(dfloat)wy;
vWin->z=(dfloat)wz;
}
/****************************
* RAY - SPHERE Intersection *
****************************/
static bool RaySphereIntersect(const DVector3 *origin,const DVector3 *dir,
static bool RaySphereIntersect(const DVector3 *origin,const DVector3 *dir, const DVector3 *center,dfloat radius,DVector3 *intersect)
// Determines whether a ray intersects the sphere
// From 'Real-Time Rendering', page 299.
// Calculates intersection point in 'intersect', if wished. Otherwise,
// pass 0 for 'intersect' (is quite a bit faster).
{
DVector3 l;
dfloat d,lSquared,mSquared;
dfloat rSquared=radius*radius;
#ifdef OBS
qdbg("origin %f,%f,%f\n",origin->x,origin->y,origin->z);
qdbg("dir %f,%f,%f\n",dir->x,dir->y,dir->z);
#endif
// Calculate line from origin to center
l.x=center->x-origin->x;
l.y=center->y-origin->y;
l.z=center->z-origin->z;
//qdbg("line=%f,%f,%f\n",l.x,l.y,l.z);
// Calculate length of projection of direction onto that line
d=l.Dot(*dir);
//qdbg(" proj. d=%f\n",d);
lSquared=l.DotSelf();
//qdbg(" l^2=%f, radius=%f (^2=%f)\n",lSquared,radius,rSquared);
if(d<0&&lSquared>rSquared)
{
// No intersection
return FALSE;
}
// Check for how far we are off the center
mSquared=lSquared-d*d;
if(mSquared>rSquared)return FALSE;
// Calculate intersection point, if requested
if(!intersect)return TRUE;
dfloat q,t;
q=sqrt(rSquared-mSquared);
if(lSquared>rSquared)t=d-q;
else t=d+q;
//qdbg("t=%f\n",t);
// Intersection point is t*(*dir) away from the origin
intersect->x=origin->x+t*dir->x;
intersect->y=origin->y+t*dir->y;
intersect->z=origin->z+t*dir->z;
return TRUE;
}
/******************************
* RAY - TRIANGLE Intersection *
******************************/
#define CROSS(dest,v1,v2) \
dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \
dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \
dest[2]=v1[0]*v2[1]-v1[1]*v2[0];
#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2])
#define SUB(dest,v1,v2)\
dest[0]=v1[0]-v2[0]; \
dest[1]=v1[1]-v2[1]; \
dest[2]=v1[2]-v2[2];
// Cull backfacing polygons?
#define OPT_CULL_RAY_TRIANGLE
static int RayTriangleIntersect(const dfloat orig[3],const dfloat dir[3],
static int RayTriangleIntersect(const dfloat orig[3],const dfloat dir[3], const dfloat vert0[3],const dfloat vert1[3],const dfloat vert2[3],
dfloat *t,dfloat *u,dfloat *v)
// From Real-Time Rendering, page 305
// Returns 0 if not hit is found
// If a hit is found, t contains the distance along the ray (dir)
// and u/v contain u/v coordinates into the triangle (like texture
// coordinates).
{
dfloat edge1[3], edge2[3], tvec[3], pvec[3], qvec[3];
dfloat det,inv_det;
/* find vectors for two edges sharing vert0 */
SUB(edge1, vert1, vert0);
SUB(edge2, vert2, vert0);
/* begin calculating determinant - also used to calculate U parameter */
CROSS(pvec, dir, edge2);
/* if determinant is near zero, ray lies in plane of triangle */
det = DOT(edge1, pvec);
#ifdef OPT_CULL_RAY_TRIANGLE
// Culling section; triangle will be culled if not facing the right
// way.
if(det<D3_EPSILON)
return 0;
/* calculate distance from vert0 to ray origin */
SUB(tvec, orig, vert0);
/* calculate U parameter and test bounds */
*u = DOT(tvec, pvec);
if (*u < 0.0 || *u > det)
return 0;
/* prepare to test V parameter */
CROSS(qvec, tvec, edge1);
/* calculate V parameter and test bounds */
*v = DOT(dir, qvec);
if (*v < 0.0 || *u + *v > det)
return 0;
/* calculate t, scale parameters, ray intersects triangle */
*t = DOT(edge2, qvec);
inv_det = 1.0 / det;
*t *= inv_det;
*u *= inv_det;
*v *= inv_det;
#else
// The non-culling branch
if(det>-D3_EPSILON&&det<D3_EPSILON)
return 0;
inv_det = 1.0 / det;
/* calculate distance from vert0 to ray origin */
SUB(tvec, orig, vert0);
/* calculate U parameter and test bounds */
*u = DOT(tvec, pvec) * inv_det;
if (*u < 0.0 || *u > 1.0)
return 0;
/* prepare to test V parameter */
CROSS(qvec, tvec, edge1);
/* calculate V parameter and test bounds */
*v = DOT(dir, qvec) * inv_det;
if (*v < 0.0 || *u + *v > 1.0)
return 0;
/* calculate t, ray intersects triangle */
*t = DOT(edge2, qvec) * inv_det;
#endif
// We've got an intersection!
return 1;
}
/********************************
* Finding a triangle in a geode *
********************************/
static bool FindGeodeTriangle(DVector3 *origin,DVector3 *dir,DGeode *geode,
static bool FindGeodeTriangle(DVector3 *origin,DVector3 *dir,DGeode *geode, RSurfaceInfo *si,DVector3 *inter)
// The ray is traced and tested if it hits any triangle in 'geode'.
// Returns index of triangle.
// If a triangle is hit, the info is saved as cache info in 'si'.
{
int g,tri,n,nTris;
dfloat *pVertex;
dindex *pIndex;
DGeob *geob;
dfloat t,u,v;
dfloat minT;
// Find the surface CLOSEST to the origin
minT=9999.0f;
for(g=0;g<geode->geobs;g++)
{
geob=geode->geob[g];
pVertex=geob->vertex;
if(!pVertex)continue;
pIndex=geob->index;
//for(tri=geob->indices/3;tri>0;tri--,pIndex+=3)
nTris=geob->indices/3;
for(tri=0;tri<nTris;tri++,pIndex+=3)
{
if(RayTriangleIntersect(&origin->x,&dir->x,
&pVertex[(pIndex[0])*3],
&pVertex[(pIndex[1])*3],
&pVertex[(pIndex[2])*3],
&t,&u,&v))
{
//qdbg("Intersect TRI! t=%.2f, u=%.2f, v=%.2f\n",t,u,v);
// Don't count this triangle if it's further away than
// a previously found triangle, or the triangle is found
// in the other direction (above the wheel for example; a bridge
// possibly).
if(t<0||t>minT)continue;
// Calculate intersection point
// 2 ways are possible:
// - follow the ray: origin+t*direction
// - interpolate vertices; u and v are barycentric coordinates
// which we can average and thus get the weighted average
// of the 3 vertices that make up the triangle
inter->x=origin->x+t*dir->x;
inter->y=origin->y+t*dir->y;
inter->z=origin->z+t*dir->z;
// Store cache information (for next time)
si->lastTri=tri;
si->lastGeob=g;
// Caller must store si->lastNode (!) for cache info to be complete
return TRUE;
}
}
}
return FALSE;
}
/**********
* Caching *
**********/
static bool CacheCheckTriangle(const DVector3 *origin,const DVector3 *dir,
static bool CacheCheckTriangle(const DVector3 *origin,const DVector3 *dir, const DGeode *geode,const RSurfaceInfo *si,DVector3 *inter)
// Check if the cached triangle still hits
// If so, it returs TRUE and the intersection in 'inter'
{
int g,tri,n;
dfloat *pVertex;
dindex *pIndex;
DGeob *geob;
dfloat t,u,v;
// Cache info available?
if(si->lastNode==-1)return FALSE;
// Get back to the right triangle
geob=geode->geob[si->lastGeob];
pVertex=geob->vertex;
if(!pVertex)return FALSE;
pIndex=&geob->index[si->lastTri*3];
if(RayTriangleIntersect(&origin->x,&dir->x,
&pVertex[pIndex[0]*3],
&pVertex[pIndex[1]*3],
&pVertex[pIndex[2]*3],
&t,&u,&v))
{
//qdbg("Cached tri still hits\n");
// Calculate intersection point
inter->x=origin->x+t*dir->x;
inter->y=origin->y+t*dir->y;
inter->z=origin->z+t*dir->z;
return TRUE;
}
//qdbg("Cache miss!\n");
return FALSE;
}
/************
* Pick test *
************/
void RTrackVRML::GetSurfaceInfo(DVector3 *pos,DVector3 *dir,
void RTrackVRML::GetSurfaceInfo(DVector3 *pos,DVector3 *dir, RSurfaceInfo *si)
// Determines what the surface looks like under 'pos'
// 'dir' is the direction of the ray to trace (often like (0,-1,0))
// Currently returns valid (si->): x,y,z
{
DVector3 vWin;
DVector3 vOrigin,vDir,vPrj,vIntersect;
int i,n;
DCSLNode *node;
//qdbg("RTrackVRML:GetSurfaceInfo()\n");
// Default if no hit
si->x=si->y=si->z=0;
// Build ray definition
vOrigin=*pos;
vDir=*dir;
//vDir.Normalize();
// See if it hits a sphere in the track
QUICK_BUILD_CULLER;
// Check cached triangle first
if(si->lastNode>=0)
{
if(CacheCheckTriangle(&vOrigin,&vDir,
GetCuller()->GetNode(si->lastNode)->geode,si,&vIntersect))
{
// We're done; no change!
si->x=vIntersect.x;
si->y=vIntersect.y;
si->z=vIntersect.z;
return;
}
}
// Search all geodes, all geobs in every geode, and every
// triangle in each geob
n=GetCuller()->GetNodes();
for(i=0;i<n;i++)
{
node=GetCuller()->GetNode(i);
#ifdef OBS
DVector3 v;
Project(&node->center,&v);
qdbg("Projected node center: %f,%f,%f\n",v.x,v.y,v.z);
#endif
#ifdef OBS
v=node->center;
qdbg("Sphere center: %.2f,%.2f,%.2f, r=%.f\n",v.x,v.y,v.z,node->radius);
#endif
if(RaySphereIntersect(&vOrigin,&vDir,&node->center,node->radius,0))
{
if(FindGeodeTriangle(&vOrigin,&vDir,node->geode,si,&vIntersect))
{
// vIntersect contains 3D coordinate of triangle intersection
//*pt=vIntersect;
// Pass on point of intersection for surface
si->x=vIntersect.x;
si->y=vIntersect.y;
si->z=vIntersect.z;
#ifdef OBS
qdbg("intersection point=(%.2f,%.2f,%.2f) node %d, geob %d, tri %d\n",
vIntersect.x,vIntersect.y,vIntersect.z,i,si->lastGeob,si->lastTri);
vOrigin.DbgPrint("origin");
vDir.DbgPrint("dir");
#endif
// Store cache info that wasn't already stored
// by FindGeodeTriangle()
si->lastNode=i;
#ifdef OBS
// Don't seek further
break;
#endif
}
}
}
//qdbg("--\n");
}
}