diff --git a/src/Input/input.cxx b/src/Input/input.cxx index 9c8f61351..971ffeb98 100644 --- a/src/Input/input.cxx +++ b/src/Input/input.cxx @@ -284,17 +284,14 @@ FGInput::doMouseClick (int b, int updown, int x, int y) // pui didn't want the click event so compute a // scenegraph intersection point corresponding to the mouse click if (updown == MOUSE_BUTTON_DOWN) { - FGScenery* scenery = globals->get_scenery(); - SGVec3d start, dir; // Get the list of hit callbacks. Take the first callback that // accepts the mouse button press and ignore the rest of them // That is they get sorted by distance and by scenegraph depth. // The nearest one is the first one and the deepest // (the most specialized one in the scenegraph) is the first. - if (FGRenderer::getPickInfo(start, dir, x, y)) { - std::vector pickList; - scenery->pick(start, dir, pickList); + std::vector pickList; + if (FGRenderer::pick(x, y, pickList)) { std::vector::const_iterator i; for (i = pickList.begin(); i != pickList.end(); ++i) { if (i->callback->buttonPressed(b, i->info)) { diff --git a/src/Main/renderer.cxx b/src/Main/renderer.cxx index 9d0581acf..8b3c895e7 100644 --- a/src/Main/renderer.cxx +++ b/src/Main/renderer.cxx @@ -53,6 +53,7 @@ #include #include +#include #include #include @@ -975,55 +976,94 @@ n = 0.2; fgHackFrustum(); } -bool FGRenderer::getPickInfo( SGVec3d& pt, SGVec3d& dir, - unsigned x, unsigned y ) +bool +FGRenderer::pick( unsigned x, unsigned y, + std::vector& pickList ) { - // Get the matrices involved in the transform from global to screen - // coordinates. - osg::Matrix pm = sceneView->getCamera()->getProjectionMatrix(); + // wipe out the return ... + pickList.resize(0); - osg::Matrix mv; - osg::NodePathList paths; - paths = globals->get_scenery()->get_scene_graph()->getParentalNodePaths(); - if (!paths.empty()) { - // Ok, we know that this should not have multiple parents ... - // FIXME: is this allways true? - mv = osg::computeLocalToEye(sceneView->getCamera()->getViewMatrix(), - paths.front(), false); - } - - // Compose and invert - osg::Matrix m = osg::Matrix::inverse(mv*pm); - - // Get the width and height of the display to be able to normalize the - // mouse coordinate - float width = fgGetInt("/sim/startup/xsize"); - float height = fgGetInt("/sim/startup/ysize"); - - // Compute some coordinates of in the line from the eyepoint to the - // mouse click coodinates. - // First build the normalized projection coordinates - osg::Vec4 normPt((2*x - width)/width, -(2*y - height)/height, 1, 1); - // Transform them into the real world - osg::Vec4 worldPt4 = m.preMult(normPt); - if (fabs(worldPt4[3]) < SGLimitsf::min()) + // we can get called early ... + if (!sceneView.valid()) return false; - SGVec3f worldPt(worldPt4[0]/worldPt4[3], - worldPt4[1]/worldPt4[3], - worldPt4[2]/worldPt4[3]); - // Now build a direction from the point - FGViewer* view = globals->get_current_view(); - dir = normalize(toVec3d(worldPt - SGVec3f(view->get_view_pos()))); + osg::Node* sceneData = globals->get_scenery()->get_scene_graph(); + if (!sceneData) + return false; + osg::Viewport* viewport = sceneView->getViewport(); + if (!viewport) + return false; - // Copy the start point - pt = SGVec3d(view->get_absolute_view_pos()); + // good old scenery center + SGVec3d center = globals->get_scenery()->get_center(); - // OSGFIXME: ist this sufficient??? especially the precision problems here?? -// bool mSceneView->projectWindowXYIntoObject(int x,int y,osg::Vec3& near_point,osg::Vec3& far_point) const; + // don't know why, but the update has partly happened somehow, + // so update the scneery part of the viewer + FGViewer *current_view = globals->get_current_view(); + // Force update of center dependent values ... + current_view->set_dirty(); + SGVec3d position = current_view->getViewPosition(); + SGQuatd attitude = current_view->getViewOrientation(); + SGVec3d osgPosition = attitude.transform(center - position); + mCameraView->setPosition(osgPosition.osg()); + mCameraView->setAttitude(inverse(attitude).osg()); + osg::Matrix projection(sceneView->getProjectionMatrix()); + osg::Matrix modelview(sceneView->getViewMatrix()); - return true; + osg::NodePathList nodePath = sceneData->getParentalNodePaths(); + // modify the view matrix so that it accounts for this nodePath's + // accumulated transform + if (!nodePath.empty()) + modelview.preMult(computeLocalToWorld(nodePath.front())); + + // swap the y values ... + y = viewport->height() - y; + // set up the pick visitor + osgUtil::PickVisitor pickVisitor(viewport, projection, modelview, x, y); + sceneData->accept(pickVisitor); + if (!pickVisitor.hits()) + return false; + + // collect all interaction callbacks on the pick ray. + // They get stored in the pickCallbacks list where they are sorted back + // to front and croasest to finest wrt the scenery node they are attached to + osgUtil::PickVisitor::LineSegmentHitListMap::const_iterator mi; + for (mi = pickVisitor.getSegHitList().begin(); + mi != pickVisitor.getSegHitList().end(); + ++mi) { + osgUtil::IntersectVisitor::HitList::const_iterator hi; + for (hi = mi->second.begin(); hi != mi->second.end(); ++hi) { + // ok, go back the nodes and ask for intersection callbacks, + // execute them in top down order + const osg::NodePath& np = hi->getNodePath(); + osg::NodePath::const_reverse_iterator npi; + for (npi = np.rbegin(); npi != np.rend(); ++npi) { + SGSceneUserData* ud = SGSceneUserData::getSceneUserData(*npi); + if (!ud) + continue; + for (unsigned i = 0; i < ud->getNumPickCallbacks(); ++i) { + SGPickCallback* pickCallback = ud->getPickCallback(i); + if (!pickCallback) + continue; + SGSceneryPick sceneryPick; + /// note that this is done totally in doubles instead of + /// just using getWorldIntersectionPoint + osg::Vec3d localPt = hi->getLocalIntersectPoint(); + sceneryPick.info.local = SGVec3d(localPt); + if (hi->getMatrix()) + sceneryPick.info.wgs84 = SGVec3d(localPt*(*hi->getMatrix())); + else + sceneryPick.info.wgs84 = SGVec3d(localPt); + sceneryPick.info.wgs84 += globals->get_scenery()->get_center(); + sceneryPick.callback = pickCallback; + pickList.push_back(sceneryPick); + } + } + } + } + + return !pickList.empty(); } // end of renderer.cxx diff --git a/src/Main/renderer.hxx b/src/Main/renderer.hxx index 83faba51b..b80087da6 100644 --- a/src/Main/renderer.hxx +++ b/src/Main/renderer.hxx @@ -4,6 +4,7 @@ #include #include +#include #define FG_ENABLE_MULTIPASS_CLOUDS 1 @@ -45,11 +46,10 @@ public: */ static void setNearFar( float n, float f ); - /** Get the pick start point and direction in global coordinates. - * The inputs are expected to be the x and y coordinates of the - * screen point relative to the window. + /** Just pick into the scene and return the pick callbacks on the way ... */ - static bool getPickInfo( SGVec3d& p, SGVec3d& d, unsigned x, unsigned y ); + static bool pick( unsigned x, unsigned y, + std::vector& pickList ); }; #endif diff --git a/src/Scenery/scenery.cxx b/src/Scenery/scenery.cxx index bcd7d1865..a4b32d73d 100644 --- a/src/Scenery/scenery.cxx +++ b/src/Scenery/scenery.cxx @@ -217,25 +217,6 @@ FGScenery::get_cart_elevation_m(const SGVec3d& pos, double max_altoff, return hits; } -static const osgUtil::Hit* -getNearestHit(const osgUtil::IntersectVisitor::HitList& hitList, - const SGVec3d& start) -{ - const osgUtil::Hit* nearestHit = 0; - double dist = SGLimitsd::max(); - osgUtil::IntersectVisitor::HitList::const_iterator hit; - for (hit = hitList.begin(); hit != hitList.end(); ++hit) { - SGVec3d point(hit->getWorldIntersectPoint()); - double newdist = length(start - point); - if (newdist < dist) { - dist = newdist; - nearestHit = &*hit; - } - } - - return nearestHit; -} - bool FGScenery::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir, SGVec3d& nearestHit, bool exact) @@ -289,53 +270,3 @@ FGScenery::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir, return hits; } - -void -FGScenery::pick(const SGVec3d& pos, const SGVec3d& dir, - std::vector& pickList) -{ - pickList.clear(); - - // Make really sure the direction is normalized, is really cheap compared to - // computation of ground intersection. - SGVec3d start = pos - center; - SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ??? - - osgUtil::IntersectVisitor intersectVisitor; -// osgUtil::PickVisitor intersectVisitor; -// intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT); - osg::ref_ptr lineSegment; - lineSegment = new osg::LineSegment(start.osg(), end.osg()); - intersectVisitor.addLineSegment(lineSegment.get()); - get_scene_graph()->accept(intersectVisitor); - if (!intersectVisitor.hits()) - return; - - // collect all interaction callbacks on the pick ray. - // They get stored in the pickCallbacks list where they are sorted back - // to front and croasest to finest wrt the scenery node they are attached to - osgUtil::IntersectVisitor::HitList::const_iterator hi; - for (hi = intersectVisitor.getHitList(lineSegment.get()).begin(); - hi != intersectVisitor.getHitList(lineSegment.get()).end(); - ++hi) { - - // ok, go back the nodes and ask for intersection callbacks, - // execute them in top down order - const osg::NodePath& np = hi->getNodePath(); - osg::NodePath::const_reverse_iterator npi; - for (npi = np.rbegin(); npi != np.rend(); ++npi) { - SGSceneUserData* ud = SGSceneUserData::getSceneUserData(*npi); - if (!ud) - continue; - SGPickCallback* pickCallback = ud->getPickCallback(); - if (!pickCallback) - continue; - - SGSceneryPick sceneryPick; - sceneryPick.info.wgs84 = center + SGVec3d(hi->getWorldIntersectPoint()); - sceneryPick.info.local = SGVec3d(hi->getLocalIntersectPoint()); - sceneryPick.callback = pickCallback; - pickList.push_back(sceneryPick); - } - } -} diff --git a/src/Scenery/scenery.hxx b/src/Scenery/scenery.hxx index a1cf2a77b..4229e4c58 100644 --- a/src/Scenery/scenery.hxx +++ b/src/Scenery/scenery.hxx @@ -115,8 +115,6 @@ public: /// On success, true is returned. bool get_cart_ground_intersection(const SGVec3d& start, const SGVec3d& dir, SGVec3d& nearestHit, bool exact = false); - void pick(const SGVec3d& pos, const SGVec3d& dir, - std::vector& pickList); const SGVec3d& get_center() const { return center; } void set_center( const SGVec3d& p );