5 #include "panelnode.hxx"
12 #include <osg/BlendFunc>
14 #include <simgear/compiler.h>
15 #include <simgear/structure/exception.hxx>
16 #include <simgear/debug/logstream.hxx>
18 #include <simgear/scene/util/OsgMath.hxx>
19 #include <simgear/scene/util/SGPickCallback.hxx>
20 #include <simgear/scene/util/SGSceneUserData.hxx>
21 #include <simgear/scene/util/SGNodeMasks.hxx>
25 #include <Main/fg_os.hxx>
26 #include <Cockpit/panel.hxx>
27 #include <Cockpit/panel_io.hxx>
28 #include "Viewer/viewer.hxx"
29 #include "Viewer/viewmgr.hxx"
33 class PanelTransformListener : public SGPropertyChangeListener
36 PanelTransformListener(FGPanelNode* pn) : _panelNode(pn) {}
38 virtual void valueChanged (SGPropertyNode * node)
40 _panelNode->dirtyBound();
43 FGPanelNode* _panelNode;
46 class PanelPathListener : public SGPropertyChangeListener
49 PanelPathListener(FGPanelNode* pn) : _panelNode(pn) {}
51 virtual void valueChanged (SGPropertyNode * node)
53 _panelNode->setPanelPath(node->getStringValue());
56 FGPanelNode* _panelNode;
59 class FGPanelPickCallback : public SGPickCallback {
61 FGPanelPickCallback(FGPanelNode* p) :
65 virtual bool buttonPressed( int b,
66 const osgGA::GUIEventAdapter&,
69 if (!panel->getPanel()) {
74 // convert to panel coordinates
75 osg::Matrixd m = osg::Matrixd::inverse(panel->transformMatrix());
76 picked = toOsg(info.local) * m;
77 SG_LOG( SG_INSTR, SG_DEBUG, "panel pick: " << toSG(picked) );
80 return panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_DOWN,
81 picked.x(), picked.y());
84 virtual void update(double dt, int keyModState)
86 panel->getPanel()->updateMouseDelay(dt);
89 virtual void buttonReleased( int,
90 const osgGA::GUIEventAdapter&,
93 panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_UP,
94 picked.x(), picked.y());
103 class FGPanelSwitchCallback : public osg::NodeCallback {
105 FGPanelSwitchCallback(FGPanelNode* pn) :
107 visProp(fgGetNode("/sim/panel/visibility"))
111 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
113 assert(dynamic_cast<osg::Switch*>(node));
114 osg::Switch* sw = static_cast<osg::Switch*>(node);
116 if (!visProp->getBoolValue()) {
117 sw->setValue(0, false);
122 panel->lazyLoad(); // isVisible check needs the panel loaded for auto-hide flag
123 bool enabled = panel->isVisible2d();
124 sw->setValue(0, enabled);
133 SGPropertyNode_ptr visProp;
137 FGPanelNode::FGPanelNode(SGPropertyNode* props) :
139 _resizeToViewport(false),
143 _panelPath = props->getStringValue("path");
145 // And the corner points
146 SGPropertyNode* pt = props->getChild("bottom-left");
147 _bottomLeft[0] = pt->getFloatValue("x-m");
148 _bottomLeft[1] = pt->getFloatValue("y-m");
149 _bottomLeft[2] = pt->getFloatValue("z-m");
151 pt = props->getChild("top-left");
152 _topLeft[0] = pt->getFloatValue("x-m");
153 _topLeft[1] = pt->getFloatValue("y-m");
154 _topLeft[2] = pt->getFloatValue("z-m");
156 pt = props->getChild("bottom-right");
157 _bottomRight[0] = pt->getFloatValue("x-m");
158 _bottomRight[1] = pt->getFloatValue("y-m");
159 _bottomRight[2] = pt->getFloatValue("z-m");
161 _depthTest = props->getBoolValue("depth-test");
164 FGPanelNode::FGPanelNode() :
166 _resizeToViewport(true),
169 globals->get_commands()->addCommand("panel-mouse-click", this, &FGPanelNode::panelMouseClickCommand);
171 SGPropertyNode* pathNode = fgGetNode("/sim/panel/path");
172 _pathListener.reset(new PanelPathListener(this));
173 pathNode->addChangeListener(_pathListener.get());
174 setPanelPath(pathNode->getStringValue());
176 // for a 2D panel, various options adjust the transformation
177 // matrix. We need to pass this data on to OSG or its bounding box
178 // will be stale, and picking will break.
179 // http://code.google.com/p/flightgear-bugs/issues/detail?id=864
180 _listener = new PanelTransformListener(this);
181 fgGetNode("/sim/panel/x-offset", true)->addChangeListener(_listener);
182 fgGetNode("/sim/panel/y-offset", true)->addChangeListener(_listener);
183 fgGetNode("/sim/startup/xsize", true)->addChangeListener(_listener);
184 fgGetNode("/sim/startup/ysize", true)->addChangeListener(_listener);
189 FGPanelNode::~FGPanelNode()
192 globals->get_commands()->removeCommand("panel-mouse-click");
193 SGPropertyNode* pathNode = fgGetNode("/sim/panel/path");
194 pathNode->removeChangeListener(_pathListener.get());
198 fgGetNode("/sim/panel/x-offset", true)->removeChangeListener(_listener);
199 fgGetNode("/sim/panel/y-offset", true)->removeChangeListener(_listener);
200 fgGetNode("/sim/startup/xsize", true)->removeChangeListener(_listener);
201 fgGetNode("/sim/startup/ysize", true)->removeChangeListener(_listener);
206 void FGPanelNode::setPanelPath(const std::string& panel)
208 if (panel == _panelPath) {
218 void FGPanelNode::lazyLoad()
220 if (!_panelPath.empty() && !_panel) {
221 _panel = fgReadPanel(_panelPath);
223 SG_LOG(SG_COCKPIT, SG_WARN, "failed to read panel from:" << _panelPath);
224 _panelPath = std::string(); // don't keep trying to read
228 _panel->setDepthTest(_depthTest);
233 void FGPanelNode::commonInit()
235 setUseDisplayList(false);
236 setDataVariance(Object::DYNAMIC);
237 getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
238 getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
241 void FGPanelNode::initWithPanel()
245 // Read out the pixel-space info
246 float panelWidth = _panel->getWidth();
247 float panelHeight = _panel->getHeight();
249 _panel->getLogicalExtent(_xmin, _ymin, _xmax, _ymax);
251 // Now generate our transformation matrix. For shorthand, use
252 // "a", "b", and "c" as our corners and "m" as the matrix. The
253 // vector u goes from a to b, v from a to c, and w is a
254 // perpendicular cross product.
255 osg::Vec3 a = _bottomLeft;
256 osg::Vec3 b = _bottomRight;
257 osg::Vec3 c = _topLeft;
262 osg::Matrix& m = _xform;
263 if ((u.length2() == 0.0) || (b.length2() == 0.0)) {
266 // Now generate a trivial basis transformation matrix. If we want
267 // to map the three unit vectors to three arbitrary vectors U, V,
268 // and W, then those just become the columns of the 3x3 matrix.
269 m(0,0) = u[0]; m(1,0) = v[0]; m(2,0) = w[0]; m(3,0) = a[0];// |Ux Vx Wx|
270 m(0,1) = u[1]; m(1,1) = v[1]; m(2,1) = w[1]; m(3,1) = a[1];//m = |Uy Vy Wy|
271 m(0,2) = u[2]; m(1,2) = v[2]; m(2,2) = w[2]; m(3,2) = a[2];// |Uz Vz Wz|
272 m(0,3) = 0; m(1,3) = 0; m(2,3) = 0; m(3,3) = 1;
275 // The above matrix maps the unit (!) square to the panel
276 // rectangle. Postmultiply scaling factors that match the
277 // pixel-space size of the panel.
279 m(0,i) *= 1.0/panelWidth;
280 m(1,i) *= 1.0/panelHeight;
286 osg::Matrix FGPanelNode::transformMatrix() const
289 return osg::Matrix();
292 if (!_resizeToViewport) {
296 double s = _panel->getAspectScale();
297 osg::Matrix m = osg::Matrix::scale(s, s, 1.0);
298 m *= osg::Matrix::translate(_panel->getXOffset(), _panel->getYOffset(), 0.0);
304 FGPanelNode::drawImplementation(osg::State& state) const
310 osg::ref_ptr<osg::RefMatrix> mv = new osg::RefMatrix;
311 mv->set(transformMatrix() * state.getModelViewMatrix());
312 state.applyModelViewMatrix(mv.get());
318 #if OSG_VERSION_LESS_THAN(3,3,2)
319 FGPanelNode::computeBound()
321 FGPanelNode::computeBoundingBox()
327 osg::Matrix m(transformMatrix());
328 coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0));
329 coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0));
330 coords[2] = m.preMult(osg::Vec3(_xmin,_ymax,0));
333 bb.expandBy(coords[0]);
334 bb.expandBy(coords[1]);
335 bb.expandBy(coords[2]);
339 void FGPanelNode::accept(osg::PrimitiveFunctor& functor) const
342 osg::Matrix m(transformMatrix());
344 coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0));
345 coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0));
346 coords[2] = m.preMult(osg::Vec3(_xmax, _ymax, 0));
347 coords[3] = m.preMult(osg::Vec3(_xmin,_ymax,0));
349 functor.setVertexArray(4, coords);
350 functor.drawArrays( GL_QUADS, 0, 4);
353 bool FGPanelNode::isVisible2d() const
359 if (!_hideNonDefaultViews) {
360 _hideNonDefaultViews = fgGetNode("/sim/panel/hide-nonzero-view", true);
363 if (_hideNonDefaultViews->getBoolValue()) {
364 if (globals->get_viewmgr()->get_current() != 0) {
370 _autoHide2d = fgGetNode("/sim/panel/hide-nonzero-heading-offset", true);
373 if (_panel->getAutohide() && _autoHide2d->getBoolValue()) {
374 if (!globals->get_current_view()) {
378 return globals->get_current_view()->getHeadingOffset_deg() == 0;
384 static osg::Node* createGeode(FGPanelNode* panel)
386 osg::Geode* geode = new osg::Geode;
387 geode->addDrawable(panel);
389 geode->setNodeMask(SG_NODEMASK_PICK_BIT | SG_NODEMASK_2DPANEL_BIT);
391 SGSceneUserData* userData;
392 userData = SGSceneUserData::getOrCreateSceneUserData(geode);
393 userData->setPickCallback(new FGPanelPickCallback(panel));
397 osg::Node* FGPanelNode::create2DPanelNode()
399 FGPanelNode* drawable = new FGPanelNode;
401 osg::Switch* ps = new osg::Switch;
402 osg::StateSet* stateSet = ps->getOrCreateStateSet();
403 stateSet->setRenderBinDetails(1000, "RenderBin");
404 ps->addChild(createGeode(drawable));
406 // speed optimization?
407 stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
408 stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
409 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
410 stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
411 stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
413 ps->setUpdateCallback(new FGPanelSwitchCallback(drawable));
417 osg::Node* FGPanelNode::load(SGPropertyNode *n)
419 FGPanelNode* drawable = new FGPanelNode(n);
420 drawable->lazyLoad(); // force load now for 2.5D panels
421 return createGeode(drawable);
425 * Built-in command: pass a mouse click to the panel.
427 * button: the mouse button number, zero-based.
428 * is-down: true if the button is down, false if it is up.
429 * x-pos: the x position of the mouse click.
430 * y-pos: the y position of the mouse click.
433 FGPanelNode::panelMouseClickCommand(const SGPropertyNode * arg)
435 return _panel->doMouseAction(arg->getIntValue("button"),
436 arg->getBoolValue("is-down") ? PU_DOWN : PU_UP,
437 arg->getIntValue("x-pos"),
438 arg->getIntValue("y-pos"));