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"
32 static FGPanelNode* global_panel = NULL;
35 * Built-in command: pass a mouse click to the panel.
37 * button: the mouse button number, zero-based.
38 * is-down: true if the button is down, false if it is up.
39 * x-pos: the x position of the mouse click.
40 * y-pos: the y position of the mouse click.
43 do_panel_mouse_click (const SGPropertyNode * arg)
46 return global_panel->getPanel()
47 ->doMouseAction(arg->getIntValue("button"),
48 arg->getBoolValue("is-down") ? PU_DOWN : PU_UP,
49 arg->getIntValue("x-pos"),
50 arg->getIntValue("y-pos"));
57 class FGPanelPickCallback : public SGPickCallback {
59 FGPanelPickCallback(FGPanelNode* p) :
63 virtual bool buttonPressed(int b, const Info& info)
66 // convert to panel coordinates
67 osg::Matrixd m = osg::Matrixd::inverse(panel->transformMatrix());
68 picked = toOsg(info.local) * m;
69 SG_LOG( SG_INSTR, SG_DEBUG, "panel pick: " << toSG(picked) );
72 return panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_DOWN,
73 picked.x(), picked.y());
76 virtual void update(double dt)
78 panel->getPanel()->updateMouseDelay(dt);
81 virtual void buttonReleased(void)
83 panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_UP,
84 picked.x(), picked.y());
93 class FGPanelSwitchCallback : public osg::NodeCallback {
95 FGPanelSwitchCallback(FGPanelNode* pn) :
97 visProp(fgGetNode("/sim/panel/visibility"))
101 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
103 assert(dynamic_cast<osg::Switch*>(node));
104 osg::Switch* sw = static_cast<osg::Switch*>(node);
106 if (!visProp->getBoolValue()) {
107 sw->setValue(0, false);
112 panel->lazyLoad(); // isVisible check needs the panel loaded for auto-hide flag
113 bool enabled = panel->isVisible2d();
114 sw->setValue(0, enabled);
123 SGPropertyNode* visProp;
127 FGPanelNode::FGPanelNode(SGPropertyNode* props) :
128 _resizeToViewport(false)
131 _panelPath = props->getStringValue("path");
133 // And the corner points
134 SGPropertyNode* pt = props->getChild("bottom-left");
135 _bottomLeft[0] = pt->getFloatValue("x-m");
136 _bottomLeft[1] = pt->getFloatValue("y-m");
137 _bottomLeft[2] = pt->getFloatValue("z-m");
139 pt = props->getChild("top-left");
140 _topLeft[0] = pt->getFloatValue("x-m");
141 _topLeft[1] = pt->getFloatValue("y-m");
142 _topLeft[2] = pt->getFloatValue("z-m");
144 pt = props->getChild("bottom-right");
145 _bottomRight[0] = pt->getFloatValue("x-m");
146 _bottomRight[1] = pt->getFloatValue("y-m");
147 _bottomRight[2] = pt->getFloatValue("z-m");
149 _depthTest = props->getBoolValue("depth-test");
152 FGPanelNode::FGPanelNode() :
153 _resizeToViewport(true),
159 void FGPanelNode::setPanelPath(const std::string& panel)
161 if (panel == _panelPath) {
171 void FGPanelNode::lazyLoad()
173 if (!_panelPath.empty() && !_panel) {
174 _panel = fgReadPanel(_panelPath);
176 SG_LOG(SG_COCKPIT, SG_WARN, "failed to read panel from:" << _panelPath);
177 _panelPath = string(); // don't keep trying to read
181 _panel->setDepthTest(_depthTest);
186 void FGPanelNode::commonInit()
188 setUseDisplayList(false);
189 setDataVariance(Object::DYNAMIC);
190 getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
191 getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
194 void FGPanelNode::initWithPanel()
198 // Read out the pixel-space info
199 float panelWidth = _panel->getWidth();
200 float panelHeight = _panel->getHeight();
202 _panel->getLogicalExtent(_xmin, _ymin, _xmax, _ymax);
204 // Now generate our transformation matrix. For shorthand, use
205 // "a", "b", and "c" as our corners and "m" as the matrix. The
206 // vector u goes from a to b, v from a to c, and w is a
207 // perpendicular cross product.
208 osg::Vec3 a = _bottomLeft;
209 osg::Vec3 b = _bottomRight;
210 osg::Vec3 c = _topLeft;
215 osg::Matrix& m = _xform;
216 // Now generate a trivial basis transformation matrix. If we want
217 // to map the three unit vectors to three arbitrary vectors U, V,
218 // and W, then those just become the columns of the 3x3 matrix.
219 m(0,0) = u[0]; m(1,0) = v[0]; m(2,0) = w[0]; m(3,0) = a[0];// |Ux Vx Wx|
220 m(0,1) = u[1]; m(1,1) = v[1]; m(2,1) = w[1]; m(3,1) = a[1];//m = |Uy Vy Wy|
221 m(0,2) = u[2]; m(1,2) = v[2]; m(2,2) = w[2]; m(3,2) = a[2];// |Uz Vz Wz|
222 m(0,3) = 0; m(1,3) = 0; m(2,3) = 0; m(3,3) = 1;
224 // The above matrix maps the unit (!) square to the panel
225 // rectangle. Postmultiply scaling factors that match the
226 // pixel-space size of the panel.
228 m(0,i) *= 1.0/panelWidth;
229 m(1,i) *= 1.0/panelHeight;
235 FGPanelNode::~FGPanelNode()
239 osg::Matrix FGPanelNode::transformMatrix() const
245 if (!_resizeToViewport) {
249 double s = _panel->getAspectScale();
250 osg::Matrix m = osg::Matrix::scale(s, s, 1.0);
251 m *= osg::Matrix::translate(_panel->getXOffset(), _panel->getYOffset(), 0.0);
257 FGPanelNode::drawImplementation(osg::State& state) const
263 osg::ref_ptr<osg::RefMatrix> mv = new osg::RefMatrix;
264 mv->set(transformMatrix() * state.getModelViewMatrix());
265 state.applyModelViewMatrix(mv.get());
271 FGPanelNode::computeBound() const
275 osg::Matrix m(transformMatrix());
276 coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0));
277 coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0));
278 coords[2] = m.preMult(osg::Vec3(_xmin,_ymax,0));
281 bb.expandBy(coords[0]);
282 bb.expandBy(coords[1]);
283 bb.expandBy(coords[2]);
287 void FGPanelNode::accept(osg::PrimitiveFunctor& functor) const
290 osg::Matrix m(transformMatrix());
292 coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0));
293 coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0));
294 coords[2] = m.preMult(osg::Vec3(_xmax, _ymax, 0));
295 coords[3] = m.preMult(osg::Vec3(_xmin,_ymax,0));
297 functor.setVertexArray(4, coords);
298 functor.drawArrays( GL_QUADS, 0, 4);
301 bool FGPanelNode::isVisible2d() const
307 if (_panel->getAutohide()) {
308 if (!globals->get_current_view()) {
312 return globals->get_current_view()->getHeadingOffset_deg() == 0;
318 static osg::Node* createGeode(FGPanelNode* panel)
320 osg::Geode* geode = new osg::Geode;
321 geode->addDrawable(panel);
323 geode->setNodeMask(SG_NODEMASK_PICK_BIT | SG_NODEMASK_2DPANEL_BIT);
325 SGSceneUserData* userData;
326 userData = SGSceneUserData::getOrCreateSceneUserData(geode);
327 userData->setPickCallback(new FGPanelPickCallback(panel));
331 class PanelPathObserver : public SGPropertyChangeListener
334 PanelPathObserver(FGPanelNode* pn) : _panelNode(pn) {}
336 virtual void valueChanged (SGPropertyNode * node)
338 _panelNode->setPanelPath(node->getStringValue());
341 FGPanelNode* _panelNode;
344 osg::Node* FGPanelNode::create2DPanelNode()
346 SGCommandMgr::instance()->addCommand("panel-mouse-click", do_panel_mouse_click);
348 SGPropertyNode* pathNode = fgGetNode("/sim/panel/path");
350 FGPanelNode* drawable = new FGPanelNode();
351 // need a global to keep the panel_mouse_click command working, sadly
352 global_panel = drawable;
354 PanelPathObserver* ppo = new PanelPathObserver(drawable);
355 pathNode->addChangeListener(ppo);
356 drawable->setPanelPath(pathNode->getStringValue());
358 osg::Switch* ps = new osg::Switch;
359 osg::StateSet* stateSet = ps->getOrCreateStateSet();
360 stateSet->setRenderBinDetails(1000, "RenderBin");
361 ps->addChild(createGeode(drawable));
363 // speed optimization?
364 stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
365 stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
366 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
367 stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
368 stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
370 ps->setUpdateCallback(new FGPanelSwitchCallback(drawable));
374 osg::Node* FGPanelNode::load(SGPropertyNode *n)
376 FGPanelNode* drawable = new FGPanelNode(n);
377 drawable->lazyLoad(); // force load now for 2.5D panels
378 return createGeode(drawable);