X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FModel%2Fpanelnode.cxx;h=6399133f330e731e0285572442c276abd449bc5a;hb=a5e516fe8cc5775aebfe8afa0328e6f26824d1bd;hp=be6889fda55d048d80e0a63044be888fcfbb5d82;hpb=0d9f2e3c9585cfc1fbc392e56c35fda8bef8c334;p=flightgear.git diff --git a/src/Model/panelnode.cxx b/src/Model/panelnode.cxx index be6889fda..6399133f3 100644 --- a/src/Model/panelnode.cxx +++ b/src/Model/panelnode.cxx @@ -2,197 +2,438 @@ # include #endif +#include "panelnode.hxx" + +#include +#include + +#include +#include +#include + #include #include +#include -#include +#include +#include +#include +#include + +#include +#include
#include #include -#include "panelnode.hxx" +#include "Viewer/viewer.hxx" +#include "Viewer/viewmgr.hxx" using std::vector; +class PanelTransformListener : public SGPropertyChangeListener +{ +public: + PanelTransformListener(FGPanelNode* pn) : _panelNode(pn) {} + + virtual void valueChanged (SGPropertyNode * node) + { + _panelNode->dirtyBound(); + } +private: + FGPanelNode* _panelNode; +}; -// Static (!) handling for all 3D panels in the program. -// OSGFIXME: Put the panel as different elements in the scenegraph. -// Then just pick in that scenegraph. -vector all_3d_panels; -bool fgHandle3DPanelMouseEvent( int button, int updown, int x, int y ) +class PanelPathListener : public SGPropertyChangeListener { - for ( unsigned int i = 0; i < all_3d_panels.size(); i++ ) { - if ( all_3d_panels[i]->doMouseAction(button, updown, x, y) ) { - return true; - } +public: + PanelPathListener(FGPanelNode* pn) : _panelNode(pn) {} + + virtual void valueChanged (SGPropertyNode * node) + { + _panelNode->setPanelPath(node->getStringValue()); } - return false; +private: + FGPanelNode* _panelNode; +}; + +class FGPanelPickCallback : public SGPickCallback { +public: + FGPanelPickCallback(FGPanelNode* p) : + panel(p) + {} + + virtual bool buttonPressed( int b, + const osgGA::GUIEventAdapter&, + const Info& info ) + { + if (!panel->getPanel()) { + return false; + } + + button = b; + // convert to panel coordinates + osg::Matrixd m = osg::Matrixd::inverse(panel->transformMatrix()); + picked = toOsg(info.local) * m; + SG_LOG( SG_INSTR, SG_DEBUG, "panel pick: " << toSG(picked) ); + + // send to the panel + return panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_DOWN, + picked.x(), picked.y()); + } + + virtual void update(double dt, int keyModState) + { + panel->getPanel()->updateMouseDelay(dt); + } + + virtual void buttonReleased( int, + const osgGA::GUIEventAdapter&, + const Info* ) + { + panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_UP, + picked.x(), picked.y()); + } + +private: + FGPanelNode* panel; + int button; + osg::Vec3 picked; +}; + +class FGPanelSwitchCallback : public osg::NodeCallback { +public: + FGPanelSwitchCallback(FGPanelNode* pn) : + panel(pn), + visProp(fgGetNode("/sim/panel/visibility")) + { + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + assert(dynamic_cast(node)); + osg::Switch* sw = static_cast(node); + + if (!visProp->getBoolValue()) { + sw->setValue(0, false); + return; + } + + + panel->lazyLoad(); // isVisible check needs the panel loaded for auto-hide flag + bool enabled = panel->isVisible2d(); + sw->setValue(0, enabled); + if (!enabled) + return; + + traverse(node, nv); + } + +private: + FGPanelNode* panel; + SGPropertyNode_ptr visProp; +}; + + +FGPanelNode::FGPanelNode(SGPropertyNode* props) : + _is2d(false), + _resizeToViewport(false), + _listener(NULL) +{ + commonInit(); + _panelPath = props->getStringValue("path"); + + // And the corner points + SGPropertyNode* pt = props->getChild("bottom-left"); + _bottomLeft[0] = pt->getFloatValue("x-m"); + _bottomLeft[1] = pt->getFloatValue("y-m"); + _bottomLeft[2] = pt->getFloatValue("z-m"); + + pt = props->getChild("top-left"); + _topLeft[0] = pt->getFloatValue("x-m"); + _topLeft[1] = pt->getFloatValue("y-m"); + _topLeft[2] = pt->getFloatValue("z-m"); + + pt = props->getChild("bottom-right"); + _bottomRight[0] = pt->getFloatValue("x-m"); + _bottomRight[1] = pt->getFloatValue("y-m"); + _bottomRight[2] = pt->getFloatValue("z-m"); + + _depthTest = props->getBoolValue("depth-test"); } -void fgUpdate3DPanels() +FGPanelNode::FGPanelNode() : + _is2d(true), + _resizeToViewport(true), + _depthTest(false) { - for ( unsigned int i = 0; i < all_3d_panels.size(); i++ ) { - all_3d_panels[i]->getPanel()->updateMouseDelay(); + globals->get_commands()->addCommand("panel-mouse-click", this, &FGPanelNode::panelMouseClickCommand); + + SGPropertyNode* pathNode = fgGetNode("/sim/panel/path"); + _pathListener.reset(new PanelPathListener(this)); + pathNode->addChangeListener(_pathListener.get()); + setPanelPath(pathNode->getStringValue()); + + // for a 2D panel, various options adjust the transformation + // matrix. We need to pass this data on to OSG or its bounding box + // will be stale, and picking will break. + // http://code.google.com/p/flightgear-bugs/issues/detail?id=864 + _listener = new PanelTransformListener(this); + fgGetNode("/sim/panel/x-offset", true)->addChangeListener(_listener); + fgGetNode("/sim/panel/y-offset", true)->addChangeListener(_listener); + fgGetNode("/sim/startup/xsize", true)->addChangeListener(_listener); + fgGetNode("/sim/startup/ysize", true)->addChangeListener(_listener); + + commonInit(); +} + +FGPanelNode::~FGPanelNode() +{ + if (_is2d) { + globals->get_commands()->removeCommand("panel-mouse-click"); + SGPropertyNode* pathNode = fgGetNode("/sim/panel/path"); + pathNode->removeChangeListener(_pathListener.get()); + } + + if (_listener) { + fgGetNode("/sim/panel/x-offset", true)->removeChangeListener(_listener); + fgGetNode("/sim/panel/y-offset", true)->removeChangeListener(_listener); + fgGetNode("/sim/startup/xsize", true)->removeChangeListener(_listener); + fgGetNode("/sim/startup/ysize", true)->removeChangeListener(_listener); + delete _listener; } } -FGPanelNode::FGPanelNode(SGPropertyNode* props) -{ - int i; - - // Make an FGPanel object. But *don't* call init() or bind() on - // it -- those methods touch static state. - const char *path = props->getStringValue("path"); - _panel = fgReadPanel(path); - if (!_panel) - throw sg_io_exception(string("Failed to load panel ") + path); - - // Never mind. We *have* to call init to make sure the static - // state is initialized (it's not, if there aren't any 2D - // panels). This is a memory leak and should be fixed!` - // FIXME - _panel->init(); - - _panel->setDepthTest( props->getBoolValue("depth-test") ); - - // Read out the pixel-space info - _xmax = _panel->getWidth(); - _ymax = _panel->getHeight(); - - // And the corner points - SGPropertyNode* pt = props->getChild("bottom-left"); - _bottomLeft[0] = pt->getFloatValue("x-m"); - _bottomLeft[1] = pt->getFloatValue("y-m"); - _bottomLeft[2] = pt->getFloatValue("z-m"); - - pt = props->getChild("top-left"); - _topLeft[0] = pt->getFloatValue("x-m"); - _topLeft[1] = pt->getFloatValue("y-m"); - _topLeft[2] = pt->getFloatValue("z-m"); - - pt = props->getChild("bottom-right"); - _bottomRight[0] = pt->getFloatValue("x-m"); - _bottomRight[1] = pt->getFloatValue("y-m"); - _bottomRight[2] = pt->getFloatValue("z-m"); - - // Now generate our transformation matrix. For shorthand, use - // "a", "b", and "c" as our corners and "m" as the matrix. The - // vector u goes from a to b, v from a to c, and w is a - // perpendicular cross product. - osg::Vec3 a = _bottomLeft; - osg::Vec3 b = _bottomRight; - osg::Vec3 c = _topLeft; - osg::Vec3 u = b - a; - osg::Vec3 v = c - a; - osg::Vec3 w = u^v; +void FGPanelNode::setPanelPath(const std::string& panel) +{ + if (panel == _panelPath) { + return; + } + + _panelPath = panel; + if (_panel) { + _panel.clear(); + } +} - osg::Matrix& m = _xform; - // Now generate a trivial basis transformation matrix. If we want - // to map the three unit vectors to three arbitrary vectors U, V, - // and W, then those just become the columns of the 3x3 matrix. - m(0,0) = u[0]; m(1,0) = v[0]; m(2,0) = w[0]; m(3,0) = a[0];// |Ux Vx Wx| - m(0,1) = u[1]; m(1,1) = v[1]; m(2,1) = w[1]; m(3,1) = a[1];//m = |Uy Vy Wy| - m(0,2) = u[2]; m(1,2) = v[2]; m(2,2) = w[2]; m(3,2) = a[2];// |Uz Vz Wz| - m(0,3) = 0; m(1,3) = 0; m(2,3) = 0; m(3,3) = 1; - - // The above matrix maps the unit (!) square to the panel - // rectangle. Postmultiply scaling factors that match the - // pixel-space size of the panel. - for(i=0; i<4; ++i) { - m(0,i) *= 1.0/_xmax; - m(1,i) *= 1.0/_ymax; +void FGPanelNode::lazyLoad() +{ + if (!_panelPath.empty() && !_panel) { + _panel = fgReadPanel(_panelPath); + if (!_panel) { + SG_LOG(SG_COCKPIT, SG_WARN, "failed to read panel from:" << _panelPath); + _panelPath = std::string(); // don't keep trying to read + return; } + + _panel->setDepthTest(_depthTest); + initWithPanel(); + } +} - _lastViewport[0] = 0; - _lastViewport[1] = 0; - _lastViewport[2] = 0; - _lastViewport[3] = 0; +void FGPanelNode::commonInit() +{ + setUseDisplayList(false); + setDataVariance(Object::DYNAMIC); + getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); +} - dirtyBound(); +void FGPanelNode::initWithPanel() +{ + int i; + + // Read out the pixel-space info + float panelWidth = _panel->getWidth(); + float panelHeight = _panel->getHeight(); + + _panel->getLogicalExtent(_xmin, _ymin, _xmax, _ymax); + + // Now generate our transformation matrix. For shorthand, use + // "a", "b", and "c" as our corners and "m" as the matrix. The + // vector u goes from a to b, v from a to c, and w is a + // perpendicular cross product. + osg::Vec3 a = _bottomLeft; + osg::Vec3 b = _bottomRight; + osg::Vec3 c = _topLeft; + osg::Vec3 u = b - a; + osg::Vec3 v = c - a; + osg::Vec3 w = u^v; - // All done. Add us to the list - all_3d_panels.push_back(this); + osg::Matrix& m = _xform; + if ((u.length2() == 0.0) || (b.length2() == 0.0)) { + m.makeIdentity(); + } else { + // Now generate a trivial basis transformation matrix. If we want + // to map the three unit vectors to three arbitrary vectors U, V, + // and W, then those just become the columns of the 3x3 matrix. + m(0,0) = u[0]; m(1,0) = v[0]; m(2,0) = w[0]; m(3,0) = a[0];// |Ux Vx Wx| + m(0,1) = u[1]; m(1,1) = v[1]; m(2,1) = w[1]; m(3,1) = a[1];//m = |Uy Vy Wy| + m(0,2) = u[2]; m(1,2) = v[2]; m(2,2) = w[2]; m(3,2) = a[2];// |Uz Vz Wz| + m(0,3) = 0; m(1,3) = 0; m(2,3) = 0; m(3,3) = 1; + } + + // The above matrix maps the unit (!) square to the panel + // rectangle. Postmultiply scaling factors that match the + // pixel-space size of the panel. + for(i=0; i<4; ++i) { + m(0,i) *= 1.0/panelWidth; + m(1,i) *= 1.0/panelHeight; + } - setUseDisplayList(false); - getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); + dirtyBound(); } -FGPanelNode::~FGPanelNode() +osg::Matrix FGPanelNode::transformMatrix() const { - delete _panel; + if (!_panel) { + return osg::Matrix(); + } + + if (!_resizeToViewport) { + return _xform; + } + + double s = _panel->getAspectScale(); + osg::Matrix m = osg::Matrix::scale(s, s, 1.0); + m *= osg::Matrix::translate(_panel->getXOffset(), _panel->getYOffset(), 0.0); + + return m; } void FGPanelNode::drawImplementation(osg::State& state) const -{ +{ + if (!_panel) { + return; + } + osg::ref_ptr mv = new osg::RefMatrix; - mv->set(_xform*state.getModelViewMatrix()); + mv->set(transformMatrix() * state.getModelViewMatrix()); state.applyModelViewMatrix(mv.get()); - // Grab the matrix state, so that we can get back from screen - // coordinates to panel coordinates when the user clicks the - // mouse. - // OSGFIXME: we don't need that when we can really pick - _lastModelview = state.getModelViewMatrix(); - _lastProjection = state.getProjectionMatrix(); - - const osg::Viewport* vp = state.getCurrentViewport(); - _lastViewport[0] = vp->x(); - _lastViewport[1] = vp->y(); - _lastViewport[2] = vp->width(); - _lastViewport[3] = vp->height(); - _panel->draw(state); } osg::BoundingBox -FGPanelNode::computeBound() const +#if OSG_VERSION_LESS_THAN(3,3,2) +FGPanelNode::computeBound() +#else +FGPanelNode::computeBoundingBox() +#endif +const { - osg::BoundingBox bb; - bb.expandBy(_bottomLeft); - bb.expandBy(_bottomRight); - bb.expandBy(_topLeft); - return bb; + + osg::Vec3 coords[3]; + osg::Matrix m(transformMatrix()); + coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0)); + coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0)); + coords[2] = m.preMult(osg::Vec3(_xmin,_ymax,0)); + + osg::BoundingBox bb; + bb.expandBy(coords[0]); + bb.expandBy(coords[1]); + bb.expandBy(coords[2]); + return bb; } -bool FGPanelNode::doMouseAction(int button, int updown, int x, int y) +void FGPanelNode::accept(osg::PrimitiveFunctor& functor) const { - if (_lastViewport[2] == 0 || _lastViewport[3] == 0) { - // we haven't been drawn yet, presumably - return false; - } - - // Covert the screen coordinates to viewport coordinates in the - // range [0:1], then transform to OpenGL "post projection" coords - // in [-1:1]. Remember the difference in Y direction! - float vx = (x + 0.5 - _lastViewport[0]) / _lastViewport[2]; - float vy = (y + 0.5 - _lastViewport[1]) / _lastViewport[3]; - vx = 2*vx - 1; - vy = 1 - 2*vy; + osg::Vec3 coords[4]; + osg::Matrix m(transformMatrix()); + + coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0)); + coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0)); + coords[2] = m.preMult(osg::Vec3(_xmax, _ymax, 0)); + coords[3] = m.preMult(osg::Vec3(_xmin,_ymax,0)); - // Make two vectors in post-projection coordinates at the given - // screen, one in the near field and one in the far field. - osg::Vec3 a, b; - a[0] = b[0] = vx; - a[1] = b[1] = vy; - a[2] = 0.75; // "Near" Z value - b[2] = -0.75; // "Far" Z value + functor.setVertexArray(4, coords); + functor.drawArrays( GL_QUADS, 0, 4); +} - // Run both vectors "backwards" through the OpenGL matrix - // transformation. Remember to w-normalize the vectors! - osg::Matrix m = _lastModelview*_lastProjection; - m = osg::Matrix::inverse(m); +bool FGPanelNode::isVisible2d() const +{ + if (!_panel) { + return false; + } + + if (!_hideNonDefaultViews) { + _hideNonDefaultViews = fgGetNode("/sim/panel/hide-nonzero-view", true); + } + + if (_hideNonDefaultViews->getBoolValue()) { + if (globals->get_viewmgr()->get_current() != 0) { + return false; + } + } + + if (!_autoHide2d) { + _autoHide2d = fgGetNode("/sim/panel/hide-nonzero-heading-offset", true); + } + + if (_panel->getAutohide() && _autoHide2d->getBoolValue()) { + if (!globals->get_current_view()) { + return false; + } + + return globals->get_current_view()->getHeadingOffset_deg() == 0; + } + + return true; +} - a = m.preMult(a); - b = m.preMult(b); +static osg::Node* createGeode(FGPanelNode* panel) +{ + osg::Geode* geode = new osg::Geode; + geode->addDrawable(panel); + + geode->setNodeMask(SG_NODEMASK_PICK_BIT | SG_NODEMASK_2DPANEL_BIT); + + SGSceneUserData* userData; + userData = SGSceneUserData::getOrCreateSceneUserData(geode); + userData->setPickCallback(new FGPanelPickCallback(panel)); + return geode; +} - // And find their intersection on the z=0 plane. The resulting X - // and Y coordinates are the hit location in panel coordinates. - float dxdz = (b[0] - a[0]) / (b[2] - a[2]); - float dydz = (b[1] - a[1]) / (b[2] - a[2]); - int panelX = (int)(a[0] - a[2]*dxdz + 0.5); - int panelY = (int)(a[1] - a[2]*dydz + 0.5); +osg::Node* FGPanelNode::create2DPanelNode() +{ + FGPanelNode* drawable = new FGPanelNode; + + osg::Switch* ps = new osg::Switch; + osg::StateSet* stateSet = ps->getOrCreateStateSet(); + stateSet->setRenderBinDetails(1000, "RenderBin"); + ps->addChild(createGeode(drawable)); + + // speed optimization? + stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA)); + stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + stateSet->setMode(GL_FOG, osg::StateAttribute::OFF); + stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + + ps->setUpdateCallback(new FGPanelSwitchCallback(drawable)); + return ps; +} - return _panel->doLocalMouseAction(button, updown, panelX, panelY); +osg::Node* FGPanelNode::load(SGPropertyNode *n) +{ + FGPanelNode* drawable = new FGPanelNode(n); + drawable->lazyLoad(); // force load now for 2.5D panels + return createGeode(drawable); } +/** + * Built-in command: pass a mouse click to the panel. + * + * button: the mouse button number, zero-based. + * is-down: true if the button is down, false if it is up. + * x-pos: the x position of the mouse click. + * y-pos: the y position of the mouse click. + */ +bool +FGPanelNode::panelMouseClickCommand(const SGPropertyNode * arg) +{ + return _panel->doMouseAction(arg->getIntValue("button"), + arg->getBoolValue("is-down") ? PU_DOWN : PU_UP, + arg->getIntValue("x-pos"), + arg->getIntValue("y-pos")); +}