X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FModel%2Fpanelnode.cxx;h=6399133f330e731e0285572442c276abd449bc5a;hb=a5e516fe8cc5775aebfe8afa0328e6f26824d1bd;hp=5b979f6aa79e27157c4372aa15ffb4a07f5e59a4;hpb=b3bb53b67f31905b7f36450636696d839c94a755;p=flightgear.git diff --git a/src/Model/panelnode.cxx b/src/Model/panelnode.cxx index 5b979f6aa..6399133f3 100644 --- a/src/Model/panelnode.cxx +++ b/src/Model/panelnode.cxx @@ -2,103 +2,438 @@ # include #endif -#include
+#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; -FGPanelNode::FGPanelNode(SGPropertyNode* props) -{ - // Make an FGPanel object. But *don't* call init() or bind() on - // it -- those methods touch static state. - _panel = fgReadPanel(props->getStringValue("path")); - - // 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 out 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. - float *a = _bottomLeft, *b = _bottomRight, *c = _topLeft, *m = _xform; - float u[3], v[3], w[3]; - int i; - for(i=0; i<3; i++) u[i] = b[i] - a[i]; // U = B - A - for(i=0; i<3; i++) v[i] = c[i] - a[i]; // V = C - A - - w[0] = u[1]*v[2] - v[1]*u[2]; // W = U x V - w[1] = u[2]*v[0] - v[2]*u[0]; - w[2] = u[0]*v[1] - v[0]*u[1]; - - // 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] = u[0]; m[4] = v[0]; m[8] = w[0]; m[12] = a[0]; // |Ux Vx Wx| - m[1] = u[1]; m[5] = v[1]; m[9] = w[1]; m[13] = a[1]; // m = |Uy Vy Wy| - m[2] = u[2]; m[6] = v[2]; m[10] = w[2]; m[14] = a[2]; // |Uz Vz Wz| - m[3] = 0; m[7] = 0; m[11] = 0; m[15] = 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[4+i] *= 1.0/_ymax; +class PanelTransformListener : public SGPropertyChangeListener +{ +public: + PanelTransformListener(FGPanelNode* pn) : _panelNode(pn) {} + + virtual void valueChanged (SGPropertyNode * node) + { + _panelNode->dirtyBound(); + } +private: + FGPanelNode* _panelNode; +}; + +class PanelPathListener : public SGPropertyChangeListener +{ +public: + PanelPathListener(FGPanelNode* pn) : _panelNode(pn) {} + + virtual void valueChanged (SGPropertyNode * node) + { + _panelNode->setPanelPath(node->getStringValue()); } +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; +}; + - // Now plib initialization. The bounding sphere is defined nicely - // by our corner points: - float cx = (b[0]+c[0])/2; - float cy = (b[1]+c[1])/2; - float cz = (b[2]+c[2])/2; - float r = sqrt((cx-a[0])*(cx-a[0]) + - (cy-a[1])*(cy-a[1]) + - (cz-a[2])*(cz-a[2])); - bsphere.setCenter(cx, cy, cz); - bsphere.setRadius(r); +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"); +} + +FGPanelNode::FGPanelNode() : + _is2d(true), + _resizeToViewport(true), + _depthTest(false) +{ + 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() { - delete _panel; + 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; + } } -void FGPanelNode::draw() +void FGPanelNode::setPanelPath(const std::string& panel) { - // What's the difference? - draw_geometry(); + if (panel == _panelPath) { + return; + } + + _panelPath = panel; + if (_panel) { + _panel.clear(); + } } -void FGPanelNode::draw_geometry() +void FGPanelNode::lazyLoad() { - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glMultMatrixf(_xform); - _panel->draw(); - glPopMatrix(); + 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(); + } } -void FGPanelNode::die() +void FGPanelNode::commonInit() { - SG_LOG(SG_ALL,SG_ALERT,"Unimplemented function called on FGPanelNode"); - *(int*)0=0; + setUseDisplayList(false); + setDataVariance(Object::DYNAMIC); + getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); +} + +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; + + 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; + } + + dirtyBound(); } +osg::Matrix FGPanelNode::transformMatrix() const +{ + 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(transformMatrix() * state.getModelViewMatrix()); + state.applyModelViewMatrix(mv.get()); + + _panel->draw(state); +} + +osg::BoundingBox +#if OSG_VERSION_LESS_THAN(3,3,2) +FGPanelNode::computeBound() +#else +FGPanelNode::computeBoundingBox() +#endif +const +{ + + 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; +} + +void FGPanelNode::accept(osg::PrimitiveFunctor& functor) const +{ + 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)); + + functor.setVertexArray(4, coords); + functor.drawArrays( GL_QUADS, 0, 4); +} + +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; +} + +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; +} + +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; +} + +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")); +}