X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FScenery%2Fscenery.cxx;h=5ed687e83c3175e9f0acfd70a39931c4b11b6b80;hb=3d4806adbe6705f785fd950bd4b4afde1cb4f8d2;hp=4c82beae27ff12f5d9ae9a5bbc997826e6865b0d;hpb=c9813d1b5d79b4aad13c263690a0223086af25ac;p=flightgear.git diff --git a/src/Scenery/scenery.cxx b/src/Scenery/scenery.cxx index 4c82beae2..5ed687e83 100644 --- a/src/Scenery/scenery.cxx +++ b/src/Scenery/scenery.cxx @@ -28,68 +28,233 @@ #include #include +#include +#include +#include +#include +#include +#include + +#include +#include #include #include -#include -#include - +#include +#include +#include +#include +#include +#include +#include +#include + +#include #include
-#include "hitlist.hxx" +#include "tilemgr.hxx" #include "scenery.hxx" +using namespace flightgear; +using namespace simgear; + +class FGGroundPickCallback : public SGPickCallback { +public: + virtual bool buttonPressed(int button, const Info& info) + { + // only on left mouse button + if (button != 0) + return false; + + SGGeod geod = SGGeod::fromCart(info.wgs84); + SG_LOG( SG_TERRAIN, SG_INFO, "Got ground pick at " << geod ); + + SGPropertyNode *c = fgGetNode("/sim/input/click", true); + c->setDoubleValue("longitude-deg", geod.getLongitudeDeg()); + c->setDoubleValue("latitude-deg", geod.getLatitudeDeg()); + c->setDoubleValue("elevation-m", geod.getElevationM()); + c->setDoubleValue("elevation-ft", geod.getElevationFt()); + fgSetBool("/sim/signals/click", 1); + + return true; + } +}; + +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 simgear::BVHMaterial* 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 simgear::BVHMaterial* 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 simgear::BVHMaterial* _material; + bool _haveHit; +}; // Scenery Management system -FGScenery::FGScenery() { +FGScenery::FGScenery() +{ SG_LOG( SG_TERRAIN, SG_INFO, "Initializing scenery subsystem" ); - - center = Point3D(0.0); + // keep reference to pager singleton, so it cannot be destroyed while FGScenery lives + _pager = FGScenery::getPagerSingleton(); } - -// Initialize the Scenery Management system FGScenery::~FGScenery() { } +// Initialize the Scenery Management system void FGScenery::init() { // Scene graph root - scene_graph = new ssgRoot; + scene_graph = new osg::Group; scene_graph->setName( "Scene" ); // Terrain branch - terrain_branch = new ssgBranch; + terrain_branch = new osg::Group; terrain_branch->setName( "Terrain" ); - scene_graph->addKid( terrain_branch ); + scene_graph->addChild( terrain_branch.get() ); + SGSceneUserData* userData; + userData = SGSceneUserData::getOrCreateSceneUserData(terrain_branch.get()); + userData->setPickCallback(new FGGroundPickCallback); - models_branch = new ssgBranch; + models_branch = new osg::Group; models_branch->setName( "Models" ); - scene_graph->addKid( models_branch ); + scene_graph->addChild( models_branch.get() ); - aircraft_branch = new ssgBranch; + aircraft_branch = new osg::Group; aircraft_branch->setName( "Aircraft" ); - scene_graph->addKid( aircraft_branch ); - - // Lighting - gnd_lights_root = new ssgRoot; - gnd_lights_root->setName( "Ground Lighting Root" ); - - vasi_lights_root = new ssgRoot; - vasi_lights_root->setName( "VASI/PAPI Lighting Root" ); - - rwy_lights_root = new ssgRoot; - rwy_lights_root->setName( "Runway Lighting Root" ); - - taxi_lights_root = new ssgRoot; - taxi_lights_root->setName( "Taxi Lighting Root" ); + scene_graph->addChild( aircraft_branch.get() ); // Initials values needed by the draw-time object loader - sgUserDataInit( globals->get_model_lib(), globals->get_fg_root(), - globals->get_props(), globals->get_sim_time_sec() ); + sgUserDataInit( globals->get_props() ); } -void FGScenery::update(double dt) { +void FGScenery::update(double dt) +{ + SG_UNUSED(dt); + // nothing here, don't call again + suspend(); } @@ -100,158 +265,93 @@ void FGScenery::bind() { void FGScenery::unbind() { } -void FGScenery::set_center( const Point3D& p ) { - center = p; - sgdVec3 c; - sgdSetVec3(c, p.x(), p.y(), p.z()); - placement_list_type::iterator it = _placement_list.begin(); - while (it != _placement_list.end()) { - (*it)->setSceneryCenter(c); - ++it; - } -} - -void FGScenery::register_placement_transform(ssgPlacementTransform *trans) { - _placement_list.push_back(trans); - sgdVec3 c; - sgdSetVec3(c, center.x(), center.y(), center.z()); - trans->setSceneryCenter(c); -} - -void FGScenery::unregister_placement_transform(ssgPlacementTransform *trans) { - placement_list_type::iterator it = _placement_list.begin(); - while (it != _placement_list.end()) { - if ((*it) == trans) { - it = _placement_list.erase(it); - } else - ++it; - } -} - bool -FGScenery::get_elevation_m(double lat, double lon, double max_alt, - double& alt, bool exact) +FGScenery::get_cart_elevation_m(const SGVec3d& pos, double max_altoff, + double& alt, + const simgear::BVHMaterial** material, + const osg::Node* butNotFrom) { -// std::cout << __PRETTY_FUNCTION__ << " " -// << lat << " " -// << lon << " " -// << max_alt -// << std::endl; - sgdVec3 pos; - sgGeodToCart(lat*SG_DEGREES_TO_RADIANS, lon*SG_DEGREES_TO_RADIANS, - max_alt, pos); - return get_cart_elevation_m(pos, 0, alt, exact); + SGGeod geod = SGGeod::fromCart(pos); + geod.setElevationM(geod.getElevationM() + max_altoff); + return get_elevation_m(geod, alt, material, butNotFrom); } bool -FGScenery::get_cart_elevation_m(const sgdVec3& pos, double max_altoff, - double& alt, bool exact) +FGScenery::get_elevation_m(const SGGeod& geod, double& alt, + const simgear::BVHMaterial** material, + const osg::Node* butNotFrom) { - Point3D saved_center = center; - bool replaced_center = false; - if (exact) { - Point3D ppos(pos[0], pos[1], pos[2]); - if (30.0*30.0 < ppos.distance3Dsquared(center)) { - set_center( ppos ); - replaced_center = true; - } - } + SGVec3d start = SGVec3d::fromGeod(geod); - // overridden with actual values if a terrain intersection is - // found - double hit_radius = 0.0; - sgdVec3 hit_normal = { 0.0, 0.0, 0.0 }; - - bool hit = false; - if ( fabs(pos[0]) > 1.0 || fabs(pos[1]) > 1.0 || fabs(pos[2]) > 1.0 ) { - sgdVec3 sc; - sgdSetVec3(sc, center[0], center[1], center[2]); - - sgdVec3 ncpos; - sgdCopyVec3(ncpos, pos); - - FGHitList hit_list; - - // scenery center has been properly defined so any hit should - // be valid (and not just luck) - hit = fgCurrentElev(ncpos, max_altoff+sgdLengthVec3(pos), - sc, (ssgTransform*)get_scene_graph(), - &hit_list, &alt, &hit_radius, hit_normal); - } + SGGeod geodEnd = geod; + geodEnd.setElevationM(SGMiscd::min(geod.getElevationM() - 10, -10000)); + SGVec3d end = SGVec3d::fromGeod(geodEnd); - if (replaced_center) - set_center( saved_center ); - - return hit; -} + FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom); + intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT); + get_scene_graph()->accept(intersectVisitor); + + if (!intersectVisitor.getHaveHit()) + return false; + geodEnd = SGGeod::fromCart(intersectVisitor.getLineSegment().getEnd()); + alt = geodEnd.getElevationM(); + if (material) + *material = intersectVisitor.getMaterial(); + + return true; +} bool -FGScenery::get_cart_ground_intersection(const sgdVec3& pos, - const sgdVec3& dir, - sgdVec3& nearestHit, bool exact) +FGScenery::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir, + SGVec3d& nearestHit, + const osg::Node* butNotFrom) { // We assume that starting positions in the center of the earth are invalid - if ( fabs(pos[0]) < 1.0 && fabs(pos[1]) < 1.0 && fabs(pos[2]) < 1.0 ) + if ( norm1(pos) < 1 ) return false; - // Well that 'exactness' is somehow problematic, but makes at least sure - // that we don't compute that with a cenery center at the other side of - // the world ... - Point3D saved_center = center; - bool replaced_center = false; - if (exact) { - Point3D ppos(pos[0], pos[1], pos[2]); - if (30.0*30.0 < ppos.distance3Dsquared(center)) { - set_center( ppos ); - replaced_center = true; - } - } - - // Not yet found any hit ... - bool result = false; - // Make really sure the direction is normalized, is really cheap compared to // computation of ground intersection. - sgdVec3 normalizedDir; - sgdCopyVec3(normalizedDir, dir); - sgdNormaliseVec3(normalizedDir); - - sgdVec3 sceneryCenter; - sgdSetVec3(sceneryCenter, center[0], center[1], center[2]); - sgdVec3 relativePos; - sgdSubVec3(relativePos, pos, sceneryCenter); - - // At the moment only intersection with the terrain? - FGHitList hit_list; - hit_list.Intersect(globals->get_scenery()->get_terrain_branch(), - relativePos, normalizedDir); - - double dist = DBL_MAX; - int hitcount = hit_list.num_hits(); - for (int i = 0; i < hitcount; ++i) { - // Check for the nearest hit - sgdVec3 diff; - sgdSubVec3(diff, hit_list.get_point(i), relativePos); - - // We only want hits in front of us ... - if (sgdScalarProductVec3(normalizedDir, diff) < 0) - continue; - - // find the nearest hit - double nDist = sgdScalarProductVec3(diff, diff); - if (dist < nDist) - continue; - - // Store the hit point - dist = nDist; - sgdAddVec3(nearestHit, hit_list.get_point(i), sceneryCenter); - result = true; - } + SGVec3d start = pos; + SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ??? + + FGSceneryIntersect intersectVisitor(SGLineSegmentd(start, end), butNotFrom); + intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT); + get_scene_graph()->accept(intersectVisitor); - if (replaced_center) - set_center( saved_center ); + if (!intersectVisitor.getHaveHit()) + return false; - return result; + nearestHit = intersectVisitor.getLineSegment().getEnd(); + return true; +} + +bool FGScenery::scenery_available(const SGGeod& position, double range_m) +{ + if(globals->get_tile_mgr()->schedule_scenery(position, range_m, 0.0)) + { + double elev; + if (!get_elevation_m(SGGeod::fromGeodM(position, SG_MAX_ELEVATION_M), elev, 0, 0)) + return false; + SGVec3f p = SGVec3f::fromGeod(SGGeod::fromGeodM(position, elev)); + osg::FrameStamp* framestamp + = globals->get_renderer()->getViewer()->getFrameStamp(); + simgear::CheckSceneryVisitor csnv(_pager, toOsg(p), range_m, framestamp); + // currently the PagedLODs will not be loaded by the DatabasePager + // while the splashscreen is there, so CheckSceneryVisitor force-loads + // missing objects in the main thread + get_scene_graph()->accept(csnv); + if(!csnv.isLoaded()) + return false; + return true; + } + return false; +} + +SceneryPager* FGScenery::getPagerSingleton() +{ + static osg::ref_ptr pager = new SceneryPager; + return pager.get(); }