]> git.mxchange.org Git - flightgear.git/blob - src/Model/panelnode.cxx
Lazy-loading of the 2D panel node.
[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
30 using std::vector;
31
32 static FGPanelNode* global_panel = NULL;
33
34 /**
35  * Built-in command: pass a mouse click to the panel.
36  *
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.
41  */
42 static bool
43 do_panel_mouse_click (const SGPropertyNode * arg)
44 {
45   if (global_panel)
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"));
51   else
52     return false;
53   
54   return false;
55 }
56
57 class FGPanelPickCallback : public SGPickCallback {
58 public:
59   FGPanelPickCallback(FGPanelNode* p) :
60     panel(p)
61   {}
62   
63   virtual bool buttonPressed(int b, const Info& info)
64   {    
65     button = b;
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) );
70   
71   // send to the panel
72     return panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_DOWN, 
73                                           picked.x(), picked.y());
74   }
75   
76   virtual void update(double dt)
77   {
78     panel->getPanel()->updateMouseDelay(dt);
79   }
80   
81   virtual void buttonReleased(void)
82   {
83     panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_UP, 
84                                           picked.x(), picked.y());
85   }
86   
87 private:
88   FGPanelNode* panel;
89   int button;
90   osg::Vec3 picked;
91 };
92
93 class FGPanelSwitchCallback : public osg::NodeCallback {
94 public:
95   FGPanelSwitchCallback(FGPanelNode* pn) : 
96     panel(pn),
97     visProp(fgGetNode("/sim/panel/visibility"))
98   {
99   }
100   
101   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
102   {
103     assert(dynamic_cast<osg::Switch*>(node));
104     osg::Switch* sw = static_cast<osg::Switch*>(node);
105     
106     if (!visProp->getBoolValue()) {
107       sw->setValue(0, false);
108       return;
109     }
110   
111     
112     panel->lazyLoad(); // isVisible check needs the panel loaded for auto-hide flag
113     bool enabled = panel->isVisible2d();
114     sw->setValue(0, enabled);
115     if (!enabled)
116       return;
117     
118     traverse(node, nv);
119   }
120   
121 private:
122   FGPanelNode* panel;
123   SGPropertyNode* visProp;
124 };
125
126
127 FGPanelNode::FGPanelNode(SGPropertyNode* props) :
128   _resizeToViewport(false)
129 {  
130   commonInit();
131   _panelPath = props->getStringValue("path");
132
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");
138   
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");
143   
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");
148   
149   _depthTest = props->getBoolValue("depth-test");
150 }
151
152 FGPanelNode::FGPanelNode() :
153   _resizeToViewport(true),
154   _depthTest(false)
155 {
156   commonInit();
157 }
158
159 void FGPanelNode::setPanelPath(const std::string& panel)
160 {
161   if (panel == _panelPath) {
162     return;
163   }
164   
165   _panelPath = panel;
166   if (_panel) {
167     _panel.clear();
168   }
169 }
170
171 void FGPanelNode::lazyLoad()
172 {
173   if (!_panelPath.empty() && !_panel) {
174     _panel = fgReadPanel(_panelPath);
175     if (!_panel) {
176       SG_LOG(SG_COCKPIT, SG_WARN, "failed to read panel from:" << _panelPath);
177       _panelPath = string(); // don't keep trying to read
178       return;
179     }
180     
181     _panel->setDepthTest(_depthTest);
182     initWithPanel();
183   }
184 }
185
186 void FGPanelNode::commonInit()
187 {
188   setUseDisplayList(false);
189   setDataVariance(Object::DYNAMIC);
190   getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
191   getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
192 }
193
194 void FGPanelNode::initWithPanel()
195 {
196   int i;
197
198   // Read out the pixel-space info
199   float panelWidth = _panel->getWidth();
200   float panelHeight = _panel->getHeight();
201
202   _panel->getLogicalExtent(_xmin, _ymin, _xmax, _ymax);
203
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;
211   osg::Vec3 u = b - a;
212   osg::Vec3 v = c - a;
213   osg::Vec3 w = u^v;
214
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;
223
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.
227   for(i=0; i<4; ++i) {
228       m(0,i) *= 1.0/panelWidth;
229       m(1,i) *= 1.0/panelHeight;
230   }
231
232   dirtyBound();
233 }
234
235 FGPanelNode::~FGPanelNode()
236 {
237 }
238
239 osg::Matrix FGPanelNode::transformMatrix() const
240 {
241   if (!_panel) {
242     osg::Matrix();
243   }
244
245   if (!_resizeToViewport) {
246     return _xform;
247   }
248   
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);
252
253   return m;
254 }
255
256 void
257 FGPanelNode::drawImplementation(osg::State& state) const
258 {  
259   if (!_panel) {
260     return;
261   }
262   
263   osg::ref_ptr<osg::RefMatrix> mv = new osg::RefMatrix;
264   mv->set(transformMatrix() * state.getModelViewMatrix());
265   state.applyModelViewMatrix(mv.get());
266   
267   _panel->draw(state);
268 }
269
270 osg::BoundingBox
271 FGPanelNode::computeBound() const
272 {
273
274   osg::Vec3 coords[3];
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));
279
280   osg::BoundingBox bb;
281   bb.expandBy(coords[0]);
282   bb.expandBy(coords[1]);
283   bb.expandBy(coords[2]);
284   return bb;
285 }
286
287 void FGPanelNode::accept(osg::PrimitiveFunctor& functor) const
288 {
289   osg::Vec3 coords[4];
290   osg::Matrix m(transformMatrix());
291   
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));
296
297   functor.setVertexArray(4, coords);
298   functor.drawArrays( GL_QUADS, 0, 4);
299 }
300
301 bool FGPanelNode::isVisible2d() const
302 {
303   if (!_panel) {
304     return false;
305   }
306   
307   if (_panel->getAutohide()) {
308     if (!globals->get_current_view()) {
309       return false;
310     }
311     
312     return globals->get_current_view()->getHeadingOffset_deg() == 0;
313   }
314   
315   return true;
316 }
317
318 static osg::Node* createGeode(FGPanelNode* panel)
319 {
320     osg::Geode* geode = new osg::Geode;
321     geode->addDrawable(panel);
322     
323     geode->setNodeMask(SG_NODEMASK_PICK_BIT | SG_NODEMASK_2DPANEL_BIT);
324     
325     SGSceneUserData* userData;
326     userData = SGSceneUserData::getOrCreateSceneUserData(geode);
327     userData->setPickCallback(new FGPanelPickCallback(panel));
328     return geode;
329 }
330
331 class PanelPathObserver : public SGPropertyChangeListener
332 {
333 public:
334   PanelPathObserver(FGPanelNode* pn) : _panelNode(pn) {}
335   
336   virtual void valueChanged (SGPropertyNode * node)
337   {
338     _panelNode->setPanelPath(node->getStringValue());
339   }
340 private:
341   FGPanelNode* _panelNode;
342 };
343
344 osg::Node* FGPanelNode::create2DPanelNode()
345 {
346   SGCommandMgr::instance()->addCommand("panel-mouse-click", do_panel_mouse_click);
347   
348   SGPropertyNode* pathNode = fgGetNode("/sim/panel/path");
349   
350   FGPanelNode* drawable = new FGPanelNode();
351 // need a global to keep the panel_mouse_click command working, sadly
352   global_panel = drawable;
353   
354   PanelPathObserver* ppo = new PanelPathObserver(drawable);
355   pathNode->addChangeListener(ppo);
356   drawable->setPanelPath(pathNode->getStringValue());
357   
358   osg::Switch* ps = new osg::Switch;
359   osg::StateSet* stateSet = ps->getOrCreateStateSet();
360   stateSet->setRenderBinDetails(1000, "RenderBin");
361   ps->addChild(createGeode(drawable));
362   
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);
369   
370   ps->setUpdateCallback(new FGPanelSwitchCallback(drawable));
371   return ps;
372 }
373
374 osg::Node* FGPanelNode::load(SGPropertyNode *n)
375 {
376   FGPanelNode* drawable = new FGPanelNode(n);
377   drawable->lazyLoad(); // force load now for 2.5D panels
378   return createGeode(drawable);
379 }