]> git.mxchange.org Git - flightgear.git/blob - src/Model/panelnode.cxx
Remove dependency on command-mgr singleton
[flightgear.git] / src / Model / panelnode.cxx
1 #ifdef HAVE_CONFIG_H
2 #  include <config.h>
3 #endif
4
5 #include "panelnode.hxx"
6
7 #include <vector>
8 #include <algorithm>
9
10 #include <osg/Geode>
11 #include <osg/Switch>
12 #include <osg/BlendFunc>
13
14 #include <simgear/compiler.h>
15 #include <simgear/structure/exception.hxx>
16 #include <simgear/debug/logstream.hxx>
17
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>
22
23 #include <plib/pu.h>
24
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"
30
31 using std::vector;
32
33 class PanelTransformListener : public SGPropertyChangeListener
34 {
35 public:
36     PanelTransformListener(FGPanelNode* pn) : _panelNode(pn) {}
37     
38     virtual void valueChanged (SGPropertyNode * node)
39     {
40         _panelNode->dirtyBound();
41     }
42 private:
43     FGPanelNode* _panelNode;
44 };
45
46 class PanelPathListener : public SGPropertyChangeListener
47 {
48 public:
49     PanelPathListener(FGPanelNode* pn) : _panelNode(pn) {}
50     
51     virtual void valueChanged (SGPropertyNode * node)
52     {
53         _panelNode->setPanelPath(node->getStringValue());
54     }
55 private:
56     FGPanelNode* _panelNode;
57 };
58
59 class FGPanelPickCallback : public SGPickCallback {
60 public:
61   FGPanelPickCallback(FGPanelNode* p) :
62     panel(p)
63   {}
64   
65   virtual bool buttonPressed( int b,
66                               const osgGA::GUIEventAdapter&,
67                               const Info& info )
68   {    
69     button = b;
70   // convert to panel coordinates
71     osg::Matrixd m = osg::Matrixd::inverse(panel->transformMatrix());
72     picked = toOsg(info.local) * m;
73     SG_LOG( SG_INSTR, SG_DEBUG, "panel pick: " << toSG(picked) );
74   
75   // send to the panel
76     return panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_DOWN, 
77                                           picked.x(), picked.y());
78   }
79   
80   virtual void update(double dt, int keyModState)
81   {
82     panel->getPanel()->updateMouseDelay(dt);
83   }
84   
85   virtual void buttonReleased( int,
86                                const osgGA::GUIEventAdapter&,
87                                const Info* )
88   {
89     panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_UP, 
90                                           picked.x(), picked.y());
91   }
92   
93 private:
94   FGPanelNode* panel;
95   int button;
96   osg::Vec3 picked;
97 };
98
99 class FGPanelSwitchCallback : public osg::NodeCallback {
100 public:
101   FGPanelSwitchCallback(FGPanelNode* pn) : 
102     panel(pn),
103     visProp(fgGetNode("/sim/panel/visibility"))
104   {
105   }
106   
107   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
108   {
109     assert(dynamic_cast<osg::Switch*>(node));
110     osg::Switch* sw = static_cast<osg::Switch*>(node);
111     
112     if (!visProp->getBoolValue()) {
113       sw->setValue(0, false);
114       return;
115     }
116   
117     
118     panel->lazyLoad(); // isVisible check needs the panel loaded for auto-hide flag
119     bool enabled = panel->isVisible2d();
120     sw->setValue(0, enabled);
121     if (!enabled)
122       return;
123     
124     traverse(node, nv);
125   }
126   
127 private:
128   FGPanelNode* panel;
129   SGPropertyNode_ptr visProp;
130 };
131
132
133 FGPanelNode::FGPanelNode(SGPropertyNode* props) :
134     _is2d(false),
135     _resizeToViewport(false),
136     _listener(NULL)
137 {  
138   commonInit();
139   _panelPath = props->getStringValue("path");
140
141   // And the corner points
142   SGPropertyNode* pt = props->getChild("bottom-left");
143   _bottomLeft[0] = pt->getFloatValue("x-m");
144   _bottomLeft[1] = pt->getFloatValue("y-m");
145   _bottomLeft[2] = pt->getFloatValue("z-m");
146   
147   pt = props->getChild("top-left");
148   _topLeft[0] = pt->getFloatValue("x-m");
149   _topLeft[1] = pt->getFloatValue("y-m");
150   _topLeft[2] = pt->getFloatValue("z-m");
151   
152   pt = props->getChild("bottom-right");
153   _bottomRight[0] = pt->getFloatValue("x-m");
154   _bottomRight[1] = pt->getFloatValue("y-m");
155   _bottomRight[2] = pt->getFloatValue("z-m");
156   
157   _depthTest = props->getBoolValue("depth-test");
158 }
159
160 FGPanelNode::FGPanelNode() :
161     _is2d(true),
162   _resizeToViewport(true),
163   _depthTest(false)
164 {
165     globals->get_commands()->addCommand("panel-mouse-click", this, &FGPanelNode::panelMouseClickCommand);
166
167     SGPropertyNode* pathNode = fgGetNode("/sim/panel/path");
168     _pathListener.reset(new PanelPathListener(this));
169     pathNode->addChangeListener(_pathListener.get());
170     setPanelPath(pathNode->getStringValue());
171     
172     // for a 2D panel, various options adjust the transformation
173     // matrix. We need to pass this data on to OSG or its bounding box
174     // will be stale, and picking will break.
175     // http://code.google.com/p/flightgear-bugs/issues/detail?id=864
176     _listener = new PanelTransformListener(this);
177     fgGetNode("/sim/panel/x-offset", true)->addChangeListener(_listener);
178     fgGetNode("/sim/panel/y-offset", true)->addChangeListener(_listener);
179     fgGetNode("/sim/startup/xsize", true)->addChangeListener(_listener);
180     fgGetNode("/sim/startup/ysize", true)->addChangeListener(_listener);
181     
182     commonInit();
183 }
184
185 FGPanelNode::~FGPanelNode()
186 {
187     if (_is2d) {
188         globals->get_commands()->removeCommand("panel-mouse-click");
189         SGPropertyNode* pathNode = fgGetNode("/sim/panel/path");
190         pathNode->removeChangeListener(_pathListener.get());
191     }
192     
193     if (_listener) {
194         fgGetNode("/sim/panel/x-offset", true)->removeChangeListener(_listener);
195         fgGetNode("/sim/panel/y-offset", true)->removeChangeListener(_listener);
196         fgGetNode("/sim/startup/xsize", true)->removeChangeListener(_listener);
197         fgGetNode("/sim/startup/ysize", true)->removeChangeListener(_listener);
198         delete _listener;
199     }
200 }
201
202 void FGPanelNode::setPanelPath(const std::string& panel)
203 {
204   if (panel == _panelPath) {
205     return;
206   }
207   
208   _panelPath = panel;
209   if (_panel) {
210     _panel.clear();
211   }
212 }
213
214 void FGPanelNode::lazyLoad()
215 {
216   if (!_panelPath.empty() && !_panel) {
217     _panel = fgReadPanel(_panelPath);
218     if (!_panel) {
219       SG_LOG(SG_COCKPIT, SG_WARN, "failed to read panel from:" << _panelPath);
220       _panelPath = std::string(); // don't keep trying to read
221       return;
222     }
223     
224     _panel->setDepthTest(_depthTest);
225     initWithPanel();
226   }
227 }
228
229 void FGPanelNode::commonInit()
230 {
231   setUseDisplayList(false);
232   setDataVariance(Object::DYNAMIC);
233   getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
234   getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
235 }
236
237 void FGPanelNode::initWithPanel()
238 {
239   int i;
240
241   // Read out the pixel-space info
242   float panelWidth = _panel->getWidth();
243   float panelHeight = _panel->getHeight();
244
245   _panel->getLogicalExtent(_xmin, _ymin, _xmax, _ymax);
246     
247   // Now generate our transformation matrix.  For shorthand, use
248   // "a", "b", and "c" as our corners and "m" as the matrix. The
249   // vector u goes from a to b, v from a to c, and w is a
250   // perpendicular cross product.
251   osg::Vec3 a = _bottomLeft;
252   osg::Vec3 b = _bottomRight;
253   osg::Vec3 c = _topLeft;
254   osg::Vec3 u = b - a;
255   osg::Vec3 v = c - a;
256   osg::Vec3 w = u^v;
257
258     osg::Matrix& m = _xform;
259     if ((u.length2() == 0.0) || (b.length2() == 0.0)) {
260         m.makeIdentity();
261     } else {
262         // Now generate a trivial basis transformation matrix.  If we want
263         // to map the three unit vectors to three arbitrary vectors U, V,
264         // and W, then those just become the columns of the 3x3 matrix.
265         m(0,0) = u[0]; m(1,0) = v[0]; m(2,0) = w[0]; m(3,0) = a[0];//    |Ux Vx Wx|
266         m(0,1) = u[1]; m(1,1) = v[1]; m(2,1) = w[1]; m(3,1) = a[1];//m = |Uy Vy Wy|
267         m(0,2) = u[2]; m(1,2) = v[2]; m(2,2) = w[2]; m(3,2) = a[2];//    |Uz Vz Wz|
268         m(0,3) = 0;    m(1,3) = 0;    m(2,3) = 0;    m(3,3) = 1;
269     }
270
271   // The above matrix maps the unit (!) square to the panel
272   // rectangle.  Postmultiply scaling factors that match the
273   // pixel-space size of the panel.
274   for(i=0; i<4; ++i) {
275       m(0,i) *= 1.0/panelWidth;
276       m(1,i) *= 1.0/panelHeight;
277   }
278
279   dirtyBound();
280 }
281
282 osg::Matrix FGPanelNode::transformMatrix() const
283 {
284   if (!_panel) {
285     return osg::Matrix();
286   }
287
288   if (!_resizeToViewport) {
289     return _xform;
290   }
291   
292   double s = _panel->getAspectScale();
293   osg::Matrix m = osg::Matrix::scale(s, s, 1.0);
294   m *= osg::Matrix::translate(_panel->getXOffset(), _panel->getYOffset(), 0.0);
295
296   return m;
297 }
298
299 void
300 FGPanelNode::drawImplementation(osg::State& state) const
301 {  
302   if (!_panel) {
303     return;
304   }
305   
306   osg::ref_ptr<osg::RefMatrix> mv = new osg::RefMatrix;
307   mv->set(transformMatrix() * state.getModelViewMatrix());
308   state.applyModelViewMatrix(mv.get());
309   
310   _panel->draw(state);
311 }
312
313 osg::BoundingBox
314 FGPanelNode::computeBound() const
315 {
316
317   osg::Vec3 coords[3];
318   osg::Matrix m(transformMatrix());
319   coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0));
320   coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0));
321   coords[2] = m.preMult(osg::Vec3(_xmin,_ymax,0));
322
323   osg::BoundingBox bb;
324   bb.expandBy(coords[0]);
325   bb.expandBy(coords[1]);
326   bb.expandBy(coords[2]);
327   return bb;
328 }
329
330 void FGPanelNode::accept(osg::PrimitiveFunctor& functor) const
331 {
332   osg::Vec3 coords[4];
333   osg::Matrix m(transformMatrix());
334   
335   coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0));
336   coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0));
337   coords[2] = m.preMult(osg::Vec3(_xmax, _ymax, 0));
338   coords[3] = m.preMult(osg::Vec3(_xmin,_ymax,0));
339
340   functor.setVertexArray(4, coords);
341   functor.drawArrays( GL_QUADS, 0, 4);
342 }
343
344 bool FGPanelNode::isVisible2d() const
345 {
346   if (!_panel) {
347     return false;
348   }
349   
350   if (!_hideNonDefaultViews) {
351     _hideNonDefaultViews = fgGetNode("/sim/panel/hide-nonzero-view", true);
352   }
353   
354   if (_hideNonDefaultViews->getBoolValue()) {
355     if (globals->get_viewmgr()->get_current() != 0) {
356       return false;
357     }
358   }
359   
360   if (!_autoHide2d) {
361     _autoHide2d = fgGetNode("/sim/panel/hide-nonzero-heading-offset", true);
362   }
363   
364   if (_panel->getAutohide() && _autoHide2d->getBoolValue()) {
365     if (!globals->get_current_view()) {
366       return false;
367     }
368     
369     return globals->get_current_view()->getHeadingOffset_deg() == 0;
370   }
371   
372   return true;
373 }
374
375 static osg::Node* createGeode(FGPanelNode* panel)
376 {
377     osg::Geode* geode = new osg::Geode;
378     geode->addDrawable(panel);
379     
380     geode->setNodeMask(SG_NODEMASK_PICK_BIT | SG_NODEMASK_2DPANEL_BIT);
381     
382     SGSceneUserData* userData;
383     userData = SGSceneUserData::getOrCreateSceneUserData(geode);
384     userData->setPickCallback(new FGPanelPickCallback(panel));
385     return geode;
386 }
387
388 osg::Node* FGPanelNode::create2DPanelNode()
389 {
390   FGPanelNode* drawable = new FGPanelNode;
391     
392   osg::Switch* ps = new osg::Switch;
393   osg::StateSet* stateSet = ps->getOrCreateStateSet();
394   stateSet->setRenderBinDetails(1000, "RenderBin");
395   ps->addChild(createGeode(drawable));
396   
397   // speed optimization?
398   stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
399   stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
400   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
401   stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
402   stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
403   
404   ps->setUpdateCallback(new FGPanelSwitchCallback(drawable));
405   return ps;
406 }
407
408 osg::Node* FGPanelNode::load(SGPropertyNode *n)
409 {
410   FGPanelNode* drawable = new FGPanelNode(n);
411   drawable->lazyLoad(); // force load now for 2.5D panels
412   return createGeode(drawable);
413 }
414
415 /**
416  * Built-in command: pass a mouse click to the panel.
417  *
418  * button: the mouse button number, zero-based.
419  * is-down: true if the button is down, false if it is up.
420  * x-pos: the x position of the mouse click.
421  * y-pos: the y position of the mouse click.
422  */
423 bool
424 FGPanelNode::panelMouseClickCommand(const SGPropertyNode * arg)
425 {
426     return _panel->doMouseAction(arg->getIntValue("button"),
427                          arg->getBoolValue("is-down") ? PU_DOWN : PU_UP,
428                          arg->getIntValue("x-pos"),
429                          arg->getIntValue("y-pos"));
430 }