1
0
Fork 0

Use the bv tree leafs for altitude queries.

Make use ot the bounding volume tree nodes already
present in the scenegraph for the ground cache for
scenery intersection and elevation queries.
This commit is contained in:
Mathias Froehlich 2011-08-07 11:12:45 +02:00
parent 0b04bf9cdd
commit 2c24b1da17

View file

@ -28,8 +28,12 @@
#include <stdio.h>
#include <string.h>
#include <osg/Camera>
#include <osg/Transform>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osg/CameraView>
#include <osgViewer/Viewer>
#include <osgUtil/IntersectVisitor>
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
@ -38,6 +42,8 @@
#include <simgear/scene/util/SGNodeMasks.hxx>
#include <simgear/scene/util/SGSceneUserData.hxx>
#include <simgear/scene/model/CheckSceneryVisitor.hxx>
#include <simgear/scene/bvh/BVHNode.hxx>
#include <simgear/scene/bvh/BVHLineSegmentVisitor.hxx>
#include <Main/renderer.hxx>
#include <Main/fg_props.hxx>
@ -70,6 +76,138 @@ public:
}
};
class FGSceneryIntersect : public osg::NodeVisitor {
public:
FGSceneryIntersect(const SGLineSegmentd& lineSegment,
const osg::Node* skipNode) :
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
_lineSegment(lineSegment),
_skipNode(skipNode),
_material(0),
_haveHit(false)
{ }
bool getHaveHit() const
{ return _haveHit; }
const SGLineSegmentd& getLineSegment() const
{ return _lineSegment; }
const SGMaterial* getMaterial() const
{ return _material; }
virtual void apply(osg::Node& node)
{
if (&node == _skipNode)
return;
if (!testBoundingSphere(node.getBound()))
return;
addBoundingVolume(node);
}
virtual void apply(osg::Group& group)
{
if (&group == _skipNode)
return;
if (!testBoundingSphere(group.getBound()))
return;
traverse(group);
addBoundingVolume(group);
}
virtual void apply(osg::Transform& transform)
{ handleTransform(transform); }
virtual void apply(osg::Camera& camera)
{
if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
return;
handleTransform(camera);
}
virtual void apply(osg::CameraView& transform)
{ handleTransform(transform); }
virtual void apply(osg::MatrixTransform& transform)
{ handleTransform(transform); }
virtual void apply(osg::PositionAttitudeTransform& transform)
{ handleTransform(transform); }
private:
void handleTransform(osg::Transform& transform)
{
if (&transform == _skipNode)
return;
// Hmm, may be this needs to be refined somehow ...
if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
return;
if (!testBoundingSphere(transform.getBound()))
return;
osg::Matrix inverseMatrix;
if (!transform.computeWorldToLocalMatrix(inverseMatrix, this))
return;
osg::Matrix matrix;
if (!transform.computeLocalToWorldMatrix(matrix, this))
return;
SGLineSegmentd lineSegment = _lineSegment;
bool haveHit = _haveHit;
const SGMaterial* material = _material;
_haveHit = false;
_lineSegment = lineSegment.transform(SGMatrixd(inverseMatrix.ptr()));
addBoundingVolume(transform);
traverse(transform);
if (_haveHit) {
_lineSegment = _lineSegment.transform(SGMatrixd(matrix.ptr()));
} else {
_lineSegment = lineSegment;
_material = material;
_haveHit = haveHit;
}
}
simgear::BVHNode* getNodeBoundingVolume(osg::Node& node)
{
SGSceneUserData* userData = SGSceneUserData::getSceneUserData(&node);
if (!userData)
return 0;
return userData->getBVHNode();
}
void addBoundingVolume(osg::Node& node)
{
simgear::BVHNode* bvNode = getNodeBoundingVolume(node);
if (!bvNode)
return;
// Find ground intersection on the bvh nodes
simgear::BVHLineSegmentVisitor lineSegmentVisitor(_lineSegment,
0/*startTime*/);
bvNode->accept(lineSegmentVisitor);
if (!lineSegmentVisitor.empty()) {
_lineSegment = lineSegmentVisitor.getLineSegment();
_material = lineSegmentVisitor.getMaterial();
_haveHit = true;
}
}
bool testBoundingSphere(const osg::BoundingSphere& bound) const
{
if (!bound.valid())
return false;
SGSphered sphere(toVec3d(toSG(bound._center)), bound._radius);
return intersects(_lineSegment, sphere);
}
SGLineSegmentd _lineSegment;
const osg::Node* _skipNode;
const SGMaterial* _material;
bool _haveHit;
};
// Scenery Management system
FGScenery::FGScenery()
{
@ -139,42 +277,19 @@ FGScenery::get_elevation_m(const SGGeod& geod, double& alt,
geodEnd.setElevationM(SGMiscd::min(geod.getElevationM() - 10, -10000));
SGVec3d end = SGVec3d::fromGeod(geodEnd);
osgUtil::IntersectVisitor intersectVisitor;
FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
osg::ref_ptr<osg::LineSegment> lineSegment;
lineSegment = new osg::LineSegment(toOsg(start), toOsg(end));
intersectVisitor.addLineSegment(lineSegment.get());
get_scene_graph()->accept(intersectVisitor);
bool hits = false;
if (intersectVisitor.hits()) {
int nHits = intersectVisitor.getNumHits(lineSegment.get());
alt = -SGLimitsd::max();
for (int i = 0; i < nHits; ++i) {
const osgUtil::Hit& hit
= intersectVisitor.getHitList(lineSegment.get())[i];
if (butNotFrom &&
std::find(hit.getNodePath().begin(), hit.getNodePath().end(),
butNotFrom) != hit.getNodePath().end())
continue;
// We might need the double variant of the intersection point.
// Thus we cannot use the float variant delivered by
// hit.getWorldIntersectPoint() but we have to redo that with osg::Vec3d.
osg::Vec3d point = hit.getLocalIntersectPoint();
if (hit.getMatrix())
point = point*(*hit.getMatrix());
SGGeod geod = SGGeod::fromCart(toSG(point));
double elevation = geod.getElevationM();
if (alt < elevation) {
alt = elevation;
hits = true;
if (material)
*material = SGMaterialLib::findMaterial(hit.getGeode());
}
}
}
if (!intersectVisitor.getHaveHit())
return false;
return hits;
geodEnd = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd());
alt = geod.getElevationM();
if (material)
*material = intersectVisitor.getMaterial();
return true;
}
bool
@ -191,39 +306,15 @@ FGScenery::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir,
SGVec3d start = pos;
SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ???
osgUtil::IntersectVisitor intersectVisitor;
FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom);
intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
osg::ref_ptr<osg::LineSegment> lineSegment;
lineSegment = new osg::LineSegment(toOsg(start), toOsg(end));
intersectVisitor.addLineSegment(lineSegment.get());
get_scene_graph()->accept(intersectVisitor);
bool hits = false;
if (intersectVisitor.hits()) {
int nHits = intersectVisitor.getNumHits(lineSegment.get());
double dist = SGLimitsd::max();
for (int i = 0; i < nHits; ++i) {
const osgUtil::Hit& hit
= intersectVisitor.getHitList(lineSegment.get())[i];
if (butNotFrom &&
std::find(hit.getNodePath().begin(), hit.getNodePath().end(),
butNotFrom) != hit.getNodePath().end())
continue;
// We might need the double variant of the intersection point.
// Thus we cannot use the float variant delivered by
// hit.getWorldIntersectPoint() but we have to redo that with osg::Vec3d.
osg::Vec3d point = hit.getLocalIntersectPoint();
if (hit.getMatrix())
point = point*(*hit.getMatrix());
double newdist = length(start - toSG(point));
if (newdist < dist) {
dist = newdist;
nearestHit = toSG(point);
hits = true;
}
}
}
return hits;
if (!intersectVisitor.getHaveHit())
return false;
nearestHit = intersectVisitor.getLineSegment().getEnd();
return true;
}
bool FGScenery::scenery_available(const SGGeod& position, double range_m)