1
0
Fork 0

Incorporated Norman's optimized line/geometry intersection code.

This commit is contained in:
curt 2002-03-17 00:38:24 +00:00
parent 3bf7269174
commit 8138c82b58
4 changed files with 668 additions and 334 deletions

View file

@ -1,3 +1,7 @@
// hitlist.cxx -
// Height Over Terrain and Assosciated Routines for FlightGear based Scenery
// Written by Norman Vine, started 2000.
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
@ -26,115 +30,8 @@
#include "hitlist.hxx"
extern ssgBranch *terrain_branch;
#if 0
// check to see if the intersection point is
// actually inside this face
static bool pointInTriangle( sgdVec3 point, sgdVec3 tri[3] )
{
double xmin, xmax, ymin, ymax, zmin, zmax;
// punt if outside bouding cube
if ( point[0] < (xmin = SG_MIN3 (tri[0][0], tri[1][0], tri[2][0])) ) {
return false;
} else if ( point[0] > (xmax = SG_MAX3 (tri[0][0], tri[1][0], tri[2][0])) )
{
return false;
} else if ( point[1] < (ymin = SG_MIN3 (tri[0][1], tri[1][1], tri[2][1])) )
{
return false;
} else if ( point[1] > (ymax = SG_MAX3 (tri[0][1], tri[1][1], tri[2][1])) )
{
return false;
} else if ( point[2] < (zmin = SG_MIN3 (tri[0][2], tri[1][2], tri[2][2])) )
{
return false;
} else if ( point[2] > (zmax = SG_MAX3 (tri[0][2], tri[1][2], tri[2][2])) )
{
return false;
}
// (finally) check to see if the intersection point is
// actually inside this face
//first, drop the smallest dimension so we only have to work
//in 2d.
double dx = xmax - xmin;
double dy = ymax - ymin;
double dz = zmax - zmin;
double min_dim = SG_MIN3 (dx, dy, dz);
//first, drop the smallest dimension so we only have to work
//in 2d.
double x1, y1, x2, y2, x3, y3, rx, ry;
if ( fabs(min_dim-dx) <= SG_EPSILON ) {
// x is the smallest dimension
x1 = point[1];
y1 = point[2];
x2 = tri[0][1];
y2 = tri[0][2];
x3 = tri[1][1];
y3 = tri[1][2];
rx = tri[2][1];
ry = tri[2][2];
} else if ( fabs(min_dim-dy) <= SG_EPSILON ) {
// y is the smallest dimension
x1 = point[0];
y1 = point[2];
x2 = tri[0][0];
y2 = tri[0][2];
x3 = tri[1][0];
y3 = tri[1][2];
rx = tri[2][0];
ry = tri[2][2];
} else if ( fabs(min_dim-dz) <= SG_EPSILON ) {
// z is the smallest dimension
x1 = point[0];
y1 = point[1];
x2 = tri[0][0];
y2 = tri[0][1];
x3 = tri[1][0];
y3 = tri[1][1];
rx = tri[2][0];
ry = tri[2][1];
} else {
// all dimensions are really small so lets call it close
// enough and return a successful match
return true;
}
// check if intersection point is on the same side of p1 <-> p2 as p3
double tmp = (y2 - y3) / (x2 - x3);
int side1 = SG_SIGN (tmp * (rx - x3) + y3 - ry);
int side2 = SG_SIGN (tmp * (x1 - x3) + y3 - y1);
if ( side1 != side2 ) {
// printf("failed side 1 check\n");
return false;
}
// check if intersection point is on correct side of p2 <-> p3 as p1
tmp = (y3 - ry) / (x3 - rx);
side1 = SG_SIGN (tmp * (x2 - rx) + ry - y2);
side2 = SG_SIGN (tmp * (x1 - rx) + ry - y1);
if ( side1 != side2 ) {
// printf("failed side 2 check\n");
return false;
}
// check if intersection point is on correct side of p1 <-> p3 as p2
tmp = (y2 - ry) / (x2 - rx);
side1 = SG_SIGN (tmp * (x3 - rx) + ry - y3);
side2 = SG_SIGN (tmp * (x1 - rx) + ry - y1);
if ( side1 != side2 ) {
// printf("failed side 3 check\n");
return false;
}
return true;
}
static int sgdIsectInfLinePlane( sgdVec3 dst, const sgdVec3 l_org,
const sgdVec3 l_vec, const sgdVec4 plane )
{
@ -153,60 +50,11 @@ static int sgdIsectInfLinePlane( sgdVec3 dst, const sgdVec3 l_org,
}
static void sgdXformPnt3 ( sgdVec3 dst, const sgVec3 src, const sgdMat4 mat )
{
SGDfloat t0 = src[ 0 ] ;
SGDfloat t1 = src[ 1 ] ;
SGDfloat t2 = src[ 2 ] ;
dst[0] = ( t0 * mat[ 0 ][ 0 ] +
t1 * mat[ 1 ][ 0 ] +
t2 * mat[ 2 ][ 0 ] +
mat[ 3 ][ 0 ] ) ;
dst[1] = ( t0 * mat[ 0 ][ 1 ] +
t1 * mat[ 1 ][ 1 ] +
t2 * mat[ 2 ][ 1 ] +
mat[ 3 ][ 1 ] ) ;
dst[2] = ( t0 * mat[ 0 ][ 2 ] +
t1 * mat[ 1 ][ 2 ] +
t2 * mat[ 2 ][ 2 ] +
mat[ 3 ][ 2 ] ) ;
}
#endif // 0
/*
Find the intersection of an infinite line with a plane
(the line being defined by a point and direction).
Norman Vine -- nhv@yahoo.com (with hacks by Steve)
*/
int sgdIsectInfLinePlane( sgdVec3 dst, sgdVec3 l_org,
sgdVec3 l_vec, sgdVec4 plane )
{
SGDfloat tmp = sgdScalarProductVec3 ( l_vec, plane ) ;
/* Is line parallel to plane? */
if ( sgdAbs ( tmp ) < DBL_EPSILON )
return FALSE ;
sgdScaleVec3 ( dst, l_vec, -( sgdScalarProductVec3 ( l_org, plane )
+ plane[3] ) / tmp ) ;
sgdAddVec3 ( dst, l_org ) ;
return TRUE ;
}
/*
* Given a point and a triangle lying on the same plane
* check to see if the point is inside the triangle
*/
bool sgdPointInTriangle( sgdVec3 point, sgdVec3 tri[3] )
static bool sgdPointInTriangle( sgdVec3 point, sgdVec3 tri[3] )
{
sgdVec3 dif;
@ -297,6 +145,56 @@ bool sgdPointInTriangle( sgdVec3 point, sgdVec3 tri[3] )
}
inline static int isZeroAreaTri( sgdVec3 tri[3] )
{
return( sgdEqualVec3(tri[0], tri[1]) ||
sgdEqualVec3(tri[1], tri[2]) ||
sgdEqualVec3(tri[2], tri[0]) );
}
/*
* Given a point and a triangle lying on the same plane
* check to see if the point is inside the triangle
*/
#if 0
static bool PointInTri( sgdVec3 P, sgdVec3 V[3] )
{
sgdVec3 X,W1,W2;
sgdSubVec3(X,P,V[0]);
sgdSubVec3(W1,V[1],V[2]);
sgdSubVec3(W2,V[2],V[0]);
sgdVec3 C;
sgdVectorProductVec3(C, W1, W2);
double d = sgdScalarProductVec3(X,C);
// Test not needed if you KNOW point is on plane of triangle
// and triangle is not degenerate
if( d > -FLT_EPSILON && d < FLT_EPSILON )
return false;
double u11 = sgdScalarProductVec3(W1,W1);
double u22 = sgdScalarProductVec3(W2,W2);
double u12 = sgdScalarProductVec3(W1,W2);
double y1 = sgdScalarProductVec3(X,W1);
double y2 = sgdScalarProductVec3(X,W2);
double z1 = (y1*u22 - y2*u12)/d;
if( z1>0 || z1>1 )
return false;
double z2 = (y2*u11 - y1*u12)/d;
if( z2>0 || z2>1 )
return false;
return true;
}
#endif
/*
Find the intersection of an infinite line with a leaf
the line being defined by a point and direction.
@ -314,12 +212,21 @@ bool sgdPointInTriangle( sgdVec3 point, sgdVec3 tri[3] )
Returns:
true if intersection found
false otherwise
!!! WARNING !!!
If you need an exhaustive list of hitpoints YOU MUST use
the generic version of this function as the specialized
versions will do an early out of expensive tests if the point
can not be the closest one found
!!! WARNING !!!
*/
int FGHitList::IntersectLeaf( ssgLeaf *leaf, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir )
{
int num_hits = 0;
for ( int i = 0; i < leaf->getNumTriangles(); ++i ) {
int i = 0;
for ( ; i < leaf->getNumTriangles(); ++i ) {
short i1, i2, i3;
leaf->getTriangle( i, &i1, &i2, &i3 );
@ -328,37 +235,15 @@ int FGHitList::IntersectLeaf( ssgLeaf *leaf, sgdMat4 m,
sgdSetVec3( tri[1], leaf->getVertex( i2 ) );
sgdSetVec3( tri[2], leaf->getVertex( i3 ) );
//avoid division by zero when two points are the same
if ( sgdEqualVec3(tri[0], tri[1]) ||
sgdEqualVec3(tri[1], tri[2]) ||
sgdEqualVec3(tri[2], tri[0]) ) {
// avoid division by zero when two points are the same
if( isZeroAreaTri( tri ) )
continue;
}
sgdVec4 plane;
sgdMakePlane( plane, tri[0], tri[1], tri[2] );
sgdVec3 point;
//inlined IsectInfLinePlane( point dst, orig, dir, plane )
SGDfloat tmp = sgdScalarProductVec3 ( dir, plane ) ;
/* Is line parallel to plane? */
if ( sgdAbs ( tmp ) < DBL_EPSILON )
continue ;
sgdScaleVec3 ( point, dir,
-( sgdScalarProductVec3 ( orig, plane ) + plane[3] )
/ tmp ) ;
sgdAddVec3 ( point, orig ) ;
// end of inlined intersection
#if 0
if( pointInTriangle( point, tri ) ) {
add(leaf,i,point,plane);
num_hits++;
}
#endif // 0
if( sgdIsectInfLinePlane( point, orig, dir, plane ) ) {
if( sgdPointInTriangle( point, tri ) ) {
// transform point into passed into desired coordinate frame
sgdXformPnt3( point, point, m );
@ -366,20 +251,305 @@ int FGHitList::IntersectLeaf( ssgLeaf *leaf, sgdMat4 m,
num_hits++;
}
}
}
return num_hits;
}
//=================
int FGHitList::IntersectPolyOrFanLeaf( ssgLeaf *leaf, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir )
{
double tmp_dist;
// number of hits but there could be more that
// were not found because of short circut switch !
// so you may want to use the unspecialized IntersectLeaf()
int num_hits = 0;
int ntri = leaf->getNumTriangles();
for ( int n = 0; n < ntri; ++n ) {
sgdVec3 tri[3];
if ( !n ) {
sgdSetVec3( tri[0], leaf->getVertex( short(0) ) );
sgdSetVec3( tri[1], leaf->getVertex( short(1) ) );
sgdSetVec3( tri[2], leaf->getVertex( short(2) ) );
} else {
sgdCopyVec3( tri[1], tri[2] );
sgdSetVec3( tri[2], leaf->getVertex( short(n+2) ) );
}
if( isZeroAreaTri( tri ) )
continue;
sgdVec4 plane;
sgdMakePlane( plane, tri[0], tri[1], tri[2] );
sgdVec3 point;
//inlined IsectInfLinePlane( point dst, orig, dir, plane )
{
SGDfloat tmp = sgdScalarProductVec3 ( dir, plane ) ;
/* Is line parallel to plane? */
if ( sgdAbs ( tmp ) < FLT_EPSILON /*DBL_EPSILON*/ )
continue ;
sgdScaleVec3 ( point, dir,
-( sgdScalarProductVec3 ( orig, plane ) + plane[3] )
/ tmp ) ;
sgdAddVec3 ( point, orig ) ;
} // end of inlined intersection routine
// short circut if this point is further away then a previous hit
tmp_dist = sgdScalarProductVec3(point,point);
if( tmp_dist > test_dist )
continue;
if( sgdPointInTriangle( point, tri ) ) {
// transform point into passed coordinate frame
sgdXformPnt3( point, point, m );
add(leaf,n,point,plane);
test_dist = tmp_dist;
num_hits++;
}
}
return num_hits;
}
//===============
int FGHitList::IntersectTriLeaf( ssgLeaf *leaf, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir )
{
double tmp_dist;
// number of hits but there could be more that
// were not found because of short circut switch !
// so you may want to use the unspecialized IntersectLeaf()
int num_hits = 0;
int ntri = leaf->getNumTriangles();
for ( int n = 0; n < ntri; ++n ) {
sgdVec3 tri[3];
sgdSetVec3( tri[0], leaf->getVertex( short(n*3) ) );
sgdSetVec3( tri[1], leaf->getVertex( short(n*3+1) ) );
sgdSetVec3( tri[2], leaf->getVertex( short(n*3+2) ) );
// avoid division by zero when two points are the same
if( isZeroAreaTri( tri ) )
continue;
sgdVec4 plane;
sgdMakePlane( plane, tri[0], tri[1], tri[2] );
sgdVec3 point;
//inlined IsectInfLinePlane( point dst, orig, dir, plane )
{
SGDfloat tmp = sgdScalarProductVec3 ( dir, plane ) ;
/* Is line parallel to plane? */
if ( sgdAbs ( tmp ) < FLT_EPSILON /*DBL_EPSILON*/ )
continue ;
sgdScaleVec3 ( point, dir,
-( sgdScalarProductVec3 ( orig, plane ) + plane[3] )
/ tmp ) ;
sgdAddVec3 ( point, orig ) ;
} // end of inlined intersection routine
// short circut if this point is further away then a previous hit
tmp_dist = sgdScalarProductVec3(point,point);
if( tmp_dist > test_dist )
continue;
if( sgdPointInTriangle( point, tri ) ) {
// transform point into passed coordinate frame
sgdXformPnt3( point, point, m );
add(leaf,n,point,plane);
test_dist = tmp_dist;
num_hits++;
}
}
return num_hits;
}
//============================
int FGHitList::IntersectStripLeaf( ssgLeaf *leaf, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir )
{
double tmp_dist;
// number of hits but there could be more that
// were not found because of short circut switch !
// so you may want to use the unspecialized IntersectLeaf()
int num_hits = 0;
int ntri = leaf->getNumTriangles();
for ( int n = 0; n < ntri; ++n ) {
sgdVec3 tri[3];
if ( !n ) {
sgdSetVec3( tri[0], leaf->getVertex( short(0) ) );
sgdSetVec3( tri[1], leaf->getVertex( short(1) ) );
sgdSetVec3( tri[2], leaf->getVertex( short(2) ) );
} else {
if ( n & 1 ) {
sgdSetVec3( tri[0], leaf->getVertex( short(n+2) ) );
sgdCopyVec3( tri[1], tri[2] );
sgdCopyVec3( tri[2], tri[1] );
} else {
sgdCopyVec3( tri[0], tri[1] );
sgdCopyVec3( tri[1], tri[2] );
sgdSetVec3( tri[2], leaf->getVertex( short(n+2) ) );
}
}
if( isZeroAreaTri( tri ) )
continue;
sgdVec4 plane;
sgdMakePlane( plane, tri[0], tri[1], tri[2] );
sgdVec3 point;
//inlined IsectInfLinePlane( point dst, orig, dir, plane )
{
SGDfloat tmp = sgdScalarProductVec3 ( dir, plane ) ;
/* Is line parallel to plane? */
if ( sgdAbs ( tmp ) < FLT_EPSILON /*DBL_EPSILON*/ )
continue ;
sgdScaleVec3 ( point, dir,
-( sgdScalarProductVec3 ( orig, plane ) + plane[3] )
/ tmp ) ;
sgdAddVec3 ( point, orig ) ;
} // end of inlined intersection routine
// short circut if this point is further away then a previous hit
tmp_dist = sgdScalarProductVec3(point,point);
if( tmp_dist > test_dist )
continue;
if( sgdPointInTriangle( point, tri ) ) {
// transform point into passed coordinate frame
sgdXformPnt3( point, point, m );
add(leaf,n,point,plane);
test_dist = tmp_dist;
num_hits++;
}
}
return num_hits;
}
// ======================
int FGHitList::IntersectQuadsLeaf( ssgLeaf *leaf, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir )
{
double tmp_dist;
// number of hits but there could be more that
// were not found because of short circut switch !
// so you may want to use the unspecialized IntersectLeaf()
int num_hits = 0;
int ntri = leaf->getNumTriangles();
for ( int n = 0; n < ntri; ++n ) {
sgdVec3 tri[3];
sgdSetVec3( tri[0], leaf->getVertex( short(n*2) ) );
sgdSetVec3( tri[1], leaf->getVertex( short(n*2+1) ) );
sgdSetVec3( tri[2], leaf->getVertex( short(n*2 + 2 - (n&1)*4) ) );
if( isZeroAreaTri( tri ) )
continue;
sgdVec4 plane;
sgdMakePlane( plane, tri[0], tri[1], tri[2] );
sgdVec3 point;
//inlined IsectInfLinePlane( point dst, orig, dir, plane )
{
SGDfloat tmp = sgdScalarProductVec3 ( dir, plane ) ;
/* Is line parallel to plane? */
if ( sgdAbs ( tmp ) < FLT_EPSILON /*DBL_EPSILON*/ )
continue ;
sgdScaleVec3 ( point, dir,
-( sgdScalarProductVec3 ( orig, plane ) + plane[3] )
/ tmp ) ;
sgdAddVec3 ( point, orig ) ;
} // end of inlined intersection routine
// short circut if this point is further away then a previous hit
tmp_dist = sgdScalarProductVec3(point,point);
if( tmp_dist > test_dist )
continue;
if( sgdPointInTriangle( point, tri ) ) {
// transform point into passed coordinate frame
sgdXformPnt3( point, point, m );
add(leaf,n,point,plane);
test_dist = tmp_dist;
num_hits++;
}
}
return num_hits;
}
// Need these because of mixed matrix types
static void sgMultMat4(sgdMat4 dst, sgdMat4 m1, sgMat4 m2)
{
for ( int j = 0 ; j < 4 ; j++ ) {
dst[0][j] = m2[0][0] * m1[0][j] +
m2[0][1] * m1[1][j] +
m2[0][2] * m1[2][j] +
m2[0][3] * m1[3][j] ;
dst[1][j] = m2[1][0] * m1[0][j] +
m2[1][1] * m1[1][j] +
m2[1][2] * m1[2][j] +
m2[1][3] * m1[3][j] ;
dst[2][j] = m2[2][0] * m1[0][j] +
m2[2][1] * m1[1][j] +
m2[2][2] * m1[2][j] +
m2[2][3] * m1[3][j] ;
dst[3][j] = m2[3][0] * m1[0][j] +
m2[3][1] * m1[1][j] +
m2[3][2] * m1[2][j] +
m2[3][3] * m1[3][j] ;
}
}
/*
*
*/
void FGHitList::IntersectBranch( ssgBranch *branch, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir )
{
sgSphere *bsphere;
/* the lookat vector and matrix in branch's coordinate frame
* but we won't determine these unless needed to,
* but we won't determine these unless needed,
* This 'lazy evaluation' is a result of profiling data */
sgdVec3 _orig, _dir;
sgdMat4 _m;
sgdVec3 orig_leaf, dir_leaf;
sgdMat4 m_leaf;
// 'lazy evaluation' flag
int first_time = 1;
@ -389,12 +559,11 @@ void FGHitList::IntersectBranch( ssgBranch *branch, sgdMat4 m,
kid = branch->getNextKid() )
{
if ( kid->getTraversalMask() & SSGTRAV_HOT ) {
bsphere = kid->getBSphere();
sgdVec3 center;
sgdSetVec3( center,
bsphere->getCenter()[0],
bsphere->getCenter()[1],
bsphere->getCenter()[2] );
kid->getBSphere()->getCenter()[0],
kid->getBSphere()->getCenter()[1],
kid->getBSphere()->getCenter()[2] );
sgdXformPnt3( center, m ) ;
// sgdClosestPointToLineDistSquared( center, orig, dir )
@ -408,29 +577,54 @@ void FGHitList::IntersectBranch( ssgBranch *branch, sgdMat4 m,
// doubles because of possible overflow
#define SQUARE(x) (x*x)
if( sgdScalarProductVec3(v, v)
< SQUARE( double(bsphere->getRadius()) ) )
< SQUARE( double(kid->getBSphere()->getRadius()) ) )
{
// possible intersections
if ( kid->isAKindOf ( ssgTypeBranch() ) ) {
sgdMat4 m_new;
sgdCopyMat4(m_new, m);
if ( kid->isA( ssgTypeTransform() ) ) {
if ( kid->isA( ssgTypeTransform() ) )
{
sgMat4 fxform;
((ssgTransform *)kid)->getTransform( fxform );
sgdMat4 xform;
sgdSetMat4( xform, fxform );
sgdPreMultMat4( m_new, xform );
sgMultMat4(m_new, m, fxform);
} else {
sgdCopyMat4(m_new, m);
}
IntersectBranch( (ssgBranch *)kid, m_new, orig, dir );
} else if ( kid->isAKindOf( ssgTypeLeaf() ) ) {
if( first_time) {
if ( first_time ) {
// OK we need these
sgdTransposeNegateMat4( _m, m);
sgdXformPnt3( _orig, orig, _m );
sgdXformPnt3( _dir, dir, _m );
sgdTransposeNegateMat4( m_leaf, m);
sgdXformPnt3( orig_leaf, orig, m_leaf );
sgdXformPnt3( dir_leaf, dir, m_leaf );
first_time = 0;
}
IntersectLeaf( (ssgLeaf *)kid, m, _orig, _dir );
GLenum primType = ((ssgLeaf *)kid)->getPrimitiveType();
switch ( primType ) {
case GL_POLYGON :
case GL_TRIANGLE_FAN :
IntersectPolyOrFanLeaf( (ssgLeaf *)kid, m,
orig_leaf, dir_leaf );
break;
case GL_TRIANGLES :
IntersectTriLeaf( (ssgLeaf *)kid, m,
orig_leaf, dir_leaf );
break;
case GL_TRIANGLE_STRIP :
case GL_QUAD_STRIP :
IntersectStripLeaf( (ssgLeaf *)kid, m,
orig_leaf, dir_leaf );
break;
case GL_QUADS :
IntersectQuadsLeaf( (ssgLeaf *)kid, m,
orig_leaf, dir_leaf );
break;
default:
IntersectLeaf( (ssgLeaf *)kid, m,
orig_leaf, dir_leaf );
}
}
} else {
// end of the line for this branch
@ -441,13 +635,47 @@ void FGHitList::IntersectBranch( ssgBranch *branch, sgdMat4 m,
}
}
void ssgGetEntityTransform(ssgEntity *entity, sgMat4 m )
{
/*
Walk backwards up the tree, transforming the
vertex by all the matrices along the way.
// This expects the inital m to the identity transform
Upwards recursion hurts my head.
*/
sgMat4 mat ;
/*
If this node has a parent - get the composite
matrix for the parent.
*/
if ( entity->getNumParents() > 0 )
ssgGetEntityTransform ( entity->getParent(0), mat ) ;
else
sgMakeIdentMat4 ( mat ) ;
/*
If this node has a transform - then concatenate it.
*/
if ( entity -> isAKindOf ( ssgTypeTransform () ) ) {
sgMat4 this_mat ;
((ssgTransform *) entity) -> getTransform ( this_mat ) ;
sgPostMultMat4 ( mat, this_mat ) ;
}
sgCopyMat4 ( m, mat ) ;
}
#if 0
// previously used method
// This expects the inital m to be the identity transform
void ssgGetEntityTransform(ssgEntity *branch, sgMat4 m )
{
for ( ssgEntity *parent = branch->getParent(0);
parent != NULL;
parent = parent->getNextParent() )
parent = parent->getParent(0) )
{
// recurse till we are at the scene root
// then just unwinding the stack should
@ -460,7 +688,7 @@ void ssgGetEntityTransform(ssgEntity *branch, sgMat4 m )
}
}
}
#endif // 0
// return the passed entitity's bsphere's center point radius and
// fully formed current model matrix for entity
@ -475,53 +703,11 @@ void ssgGetCurrentBSphere( ssgEntity *entity, sgVec3 center,
}
void FGHitList::IntersectCachedLeaf( sgdMat4 m,
sgdVec3 orig, sgdVec3 dir)
{
if ( last_hit() ) {
float radius;
sgVec3 fcenter;
sgMat4 fxform;
// ssgEntity *ent = last_hit();
ssgGetCurrentBSphere( last_hit(), fcenter, &radius, fxform );
sgdMat4 m;
sgdVec3 center;
sgdSetMat4( m, fxform );
sgdXformPnt3( center, m );
if ( sgdClosestPointToLineDistSquared( center, orig, dir ) <
double(radius*radius) )
{
IntersectLeaf( (ssgLeaf *)last_hit(), m, orig, dir );
}
}
}
void FGHitList::Intersect( ssgBranch *scene, sgdVec3 orig, sgdVec3 dir ) {
sgdMat4 m;
// #define USE_CACHED_HIT
#ifdef USE_CACHED_HIT
// This optimization gives a slight speedup
// but it precludes using the hitlist for dynamic
// objects NHV
init();
if( last_hit() ) {
sgdMakeIdentMat4 ( m ) ;
IntersectCachedLeaf(m, orig, dir);
}
if( ! num_hits() ) {
#endif
clear();
sgdMakeIdentMat4 ( m ) ;
IntersectBranch( scene, m, orig, dir);
#ifdef USE_CACHED_HIT
}
#endif
}
@ -542,11 +728,20 @@ static inline Point3D operator + (const Point3D& a, const sgdVec3 b)
}
// Determine scenery altitude via ssg. Normally this just happens
// when we render the scene, but we'd also like to be able to do this
// explicitely. lat & lon are in radians. view_pos in current world
// coordinate translated near (0,0,0) (in meters.) Returns result in
// meters.
/*
* This method is faster
*/
void FGHitList::Intersect( ssgBranch *scene, sgdMat4 m, sgdVec3 orig,
sgdVec3 dir )
{
clear();
IntersectBranch( scene, m, orig, dir);
}
// Determine scenery altitude via ssg.
// returned results are in meters
bool fgCurrentElev( sgdVec3 abs_view_pos, sgdVec3 scenery_center,
FGHitList *hit_list,
double *terrain_elev, double *radius, double *normal)
@ -557,7 +752,9 @@ bool fgCurrentElev( sgdVec3 abs_view_pos, sgdVec3 scenery_center,
sgdVec3 orig, dir;
sgdCopyVec3(orig, view_pos );
sgdCopyVec3(dir, abs_view_pos );
// sgdNormalizeVec3(dir);
// !! why is terrain not globals->get_terrain()
hit_list->Intersect( terrain_branch, orig, dir );
int this_hit=0;
@ -565,17 +762,20 @@ bool fgCurrentElev( sgdVec3 abs_view_pos, sgdVec3 scenery_center,
double result = -9999;
Point3D sc(scenery_center[0], scenery_center[1], scenery_center[2]) ;
// cout << "hits = ";
int hitcount = hit_list->num_hits();
for ( int i = 0; i < hitcount; ++i ) {
geoc = sgCartToPolar3d( sc + hit_list->get_point(i) );
double lat_geod, alt, sea_level_r;
sgGeocToGeod(geoc.lat(), geoc.radius(), &lat_geod,
&alt, &sea_level_r);
// cout << alt << " ";
if ( alt > result && alt < 10000 ) {
result = alt;
this_hit = i;
}
}
// cout << endl;
if ( result > -9000 ) {
*terrain_elev = result;
@ -598,3 +798,71 @@ bool fgCurrentElev( sgdVec3 abs_view_pos, sgdVec3 scenery_center,
return false;
}
}
// Determine scenery altitude via ssg.
// returned results are in meters
bool fgCurrentElev( sgdVec3 abs_view_pos, sgdVec3 scenery_center,
ssgTransform *terra_transform,
FGHitList *hit_list,
double *terrain_elev, double *radius, double *normal)
{
#ifndef FAST_HITLIST__TEST
return fgCurrentElev( abs_view_pos, scenery_center, hit_list,
terrain_elev,radius,normal);
#else
sgdVec3 view_pos;
sgdSubVec3( view_pos, abs_view_pos, scenery_center );
sgdVec3 orig, dir;
sgdCopyVec3(orig, view_pos );
sgdCopyVec3(dir, abs_view_pos );
sgdNormalizeVec3(dir);
sgMat4 fxform;
sgMakeIdentMat4 ( fxform ) ;
ssgGetEntityTransform( terra_transform, fxform );
sgdMat4 xform;
sgdSetMat4(xform,fxform);
hit_list->Intersect( terra_transform, xform, orig, dir );
int this_hit=0;
Point3D geoc;
double result = -9999;
Point3D sc(scenery_center[0], scenery_center[1], scenery_center[2]) ;
int hitcount = hit_list->num_hits();
for ( int i = 0; i < hitcount; ++i ) {
geoc = sgCartToPolar3d( sc + hit_list->get_point(i) );
double lat_geod, alt, sea_level_r;
sgGeocToGeod(geoc.lat(), geoc.radius(), &lat_geod,
&alt, &sea_level_r);
if ( alt > result && alt < 20000 ) {
result = alt;
this_hit = i;
}
}
if ( result > -9000 ) {
*terrain_elev = result;
*radius = geoc.radius();
sgVec3 tmp;
sgSetVec3(tmp, hit_list->get_normal(this_hit));
// cout << "cur_normal: " << tmp[0] << " " << tmp[1] << " "
// << tmp[2] << endl;
/* ssgState *IntersectedLeafState =
((ssgLeaf*)hit_list->get_entity(this_hit))->getState(); */
CurrentNormalInLocalPlane(tmp, tmp);
sgdSetVec3( normal, tmp );
// cout << "NED: " << tmp[0] << " " << tmp[1] << " " << tmp[2] << endl;
return true;
} else {
SG_LOG( SG_TERRAIN, SG_DEBUG, "DOING FULL TERRAIN INTERSECTION" );
return fgCurrentElev( abs_view_pos, scenery_center, hit_list,
terrain_elev,radius,normal);
}
#endif // !FAST_HITLIST__TEST
}

View file

@ -1,3 +1,7 @@
// hitlist.hxx
// Height Over Terrain and Assosciated Routines for FlightGear based Scenery
// Written by Norman Vine, started 2000.
#ifndef _HITLIST_HXX
#define _HITLIST_HXX
@ -11,8 +15,9 @@
#include <plib/ssg.h>
SG_USING_STD(vector);
#define FAST_HITLIST__TEST 1
SG_USING_STD(vector);
class FGHitRec {
@ -44,11 +49,12 @@ private:
ssgEntity *last;
vector < FGHitRec > list;
double test_dist;
public:
FGHitList() { last = NULL; }
void init(void) { list.clear(); }
FGHitList() { last = NULL; test_dist=DBL_MAX; }
void init(void) { list.clear(); test_dist=DBL_MAX; }
void clear(void) { init(); last = NULL; }
void add( ssgEntity *ent, int idx, sgdVec3 point, sgdVec3 normal ) {
list.push_back( FGHitRec( ent,idx,point,normal) );
@ -63,24 +69,47 @@ public:
void Intersect( ssgBranch *branch,
sgdVec3 orig, sgdVec3 dir );
void Intersect( ssgBranch *scene, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir );
void IntersectBranch( ssgBranch *branch, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir);
void IntersectCachedLeaf( sgdMat4 m,
sgdVec3 orig, sgdVec3 dir);
void IntersectCachedLeaf( sgdVec3 orig, sgdVec3 dir);
int IntersectLeaf( ssgLeaf *leaf, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir );
int IntersectPolyOrFanLeaf( ssgLeaf *leaf, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir );
int IntersectTriLeaf( ssgLeaf *leaf, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir );
int IntersectStripLeaf( ssgLeaf *leaf, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir );
int IntersectQuadsLeaf( ssgLeaf *leaf, sgdMat4 m,
sgdVec3 orig, sgdVec3 dir );
};
// Associated function, assuming a wgs84 world with 0,0,0 at the
// center, find the current terrain intersection elevation for the
// point specified.
bool fgCurrentElev( sgdVec3 abs_view_pos, sgdVec3 scenery_center,
bool fgCurrentElev( sgdVec3 abs_view_pos,
sgdVec3 scenery_center,
ssgTransform *terra_transform,
FGHitList *hit_list,
double *terrain_elev, double *radius, double *normal );
double *terrain_elev,
double *radius,
double *normal );
bool fgCurrentElev( sgdVec3 abs_view_pos,
sgdVec3 scenery_center,
FGHitList *hit_list,
double *terrain_elev,
double *radius,
double *normal );
#endif // _HITLIST_HXX

View file

@ -266,6 +266,12 @@ public:
* graph for this tile.
*/
void disconnect_ssg_nodes();
/**
* return the SSG Transform node for the terrain
*/
inline ssgTransform *get_terra_transform() { return terra_transform; }
};

View file

@ -292,6 +292,7 @@ int FGTileMgr::update( double lon, double lat ) {
// happen in the render thread because model loading can trigger
// texture loading which involves use of the opengl api.
if ( !model_queue.empty() ) {
// cout << "loading next model ..." << endl;
// load the next tile in the queue
#ifdef ENABLE_THREADS
FGDeferredModel* dm = model_queue.pop();
@ -311,6 +312,8 @@ int FGTileMgr::update( double lon, double lat ) {
delete dm;
}
// cout << "current elevation (ssg) == " << scenery.get_cur_elev() << endl;
previous_bucket = current_bucket;
last_longitude = longitude;
last_latitude = latitude;
@ -334,6 +337,7 @@ int FGTileMgr::update( double lon, double lat ) {
e->add_ssg_nodes( terrain_branch,
gnd_lights_branch,
rwy_lights_branch );
// cout << "Adding ssg nodes for "
}
sgdVec3 sc;
@ -345,6 +349,7 @@ int FGTileMgr::update( double lon, double lat ) {
#if 0
if ( scenery.center == Point3D(0.0) ) {
// initializing
cout << "initializing scenery current elevation ... " << endl;
sgdVec3 tmp_abs_view_pos;
Point3D geod_pos = Point3D( longitude * SGD_DEGREES_TO_RADIANS,
@ -354,6 +359,7 @@ int FGTileMgr::update( double lon, double lat ) {
scenery.center = tmp;
sgdSetVec3( tmp_abs_view_pos, tmp.x(), tmp.y(), tmp.z() );
// cout << "abs_view_pos = " << tmp_abs_view_pos << endl;
prep_ssg_nodes();
double tmp_elev;
@ -364,8 +370,25 @@ int FGTileMgr::update( double lon, double lat ) {
} else {
scenery.set_cur_elev( 0.0 );
}
// cout << "result = " << scenery.get_cur_elev() << endl;
} else {
#endif
/*
cout << "abs view pos = "
<< globals->get_current_view()->get_abs_view_pos()[0] << ","
<< globals->get_current_view()->get_abs_view_pos()[1] << ","
<< globals->get_current_view()->get_abs_view_pos()[2]
<< " view pos = "
<< globals->get_current_view()->get_view_pos()[0] << ","
<< globals->get_current_view()->get_view_pos()[1] << ","
<< globals->get_current_view()->get_view_pos()[2]
<< endl;
cout << "current_tile = " << current_tile << endl;
cout << "Scenery center = " << sc[0] << "," << sc[1] << "," << sc[2]
<< endl;
*/
// overridden with actual values if a terrain intersection is
// found
double hit_elev = -9999.0;
@ -376,8 +399,13 @@ int FGTileMgr::update( double lon, double lat ) {
if ( fabs(sc[0]) > 1.0 || fabs(sc[1]) > 1.0 || fabs(sc[2]) > 1.0 ) {
// scenery center has been properly defined so any hit
// should be valid (and not just luck)
sgdSetVec3( sc,
scenery.get_center()[0],
scenery.get_center()[1],
scenery.get_center()[2] );
hit = fgCurrentElev(globals->get_current_view()->get_abs_view_pos(),
sc,
current_tile->get_terra_transform(),
&hit_list,
&hit_elev,
&hit_radius,
@ -393,6 +421,8 @@ int FGTileMgr::update( double lon, double lat ) {
scenery.set_cur_radius( 0.0 );
scenery.set_cur_normal( hit_normal );
}
// cout << "Current elevation = " << scenery.get_cur_elev() << endl;
#if 0
}
#endif
@ -413,6 +443,7 @@ void FGTileMgr::prep_ssg_nodes() {
tile_cache.reset_traversal();
while ( ! tile_cache.at_end() ) {
// cout << "processing a tile" << endl;
if ( (e = tile_cache.get_current()) ) {
e->prep_ssg_node( scenery.get_center(), vis);
} else {