2002-03-17 00:38:24 +00:00
|
|
|
// hitlist.cxx -
|
|
|
|
// Height Over Terrain and Assosciated Routines for FlightGear based Scenery
|
|
|
|
// Written by Norman Vine, started 2000.
|
|
|
|
|
2000-06-16 02:15:08 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include <config.h>
|
|
|
|
#endif
|
|
|
|
|
2000-09-06 00:11:01 +00:00
|
|
|
#include <float.h>
|
2000-06-16 02:15:08 +00:00
|
|
|
#include <math.h>
|
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
#include <plib/sg.h>
|
|
|
|
#include <plib/ssg.h>
|
|
|
|
|
2001-03-24 14:56:37 +00:00
|
|
|
#include <simgear/sg_inlines.h>
|
2001-07-11 15:51:21 +00:00
|
|
|
#include <simgear/debug/logstream.hxx>
|
|
|
|
#include <simgear/math/point3d.hxx>
|
|
|
|
#include <simgear/math/sg_geodesy.hxx>
|
2000-06-16 02:15:08 +00:00
|
|
|
#include <simgear/math/vector.hxx>
|
2003-11-22 12:03:10 +00:00
|
|
|
#include <simgear/timing/timestamp.hxx>
|
2000-06-16 02:15:08 +00:00
|
|
|
|
2001-07-11 15:51:21 +00:00
|
|
|
#include <Main/globals.hxx>
|
2001-07-22 19:51:16 +00:00
|
|
|
#include <Main/viewer.hxx>
|
2002-05-14 05:49:47 +00:00
|
|
|
#include <Scenery/scenery.hxx>
|
2001-07-11 15:51:21 +00:00
|
|
|
|
2000-06-16 02:15:08 +00:00
|
|
|
#include "hitlist.hxx"
|
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
// Specialized version of sgMultMat4 needed because of mixed matrix
|
|
|
|
// types
|
|
|
|
static inline 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] ;
|
2002-03-18 16:25:16 +00:00
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
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] ;
|
2002-03-18 16:25:16 +00:00
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
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] ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk backwards up the tree, transforming the vertex by all the
|
|
|
|
* matrices along the way.
|
|
|
|
*
|
|
|
|
* Upwards recursion hurts my head.
|
|
|
|
*/
|
|
|
|
static void ssgGetEntityTransform(ssgEntity *entity, sgMat4 m ) {
|
|
|
|
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 ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// return the passed entitity's bsphere's center point radius and
|
|
|
|
// fully formed current model matrix for entity
|
|
|
|
static inline void ssgGetCurrentBSphere( ssgEntity *entity, sgVec3 center,
|
|
|
|
float *radius, sgMat4 m )
|
|
|
|
{
|
|
|
|
sgSphere *bsphere = entity->getBSphere();
|
|
|
|
*radius = (double)bsphere->getRadius();
|
|
|
|
sgCopyVec3( center, bsphere->getCenter() );
|
|
|
|
sgMakeIdentMat4 ( m ) ;
|
|
|
|
ssgGetEntityTransform( entity, m );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This is same as PLib's sgdIsectInfLinePlane() and can be replaced
|
|
|
|
// by it after the next PLib release
|
|
|
|
static inline bool fgdIsectInfLinePlane( sgdVec3 dst,
|
|
|
|
const sgdVec3 l_org,
|
|
|
|
const sgdVec3 l_vec,
|
|
|
|
const sgdVec4 plane )
|
2000-06-16 02:15:08 +00:00
|
|
|
{
|
|
|
|
SGDfloat tmp = sgdScalarProductVec3 ( l_vec, plane ) ;
|
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
/* Is line parallel to plane? */
|
2000-06-16 02:15:08 +00:00
|
|
|
|
|
|
|
if ( fabs ( tmp ) < FLT_EPSILON )
|
|
|
|
return false ;
|
|
|
|
|
|
|
|
sgdScaleVec3 ( dst, l_vec, -( sgdScalarProductVec3 ( l_org, plane )
|
2002-03-18 16:25:16 +00:00
|
|
|
+ plane[3] ) / tmp ) ;
|
2000-06-16 02:15:08 +00:00
|
|
|
sgdAddVec3 ( dst, l_org ) ;
|
|
|
|
|
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
|
2002-02-05 20:54:08 +00:00
|
|
|
/*
|
2003-11-21 22:00:46 +00:00
|
|
|
* Given a point and a triangle lying on the same plane check to see
|
|
|
|
* if the point is inside the triangle
|
|
|
|
*
|
|
|
|
* This is same as PLib's sgdPointInTriangle() and can be replaced by
|
|
|
|
* it after the next PLib release
|
2002-02-05 20:54:08 +00:00
|
|
|
*/
|
2003-11-21 22:00:46 +00:00
|
|
|
static inline bool fgdPointInTriangle( sgdVec3 point, sgdVec3 tri[3] )
|
2002-02-05 20:54:08 +00:00
|
|
|
{
|
2002-02-28 23:59:28 +00:00
|
|
|
sgdVec3 dif;
|
|
|
|
|
|
|
|
SGDfloat min, max;
|
|
|
|
// punt if outside bouding cube
|
|
|
|
SG_MIN_MAX3 ( min, max, tri[0][0], tri[1][0], tri[2][0] );
|
|
|
|
if( (point[0] < min) || (point[0] > max) )
|
|
|
|
return false;
|
|
|
|
dif[0] = max - min;
|
|
|
|
|
|
|
|
SG_MIN_MAX3 ( min, max, tri[0][1], tri[1][1], tri[2][1] );
|
|
|
|
if( (point[1] < min) || (point[1] > max) )
|
|
|
|
return false;
|
|
|
|
dif[1] = max - min;
|
|
|
|
|
|
|
|
SG_MIN_MAX3 ( min, max, tri[0][2], tri[1][2], tri[2][2] );
|
|
|
|
if( (point[2] < min) || (point[2] > max) )
|
|
|
|
return false;
|
|
|
|
dif[2] = max - min;
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2002-02-28 23:59:28 +00:00
|
|
|
// drop the smallest dimension so we only have to work in 2d.
|
|
|
|
SGDfloat min_dim = SG_MIN3 (dif[0], dif[1], dif[2]);
|
|
|
|
SGDfloat x1, y1, x2, y2, x3, y3, rx, ry;
|
|
|
|
if ( fabs(min_dim-dif[0]) <= DBL_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-dif[1]) <= DBL_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-dif[2]) <= DBL_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;
|
|
|
|
}
|
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
// check if intersection point is on the same side of p1 <-> p2 as p3
|
2002-02-28 23:59:28 +00:00
|
|
|
SGDfloat 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;
|
2002-02-05 20:54:08 +00:00
|
|
|
}
|
|
|
|
|
2000-06-16 02:15:08 +00:00
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
// Check if all three vertices are the same point (or close enough)
|
|
|
|
static inline int isZeroAreaTri( sgdVec3 tri[3] )
|
2002-03-17 00:38:24 +00:00
|
|
|
{
|
|
|
|
return( sgdEqualVec3(tri[0], tri[1]) ||
|
2002-03-18 16:25:16 +00:00
|
|
|
sgdEqualVec3(tri[1], tri[2]) ||
|
|
|
|
sgdEqualVec3(tri[2], tri[0]) );
|
2002-03-17 00:38:24 +00:00
|
|
|
}
|
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
|
|
|
|
// Constructor
|
2002-03-18 16:25:16 +00:00
|
|
|
FGHitList::FGHitList() :
|
2003-11-21 22:00:46 +00:00
|
|
|
last(NULL), test_dist(DBL_MAX)
|
2002-03-17 00:38:24 +00:00
|
|
|
{
|
2002-03-18 16:25:16 +00:00
|
|
|
}
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
|
|
|
|
// Destructor
|
2002-03-18 16:25:16 +00:00
|
|
|
FGHitList::~FGHitList() {}
|
2002-03-17 00:38:24 +00:00
|
|
|
|
|
|
|
|
2000-06-16 02:15:08 +00:00
|
|
|
/*
|
2003-11-21 22:00:46 +00:00
|
|
|
Find the intersection of an infinite line with a leaf the line being
|
|
|
|
defined by a point and direction.
|
2002-03-18 16:25:16 +00:00
|
|
|
|
|
|
|
Variables
|
|
|
|
In:
|
|
|
|
ssgLeaf pointer -- leaf
|
|
|
|
qualified matrix -- m
|
|
|
|
line origin -- orig
|
|
|
|
line direction -- dir
|
|
|
|
Out:
|
|
|
|
result -- intersection point
|
|
|
|
normal -- intersected tri's normal
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
true if intersection found
|
|
|
|
false otherwise
|
|
|
|
|
|
|
|
!!! WARNING !!!
|
2003-11-21 22:00:46 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
!!! WARNING !!!
|
2000-06-16 02:15:08 +00:00
|
|
|
*/
|
|
|
|
int FGHitList::IntersectLeaf( ssgLeaf *leaf, sgdMat4 m,
|
2002-03-18 16:25:16 +00:00
|
|
|
sgdVec3 orig, sgdVec3 dir )
|
2000-06-16 02:15:08 +00:00
|
|
|
{
|
|
|
|
int num_hits = 0;
|
2002-03-17 00:38:24 +00:00
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
for ( ; i < leaf->getNumTriangles(); ++i ) {
|
2002-03-18 16:25:16 +00:00
|
|
|
short i1, i2, i3;
|
|
|
|
leaf->getTriangle( i, &i1, &i2, &i3 );
|
|
|
|
|
|
|
|
sgdVec3 tri[3];
|
|
|
|
sgdSetVec3( tri[0], leaf->getVertex( i1 ) );
|
|
|
|
sgdSetVec3( tri[1], leaf->getVertex( i2 ) );
|
|
|
|
sgdSetVec3( tri[2], leaf->getVertex( i3 ) );
|
|
|
|
|
|
|
|
if( isZeroAreaTri( tri ) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sgdVec4 plane;
|
|
|
|
sgdMakePlane( plane, tri[0], tri[1], tri[2] );
|
|
|
|
|
|
|
|
sgdVec3 point;
|
|
|
|
if( fgdIsectInfLinePlane( point, orig, dir, plane ) ) {
|
|
|
|
if( fgdPointInTriangle( point, tri ) ) {
|
|
|
|
// transform point into passed into desired coordinate frame
|
|
|
|
sgdXformPnt3( point, point, m );
|
2002-04-25 19:44:27 +00:00
|
|
|
sgdXformPnt4(plane,plane,m);
|
2002-03-18 16:25:16 +00:00
|
|
|
add(leaf,i,point,plane);
|
|
|
|
num_hits++;
|
|
|
|
}
|
|
|
|
}
|
2002-03-17 00:38:24 +00:00
|
|
|
}
|
|
|
|
return num_hits;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
// Short circuit/slightly optimized version of the full IntersectLeaf()
|
2002-03-18 16:25:16 +00:00
|
|
|
int FGHitList::IntersectLeaf( ssgLeaf *leaf, sgdMat4 m,
|
|
|
|
sgdVec3 orig, sgdVec3 dir,
|
|
|
|
GLenum primType )
|
2002-03-17 00:38:24 +00:00
|
|
|
{
|
|
|
|
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()
|
2002-03-18 16:25:16 +00:00
|
|
|
int n, num_hits = 0;
|
2002-03-17 00:38:24 +00:00
|
|
|
|
|
|
|
int ntri = leaf->getNumTriangles();
|
2002-03-18 16:25:16 +00:00
|
|
|
for ( n = 0; n < ntri; ++n )
|
|
|
|
{
|
|
|
|
sgdVec3 tri[3];
|
|
|
|
|
|
|
|
switch ( primType )
|
|
|
|
{
|
|
|
|
case GL_POLYGON :
|
2003-11-21 03:13:52 +00:00
|
|
|
SG_LOG( SG_TERRAIN, SG_ALERT,
|
|
|
|
"WARNING: dubiously handled GL_POLYGON" );
|
2002-03-18 16:25:16 +00:00
|
|
|
case GL_TRIANGLE_FAN :
|
2003-11-21 03:13:52 +00:00
|
|
|
/* SG_LOG( SG_TERRAIN, SG_ALERT,
|
|
|
|
"IntersectLeaf: GL_TRIANGLE_FAN" ); */
|
2002-03-18 16:25:16 +00:00
|
|
|
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) ) );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GL_TRIANGLES :
|
2003-11-21 03:13:52 +00:00
|
|
|
/* SG_LOG( SG_TERRAIN, SG_DEBUG,
|
|
|
|
"IntersectLeaf: GL_TRIANGLES" ); */
|
2002-03-18 16:25:16 +00:00
|
|
|
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) ) );
|
|
|
|
break;
|
|
|
|
case GL_QUAD_STRIP :
|
2003-11-21 03:13:52 +00:00
|
|
|
SG_LOG( SG_TERRAIN, SG_ALERT,
|
|
|
|
"WARNING: dubiously handled GL_QUAD_STRIP" );
|
|
|
|
case GL_TRIANGLE_STRIP :
|
|
|
|
/* SG_LOG( SG_TERRAIN, SG_ALERT,
|
|
|
|
"IntersectLeaf: GL_TRIANGLE_STRIP" ); */
|
2002-03-18 16:25:16 +00:00
|
|
|
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 ) {
|
2003-11-21 03:13:52 +00:00
|
|
|
sgdCopyVec3( tri[0], tri[2] );
|
|
|
|
sgdSetVec3( tri[2], leaf->getVertex( short(n+2) ) );
|
2002-03-18 16:25:16 +00:00
|
|
|
} else {
|
2003-11-21 22:00:46 +00:00
|
|
|
sgdCopyVec3( tri[1], tri[2] );
|
2002-03-18 16:25:16 +00:00
|
|
|
sgdSetVec3( tri[2], leaf->getVertex( short(n+2) ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GL_QUADS :
|
2003-11-21 03:13:52 +00:00
|
|
|
SG_LOG( SG_TERRAIN, SG_ALERT,
|
|
|
|
"WARNING: dubiously handled GL_QUADS" );
|
2002-03-18 16:25:16 +00:00
|
|
|
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) ) );
|
|
|
|
break;
|
|
|
|
default:
|
2003-11-21 03:13:52 +00:00
|
|
|
SG_LOG( SG_TERRAIN, SG_ALERT,
|
|
|
|
"WARNING: not-handled structure: " << primType );
|
2002-03-18 16:25:16 +00:00
|
|
|
return IntersectLeaf( leaf, m, orig, dir);
|
|
|
|
}
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
if( isZeroAreaTri( tri ) )
|
|
|
|
continue;
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
sgdVec4 plane;
|
|
|
|
sgdMakePlane( plane, tri[0], tri[1], tri[2] );
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2002-05-10 23:35:06 +00:00
|
|
|
sgdVec3 point;
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
// find point of intersection of line from point org
|
|
|
|
// in direction dir with triangle's plane
|
|
|
|
SGDfloat tmp = sgdScalarProductVec3 ( dir, plane ) ;
|
|
|
|
/* Is line parallel to plane? */
|
|
|
|
if ( sgdAbs ( tmp ) < FLT_EPSILON /*DBL_EPSILON*/ )
|
|
|
|
continue ;
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
// find parametric point
|
|
|
|
sgdScaleVec3 ( point, dir,
|
|
|
|
-( sgdScalarProductVec3 ( orig, plane ) + plane[3] )
|
|
|
|
/ tmp ) ;
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
// short circut if this point is further away then a previous hit
|
|
|
|
tmp_dist = sgdDistanceSquaredVec3(point, orig );
|
|
|
|
if( tmp_dist > test_dist )
|
|
|
|
continue;
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
// place parametric point in world
|
|
|
|
sgdAddVec3 ( point, orig ) ;
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
if( fgdPointInTriangle( point, tri ) ) {
|
|
|
|
// transform point into passed coordinate frame
|
|
|
|
sgdXformPnt3( point, point, m );
|
2002-04-25 19:44:27 +00:00
|
|
|
sgdXformPnt4(plane,plane,m);
|
2002-03-18 16:25:16 +00:00
|
|
|
add(leaf,n,point,plane);
|
|
|
|
test_dist = tmp_dist;
|
|
|
|
num_hits++;
|
|
|
|
}
|
2002-03-17 00:38:24 +00:00
|
|
|
}
|
|
|
|
return num_hits;
|
|
|
|
}
|
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
|
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
inline static bool IN_RANGE( sgdVec3 v, double radius ) {
|
|
|
|
return ( sgdScalarProductVec3(v, v) < (radius*radius) );
|
2002-03-17 00:38:24 +00:00
|
|
|
}
|
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
void FGHitList::IntersectBranch( ssgBranch *branch, sgdMat4 m,
|
|
|
|
sgdVec3 orig, sgdVec3 dir )
|
2002-03-17 00:38:24 +00:00
|
|
|
{
|
2003-11-21 22:00:46 +00:00
|
|
|
/* the lookat vector and matrix in branch's coordinate frame but
|
|
|
|
* we won't determine these unless needed, This 'lazy evaluation'
|
|
|
|
* is a result of profiling data */
|
2002-03-18 16:25:16 +00:00
|
|
|
sgdVec3 orig_leaf, dir_leaf;
|
|
|
|
sgdMat4 m_leaf;
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
// 'lazy evaluation' flag
|
|
|
|
int first_time = 1;
|
2002-03-17 00:38:24 +00:00
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
for ( ssgEntity *kid = branch->getKid( 0 );
|
|
|
|
kid != NULL;
|
|
|
|
kid = branch->getNextKid() )
|
|
|
|
{
|
2003-06-04 09:57:11 +00:00
|
|
|
if ( kid->getTraversalMask() & SSGTRAV_HOT
|
|
|
|
&& !kid->getBSphere()->isEmpty() )
|
2002-03-18 16:25:16 +00:00
|
|
|
{
|
|
|
|
sgdVec3 center;
|
|
|
|
sgdSetVec3( center,
|
|
|
|
kid->getBSphere()->getCenter()[0],
|
|
|
|
kid->getBSphere()->getCenter()[1],
|
|
|
|
kid->getBSphere()->getCenter()[2] );
|
|
|
|
sgdXformPnt3( center, m ) ;
|
|
|
|
|
|
|
|
// sgdClosestPointToLineDistSquared( center, orig, dir )
|
|
|
|
// inlined here because because of profiling results
|
|
|
|
sgdVec3 u, u1, v;
|
|
|
|
sgdSubVec3(u, center, orig);
|
|
|
|
sgdScaleVec3( u1, dir, sgdScalarProductVec3(u,dir)
|
|
|
|
/ sgdScalarProductVec3(dir,dir) );
|
|
|
|
sgdSubVec3(v, u, u1);
|
|
|
|
|
|
|
|
// double because of possible overflow
|
|
|
|
if ( IN_RANGE( v, double(kid->getBSphere()->getRadius()) ) )
|
|
|
|
{
|
|
|
|
if ( kid->isAKindOf ( ssgTypeBranch() ) )
|
|
|
|
{
|
|
|
|
sgdMat4 m_new;
|
|
|
|
if ( kid->isA( ssgTypeTransform() ) )
|
|
|
|
{
|
|
|
|
sgMat4 fxform;
|
|
|
|
((ssgTransform *)kid)->getTransform( fxform );
|
|
|
|
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 ) {
|
|
|
|
sgdTransposeNegateMat4( m_leaf, m );
|
|
|
|
sgdXformPnt3( orig_leaf, orig, m_leaf );
|
|
|
|
sgdXformPnt3( dir_leaf, dir, m_leaf );
|
|
|
|
first_time = 0;
|
|
|
|
}
|
2003-11-21 22:00:46 +00:00
|
|
|
// GLenum primType = ((ssgLeaf *)kid)->getPrimitiveType();
|
|
|
|
// IntersectLeaf( (ssgLeaf *)kid, m, orig_leaf, dir_leaf,
|
|
|
|
// primType );
|
|
|
|
IntersectLeaf( (ssgLeaf *)kid, m, orig_leaf, dir_leaf );
|
2002-03-18 16:25:16 +00:00
|
|
|
}
|
|
|
|
} // Out of range
|
|
|
|
} // branch not requested to be traversed
|
|
|
|
} // end for loop
|
|
|
|
}
|
2002-03-17 00:38:24 +00:00
|
|
|
|
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
// a temporary hack until we get everything rewritten with sgdVec3
|
|
|
|
static inline Point3D operator + (const Point3D& a, const sgdVec3 b)
|
|
|
|
{
|
|
|
|
return Point3D(a.x()+b[0], a.y()+b[1], a.z()+b[2]);
|
|
|
|
}
|
2002-03-17 00:38:24 +00:00
|
|
|
|
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
void FGHitList::Intersect( ssgBranch *scene, sgdVec3 orig, sgdVec3 dir ) {
|
|
|
|
sgdMat4 m;
|
|
|
|
clear();
|
|
|
|
sgdMakeIdentMat4 ( m ) ;
|
|
|
|
IntersectBranch( scene, m, orig, dir );
|
2000-06-16 02:15:08 +00:00
|
|
|
}
|
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
|
2002-03-18 16:25:16 +00:00
|
|
|
void FGHitList::Intersect( ssgBranch *scene, sgdMat4 m, sgdVec3 orig, sgdVec3 dir )
|
2002-03-17 00:38:24 +00:00
|
|
|
{
|
2002-03-18 16:25:16 +00:00
|
|
|
clear();
|
|
|
|
IntersectBranch( scene, m, orig, dir );
|
2002-03-17 00:38:24 +00:00
|
|
|
}
|
|
|
|
|
2000-06-16 02:15:08 +00:00
|
|
|
|
2002-03-17 00:38:24 +00:00
|
|
|
// Determine scenery altitude via ssg.
|
|
|
|
// returned results are in meters
|
2004-04-01 15:27:53 +00:00
|
|
|
// static double hitlist1_time = 0.0;
|
2003-11-21 22:00:46 +00:00
|
|
|
|
2003-11-21 04:41:01 +00:00
|
|
|
bool fgCurrentElev( sgdVec3 abs_view_pos, double max_alt_m,
|
|
|
|
sgdVec3 scenery_center,
|
2002-03-18 19:02:59 +00:00
|
|
|
FGHitList *hit_list,
|
|
|
|
double *terrain_elev, double *radius, double *normal)
|
2001-07-11 15:51:21 +00:00
|
|
|
{
|
2003-11-24 01:47:09 +00:00
|
|
|
// SGTimeStamp start; start.stamp();
|
2003-11-21 22:00:46 +00:00
|
|
|
|
|
|
|
bool result;
|
2002-03-18 19:02:59 +00:00
|
|
|
sgdVec3 view_pos;
|
|
|
|
sgdSubVec3( view_pos, abs_view_pos, scenery_center );
|
|
|
|
|
|
|
|
sgdVec3 orig, dir;
|
|
|
|
sgdCopyVec3(orig, view_pos );
|
|
|
|
sgdCopyVec3(dir, abs_view_pos );
|
|
|
|
|
2002-05-14 05:49:47 +00:00
|
|
|
hit_list->Intersect( globals->get_scenery()->get_terrain_branch(),
|
|
|
|
orig, dir );
|
2002-03-18 19:02:59 +00:00
|
|
|
|
2003-11-25 01:08:39 +00:00
|
|
|
int this_hit = -1;
|
|
|
|
int max_hit = -1;
|
2002-03-18 19:02:59 +00:00
|
|
|
Point3D geoc;
|
2003-11-21 22:00:46 +00:00
|
|
|
double hit_elev = -9999;
|
2003-11-25 01:08:39 +00:00
|
|
|
double max_elev = -9999;
|
2002-03-18 19:02:59 +00:00
|
|
|
Point3D sc(scenery_center[0], scenery_center[1], scenery_center[2]) ;
|
|
|
|
|
|
|
|
int hitcount = hit_list->num_hits();
|
2003-11-21 03:13:52 +00:00
|
|
|
// cout << "hits = " << hitcount << endl;
|
2002-03-18 19:02:59 +00:00
|
|
|
for ( int i = 0; i < hitcount; ++i ) {
|
2003-12-19 02:42:32 +00:00
|
|
|
// FIXME: sgCartToGeod is slow. Call it just once for the
|
|
|
|
// "sc" point, and then handle the rest with a geodetic "up"
|
|
|
|
// vector approximation. Across one tile, this will be
|
|
|
|
// acceptable.
|
|
|
|
double alt = sgCartToGeod( sc + hit_list->get_point(i) ).elev();
|
2003-11-21 03:13:52 +00:00
|
|
|
// cout << "hit " << i << " lon = " << geoc.lon() << " lat = "
|
2003-11-21 22:00:46 +00:00
|
|
|
// << lat_geod << " alt = " << alt << " max alt = " << max_alt_m
|
|
|
|
// << endl;
|
|
|
|
if ( alt > hit_elev && alt < max_alt_m ) {
|
2003-11-21 04:41:01 +00:00
|
|
|
// cout << " it's a keeper" << endl;
|
2003-11-21 22:00:46 +00:00
|
|
|
hit_elev = alt;
|
2002-03-18 19:02:59 +00:00
|
|
|
this_hit = i;
|
|
|
|
}
|
2003-11-25 01:08:39 +00:00
|
|
|
if ( alt > hit_elev ) {
|
|
|
|
max_elev = alt;
|
|
|
|
max_hit = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( this_hit < 0 ) {
|
|
|
|
// no hits below us, take the max hit
|
|
|
|
this_hit = max_hit;
|
|
|
|
hit_elev = max_elev;
|
2002-03-18 19:02:59 +00:00
|
|
|
}
|
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
if ( hit_elev > -9000 ) {
|
|
|
|
*terrain_elev = hit_elev;
|
2002-03-18 19:02:59 +00:00
|
|
|
*radius = geoc.radius();
|
|
|
|
sgVec3 tmp;
|
|
|
|
sgSetVec3(tmp, hit_list->get_normal(this_hit));
|
2002-04-25 19:44:27 +00:00
|
|
|
// cout << "cur_normal: " << tmp[0] << " " << tmp[1] << " " << tmp[2] << endl;
|
|
|
|
sgdSetVec3( normal, tmp );
|
|
|
|
// float *up = globals->get_current_view()->get_world_up();
|
|
|
|
// cout << "world_up : " << up[0] << " " << up[1] << " " << up[2] << endl;
|
2002-03-18 19:02:59 +00:00
|
|
|
/* ssgState *IntersectedLeafState =
|
|
|
|
((ssgLeaf*)hit_list->get_entity(this_hit))->getState(); */
|
2003-11-21 22:00:46 +00:00
|
|
|
result = true;
|
2002-03-18 19:02:59 +00:00
|
|
|
} else {
|
|
|
|
SG_LOG( SG_TERRAIN, SG_INFO, "no terrain intersection" );
|
|
|
|
*terrain_elev = 0.0;
|
|
|
|
float *up = globals->get_current_view()->get_world_up();
|
|
|
|
sgdSetVec3(normal, up[0], up[1], up[2]);
|
2003-11-21 22:00:46 +00:00
|
|
|
result = false;
|
2002-03-18 19:02:59 +00:00
|
|
|
}
|
2003-11-21 22:00:46 +00:00
|
|
|
|
2003-11-24 01:47:09 +00:00
|
|
|
// SGTimeStamp finish; finish.stamp();
|
|
|
|
// hitlist1_time = ( 29.0 * hitlist1_time + (finish - start) ) / 30.0;
|
2003-11-21 22:00:46 +00:00
|
|
|
// cout << " time per call = " << hitlist1_time << endl;
|
|
|
|
|
|
|
|
return result;
|
2001-07-11 15:51:21 +00:00
|
|
|
}
|
2002-03-17 00:38:24 +00:00
|
|
|
|
|
|
|
|
2004-04-01 15:27:53 +00:00
|
|
|
// static double hitlist2_time = 0.0;
|
2003-11-21 22:00:46 +00:00
|
|
|
|
2002-03-17 00:38:24 +00:00
|
|
|
// Determine scenery altitude via ssg.
|
|
|
|
// returned results are in meters
|
2003-11-21 04:41:01 +00:00
|
|
|
bool fgCurrentElev( sgdVec3 abs_view_pos, double max_alt_m,
|
|
|
|
sgdVec3 scenery_center,
|
2002-03-18 19:02:59 +00:00
|
|
|
ssgTransform *terra_transform,
|
|
|
|
FGHitList *hit_list,
|
|
|
|
double *terrain_elev, double *radius, double *normal)
|
2002-03-17 00:38:24 +00:00
|
|
|
{
|
2003-11-24 01:47:09 +00:00
|
|
|
// SGTimeStamp start; start.stamp();
|
2003-11-21 22:00:46 +00:00
|
|
|
|
|
|
|
bool result;
|
2002-03-18 19:02:59 +00:00
|
|
|
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 );
|
|
|
|
|
2003-11-25 01:08:39 +00:00
|
|
|
int this_hit = -1;
|
|
|
|
int max_hit = -1;
|
2002-03-18 19:02:59 +00:00
|
|
|
Point3D geoc;
|
2003-11-21 22:00:46 +00:00
|
|
|
double hit_elev = -9999;
|
2003-11-25 01:08:39 +00:00
|
|
|
double max_elev = -9999;
|
2002-03-18 19:02:59 +00:00
|
|
|
Point3D sc(scenery_center[0], scenery_center[1], scenery_center[2]) ;
|
|
|
|
|
|
|
|
int hitcount = hit_list->num_hits();
|
2003-11-21 22:00:46 +00:00
|
|
|
// cout << "hits = " << hitcount << endl;
|
2002-03-18 19:02:59 +00:00
|
|
|
for ( int i = 0; i < hitcount; ++i ) {
|
2003-12-19 02:42:32 +00:00
|
|
|
// FIXME: sgCartToGeod is slow. Call it just once for the
|
|
|
|
// "sc" point, and then handle the rest with a geodetic "up"
|
|
|
|
// vector approximation. Across one tile, this will be
|
|
|
|
// acceptable.
|
|
|
|
double alt = sgCartToGeod( sc + hit_list->get_point(i) ).elev();
|
2003-11-21 22:00:46 +00:00
|
|
|
// cout << "hit " << i << " lon = " << geoc.lon() << " lat = "
|
|
|
|
// << lat_geod << " alt = " << alt << " max alt = " << max_alt_m
|
|
|
|
// << endl;
|
|
|
|
if ( alt > hit_elev && alt < max_alt_m ) {
|
|
|
|
hit_elev = alt;
|
2002-03-18 19:02:59 +00:00
|
|
|
this_hit = i;
|
2003-11-21 22:00:46 +00:00
|
|
|
// cout << " it's a keeper" << endl;
|
2002-03-18 19:02:59 +00:00
|
|
|
}
|
2003-11-25 01:08:39 +00:00
|
|
|
if ( alt > hit_elev ) {
|
|
|
|
max_elev = alt;
|
|
|
|
max_hit = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( this_hit < 0 ) {
|
|
|
|
// no hits below us, take the max hit
|
|
|
|
this_hit = max_hit;
|
|
|
|
hit_elev = max_elev;
|
2002-03-18 19:02:59 +00:00
|
|
|
}
|
|
|
|
|
2003-11-21 22:00:46 +00:00
|
|
|
if ( hit_elev > -9000 ) {
|
|
|
|
*terrain_elev = hit_elev;
|
2002-03-18 19:02:59 +00:00
|
|
|
*radius = geoc.radius();
|
|
|
|
sgVec3 tmp;
|
|
|
|
sgSetVec3(tmp, hit_list->get_normal(this_hit));
|
2002-04-25 19:44:27 +00:00
|
|
|
// cout << "cur_normal: " << tmp[0] << " " << tmp[1] << " " << tmp[2] << endl;
|
|
|
|
sgdSetVec3( normal, tmp );
|
|
|
|
// float *up = globals->get_current_view()->get_world_up();
|
|
|
|
// cout << "world_up : " << up[0] << " " << up[1] << " " << up[2] << endl;
|
2002-03-18 19:02:59 +00:00
|
|
|
/* ssgState *IntersectedLeafState =
|
|
|
|
((ssgLeaf*)hit_list->get_entity(this_hit))->getState(); */
|
2003-11-21 22:00:46 +00:00
|
|
|
result = true;
|
2002-03-18 19:02:59 +00:00
|
|
|
} else {
|
|
|
|
SG_LOG( SG_TERRAIN, SG_DEBUG, "DOING FULL TERRAIN INTERSECTION" );
|
2003-11-21 22:00:46 +00:00
|
|
|
result = fgCurrentElev( abs_view_pos, max_alt_m, scenery_center,
|
|
|
|
hit_list, terrain_elev, radius, normal);
|
2002-03-18 19:02:59 +00:00
|
|
|
}
|
2003-11-21 22:00:46 +00:00
|
|
|
|
2003-11-24 01:47:09 +00:00
|
|
|
// SGTimeStamp finish; finish.stamp();
|
|
|
|
// hitlist2_time = ( 29.0 * hitlist2_time + (finish - start) ) / 30.0;
|
|
|
|
// cout << "time per call 2 = " << hitlist2_time << endl;
|
2003-11-21 22:00:46 +00:00
|
|
|
|
|
|
|
return result;
|
2002-03-17 00:38:24 +00:00
|
|
|
}
|
|
|
|
|