X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FScenery%2Fscenery.cxx;h=5ed687e83c3175e9f0acfd70a39931c4b11b6b80;hb=3d4806adbe6705f785fd950bd4b4afde1cb4f8d2;hp=88badf93eea9498ce85dc92804f8071ef04a3526;hpb=7b824755ee96b8db4240ca9999570f3c7bacdad7;p=flightgear.git diff --git a/src/Scenery/scenery.cxx b/src/Scenery/scenery.cxx index 88badf93e..5ed687e83 100644 --- a/src/Scenery/scenery.cxx +++ b/src/Scenery/scenery.cxx @@ -16,7 +16,7 @@ // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ @@ -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,89 +265,93 @@ void FGScenery::bind() { void FGScenery::unbind() { } -void FGScenery::set_center( 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; - } +bool +FGScenery::get_cart_elevation_m(const SGVec3d& pos, double max_altoff, + double& alt, + const simgear::BVHMaterial** material, + const osg::Node* butNotFrom) +{ + SGGeod geod = SGGeod::fromCart(pos); + geod.setElevationM(geod.getElevationM() + max_altoff); + return get_elevation_m(geod, alt, material, butNotFrom); } -void FGScenery::register_placement_transform(ssgPlacementTransform *trans) { - trans->ref(); - _placement_list.push_back(trans); - sgdVec3 c; - sgdSetVec3(c, center.x(), center.y(), center.z()); - trans->setSceneryCenter(c); -} +bool +FGScenery::get_elevation_m(const SGGeod& geod, double& alt, + const simgear::BVHMaterial** material, + const osg::Node* butNotFrom) +{ + SGVec3d start = SGVec3d::fromGeod(geod); -void FGScenery::unregister_placement_transform(ssgPlacementTransform *trans) { - placement_list_type::iterator it = _placement_list.begin(); - while (it != _placement_list.end()) { - if ((*it) == trans) { - (*it)->deRef(); - it = _placement_list.erase(it); - } else - ++it; - } + SGGeod geodEnd = geod; + geodEnd.setElevationM(SGMiscd::min(geod.getElevationM() - 10, -10000)); + SGVec3d end = SGVec3d::fromGeod(geodEnd); + + 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_elevation_m(double lat, double lon, double max_alt, - double& alt, bool exact) +FGScenery::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir, + SGVec3d& nearestHit, + 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); + // We assume that starting positions in the center of the earth are invalid + if ( norm1(pos) < 1 ) + return false; + + // Make really sure the direction is normalized, is really cheap compared to + // computation of ground intersection. + 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 (!intersectVisitor.getHaveHit()) + return false; + + nearestHit = intersectVisitor.getLineSegment().getEnd(); + return true; } -bool -FGScenery::get_cart_elevation_m(const sgdVec3 pos, double max_altoff, - double& alt, bool exact) +bool FGScenery::scenery_available(const SGGeod& position, double range_m) { - 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 = false; - } - } - - // 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); + 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; +} - if (replaced_center) - set_center( saved_center ); - - return hit; +SceneryPager* FGScenery::getPagerSingleton() +{ + static osg::ref_ptr pager = new SceneryPager; + return pager.get(); } +