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