]> git.mxchange.org Git - flightgear.git/blob - src/Model/panelnode.cxx
#738: crash when switching 2D panels
[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
12 #include <simgear/compiler.h>
13 #include <simgear/structure/exception.hxx>
14 #include <simgear/debug/logstream.hxx>
15
16 #include <simgear/scene/util/OsgMath.hxx>
17 #include <simgear/scene/util/SGPickCallback.hxx>
18 #include <simgear/scene/util/SGSceneUserData.hxx>
19 #include <simgear/scene/util/SGNodeMasks.hxx>
20
21 #include <Main/fg_os.hxx>
22 #include <Cockpit/panel.hxx>
23 #include <Cockpit/panel_io.hxx>
24
25
26 using std::vector;
27
28 class FGPanelPickCallback : public SGPickCallback {
29 public:
30   FGPanelPickCallback(FGPanelNode* p) :
31     panel(p)
32   {}
33   
34   virtual bool buttonPressed(int b, const Info& info)
35   {    
36     button = b;
37   // convert to panel coordinates
38     osg::Matrixd m = osg::Matrixd::inverse(panel->transformMatrix());
39     picked = toOsg(info.local) * m;
40     SG_LOG( SG_INSTR, SG_DEBUG, "panel pick: " << toSG(picked) );
41   
42   // send to the panel
43     return panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_DOWN, 
44                                           picked.x(), picked.y());
45   }
46   
47   virtual void update(double /* dt */)
48   {
49     panel->getPanel()->updateMouseDelay();
50   }
51   
52   virtual void buttonReleased(void)
53   {
54     panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_UP, 
55                                           picked.x(), picked.y());
56   }
57   
58 private:
59   FGPanelNode* panel;
60   int button;
61   osg::Vec3 picked;
62 };
63
64 FGPanelNode::FGPanelNode(SGPropertyNode* props) :
65   _resizeToViewport(false)
66 {  
67     // Make an FGPanel object.  But *don't* call init() or bind() on
68     // it -- those methods touch static state.
69     const char *path = props->getStringValue("path");
70     _panel = fgReadPanel(path);
71     if (!_panel)
72         throw sg_io_exception(string("Failed to load panel ") + path);
73
74     // And the corner points
75     SGPropertyNode* pt = props->getChild("bottom-left");
76     _bottomLeft[0] = pt->getFloatValue("x-m");
77     _bottomLeft[1] = pt->getFloatValue("y-m");
78     _bottomLeft[2] = pt->getFloatValue("z-m");
79     
80     pt = props->getChild("top-left");
81     _topLeft[0] = pt->getFloatValue("x-m");
82     _topLeft[1] = pt->getFloatValue("y-m");
83     _topLeft[2] = pt->getFloatValue("z-m");
84     
85     pt = props->getChild("bottom-right");
86     _bottomRight[0] = pt->getFloatValue("x-m");
87     _bottomRight[1] = pt->getFloatValue("y-m");
88     _bottomRight[2] = pt->getFloatValue("z-m");
89   
90     _panel->setDepthTest( props->getBoolValue("depth-test") );
91   
92     initWithPanel();
93 }
94
95 FGPanelNode::FGPanelNode(FGPanel* p) :
96   _panel(p),
97   _resizeToViewport(true)
98 {
99     initWithPanel();
100 }
101
102 void FGPanelNode::initWithPanel()
103 {
104     int i;
105
106     // Never mind.  We *have* to call init to make sure the static
107     // state is initialized (it's not, if there aren't any 2D
108     // panels).  This is a memory leak and should be fixed!`
109     // FIXME
110     _panel->init();
111
112     // Read out the pixel-space info
113     float panelWidth = _panel->getWidth();
114     float panelHeight = _panel->getHeight();
115
116     _panel->getLogicalExtent(_xmin, _ymin, _xmax, _ymax);
117   
118     // Now generate our transformation matrix.  For shorthand, use
119     // "a", "b", and "c" as our corners and "m" as the matrix. The
120     // vector u goes from a to b, v from a to c, and w is a
121     // perpendicular cross product.
122     osg::Vec3 a = _bottomLeft;
123     osg::Vec3 b = _bottomRight;
124     osg::Vec3 c = _topLeft;
125     osg::Vec3 u = b - a;
126     osg::Vec3 v = c - a;
127     osg::Vec3 w = u^v;
128
129     osg::Matrix& m = _xform;
130     // Now generate a trivial basis transformation matrix.  If we want
131     // to map the three unit vectors to three arbitrary vectors U, V,
132     // and W, then those just become the columns of the 3x3 matrix.
133     m(0,0) = u[0]; m(1,0) = v[0]; m(2,0) = w[0]; m(3,0) = a[0];//    |Ux Vx Wx|
134     m(0,1) = u[1]; m(1,1) = v[1]; m(2,1) = w[1]; m(3,1) = a[1];//m = |Uy Vy Wy|
135     m(0,2) = u[2]; m(1,2) = v[2]; m(2,2) = w[2]; m(3,2) = a[2];//    |Uz Vz Wz|
136     m(0,3) = 0;    m(1,3) = 0;    m(2,3) = 0;    m(3,3) = 1;
137
138     // The above matrix maps the unit (!) square to the panel
139     // rectangle.  Postmultiply scaling factors that match the
140     // pixel-space size of the panel.
141     for(i=0; i<4; ++i) {
142         m(0,i) *= 1.0/panelWidth;
143         m(1,i) *= 1.0/panelHeight;
144     }
145
146     dirtyBound();
147     setUseDisplayList(false);
148     setDataVariance(Object::DYNAMIC);
149
150     getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
151     getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
152 }
153
154 FGPanelNode::~FGPanelNode()
155 {
156 }
157
158 osg::Matrix FGPanelNode::transformMatrix() const
159 {
160   if (!_resizeToViewport) {
161     return _xform;
162   }
163   
164   double s = _panel->getAspectScale();
165   osg::Matrix m = osg::Matrix::scale(s, s, 1.0);
166   m *= osg::Matrix::translate(_panel->getXOffset(), _panel->getYOffset(), 0.0);
167
168   return m;
169 }
170
171 void
172 FGPanelNode::drawImplementation(osg::State& state) const
173 {  
174   osg::ref_ptr<osg::RefMatrix> mv = new osg::RefMatrix;
175   mv->set(transformMatrix() * state.getModelViewMatrix());
176   state.applyModelViewMatrix(mv.get());
177   
178   _panel->draw(state);
179 }
180
181 osg::BoundingBox
182 FGPanelNode::computeBound() const
183 {
184
185   osg::Vec3 coords[3];
186   osg::Matrix m(transformMatrix());
187   coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0));
188   coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0));
189   coords[2] = m.preMult(osg::Vec3(_xmin,_ymax,0));
190
191   osg::BoundingBox bb;
192   bb.expandBy(coords[0]);
193   bb.expandBy(coords[1]);
194   bb.expandBy(coords[2]);
195   return bb;
196 }
197
198 void FGPanelNode::accept(osg::PrimitiveFunctor& functor) const
199 {
200   osg::Vec3 coords[4];
201   osg::Matrix m(transformMatrix());
202   
203   coords[0] = m.preMult(osg::Vec3(_xmin,_ymin,0));
204   coords[1] = m.preMult(osg::Vec3(_xmax,_ymin,0));
205   coords[2] = m.preMult(osg::Vec3(_xmax, _ymax, 0));
206   coords[3] = m.preMult(osg::Vec3(_xmin,_ymax,0));
207
208   functor.setVertexArray(4, coords);
209   functor.drawArrays( GL_QUADS, 0, 4);
210 }
211
212 static osg::Node* createGeode(FGPanelNode* panel)
213 {
214     osg::Geode* geode = new osg::Geode;
215     geode->addDrawable(panel);
216     
217     geode->setNodeMask(SG_NODEMASK_PICK_BIT | SG_NODEMASK_2DPANEL_BIT);
218     
219     SGSceneUserData* userData;
220     userData = SGSceneUserData::getOrCreateSceneUserData(geode);
221     userData->setPickCallback(new FGPanelPickCallback(panel));
222     return geode;
223 }
224
225 osg::Node* FGPanelNode::createNode(FGPanel* p)
226 {
227     FGPanelNode* drawable = new FGPanelNode(p);
228     return createGeode(drawable);
229 }
230
231 osg::Node* FGPanelNode::load(SGPropertyNode *n)
232 {
233   FGPanelNode* drawable = new FGPanelNode(n);
234   return createGeode(drawable);
235 }