]> git.mxchange.org Git - flightgear.git/blob - src/Model/panelnode.cxx
Merge branch 'master' of git://gitorious.org/fg/flightgear into next
[flightgear.git] / src / Model / panelnode.cxx
1 #ifdef HAVE_CONFIG_H
2 #  include <config.h>
3 #endif
4
5 #include <simgear/compiler.h>
6 #include <simgear/structure/exception.hxx>
7
8 #include <vector>
9 #include <algorithm>
10
11 #include <Cockpit/panel.hxx>
12 #include <Cockpit/panel_io.hxx>
13 #include "panelnode.hxx"
14
15 using std::vector;
16
17
18 // Static (!) handling for all 3D panels in the program.
19 // OSGFIXME: Put the panel as different elements in the scenegraph.
20 // Then just pick in that scenegraph.
21 vector<FGPanelNode*> all_3d_panels;
22 bool fgHandle3DPanelMouseEvent( int button, int updown, int x, int y )
23 {
24     for ( unsigned int i = 0; i < all_3d_panels.size(); i++ ) {
25         if ( all_3d_panels[i]->doMouseAction(button, updown, x, y) ) {
26             return true;
27         }
28     }
29     return false;
30 }
31
32 void fgUpdate3DPanels()
33 {
34     for ( unsigned int i = 0; i < all_3d_panels.size(); i++ ) {
35         all_3d_panels[i]->getPanel()->updateMouseDelay();
36     }
37 }
38
39 FGPanelNode::FGPanelNode(SGPropertyNode* props)
40 {
41     int i;
42
43     // Make an FGPanel object.  But *don't* call init() or bind() on
44     // it -- those methods touch static state.
45     const char *path = props->getStringValue("path");
46     _panel = fgReadPanel(path);
47     if (!_panel)
48         throw sg_io_exception(string("Failed to load panel ") + path);
49
50     // Never mind.  We *have* to call init to make sure the static
51     // state is initialized (it's not, if there aren't any 2D
52     // panels).  This is a memory leak and should be fixed!`
53     // FIXME
54     _panel->init();
55
56     _panel->setDepthTest( props->getBoolValue("depth-test") );
57
58     // Read out the pixel-space info
59     _xmax = _panel->getWidth();
60     _ymax = _panel->getHeight();
61
62     // And the corner points
63     SGPropertyNode* pt = props->getChild("bottom-left");
64     _bottomLeft[0] = pt->getFloatValue("x-m");
65     _bottomLeft[1] = pt->getFloatValue("y-m");
66     _bottomLeft[2] = pt->getFloatValue("z-m");
67
68     pt = props->getChild("top-left");
69     _topLeft[0] = pt->getFloatValue("x-m");
70     _topLeft[1] = pt->getFloatValue("y-m");
71     _topLeft[2] = pt->getFloatValue("z-m");
72
73     pt = props->getChild("bottom-right");
74     _bottomRight[0] = pt->getFloatValue("x-m");
75     _bottomRight[1] = pt->getFloatValue("y-m");
76     _bottomRight[2] = pt->getFloatValue("z-m");
77
78     // Now generate our transformation matrix.  For shorthand, use
79     // "a", "b", and "c" as our corners and "m" as the matrix. The
80     // vector u goes from a to b, v from a to c, and w is a
81     // perpendicular cross product.
82     osg::Vec3 a = _bottomLeft;
83     osg::Vec3 b = _bottomRight;
84     osg::Vec3 c = _topLeft;
85     osg::Vec3 u = b - a;
86     osg::Vec3 v = c - a;
87     osg::Vec3 w = u^v;
88
89     osg::Matrix& m = _xform;
90     // Now generate a trivial basis transformation matrix.  If we want
91     // to map the three unit vectors to three arbitrary vectors U, V,
92     // and W, then those just become the columns of the 3x3 matrix.
93     m(0,0) = u[0]; m(1,0) = v[0]; m(2,0) = w[0]; m(3,0) = a[0];//    |Ux Vx Wx|
94     m(0,1) = u[1]; m(1,1) = v[1]; m(2,1) = w[1]; m(3,1) = a[1];//m = |Uy Vy Wy|
95     m(0,2) = u[2]; m(1,2) = v[2]; m(2,2) = w[2]; m(3,2) = a[2];//    |Uz Vz Wz|
96     m(0,3) = 0;    m(1,3) = 0;    m(2,3) = 0;    m(3,3) = 1;
97
98     // The above matrix maps the unit (!) square to the panel
99     // rectangle.  Postmultiply scaling factors that match the
100     // pixel-space size of the panel.
101     for(i=0; i<4; ++i) {
102         m(0,i) *= 1.0/_xmax;
103         m(1,i) *= 1.0/_ymax;
104     }
105
106     _lastViewport[0] = 0;
107     _lastViewport[1] = 0;
108     _lastViewport[2] = 0;
109     _lastViewport[3] = 0;
110
111     dirtyBound();
112
113     // All done.  Add us to the list
114     all_3d_panels.push_back(this);
115
116     setUseDisplayList(false);
117     getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
118     getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
119 }
120
121 FGPanelNode::~FGPanelNode()
122 {
123     vector<FGPanelNode*>::iterator i =
124       find(all_3d_panels.begin(), all_3d_panels.end(), this);
125     if (i != all_3d_panels.end()) {
126         all_3d_panels.erase(i);
127     }
128     delete _panel;
129 }
130
131 void
132 FGPanelNode::drawImplementation(osg::State& state) const
133 {
134   osg::ref_ptr<osg::RefMatrix> mv = new osg::RefMatrix;
135   mv->set(_xform*state.getModelViewMatrix());
136   state.applyModelViewMatrix(mv.get());
137   
138   // Grab the matrix state, so that we can get back from screen
139   // coordinates to panel coordinates when the user clicks the
140   // mouse.
141   // OSGFIXME: we don't need that when we can really pick
142   _lastModelview = state.getModelViewMatrix();
143   _lastProjection = state.getProjectionMatrix();
144
145   const osg::Viewport* vp = state.getCurrentViewport();
146   _lastViewport[0] = vp->x();
147   _lastViewport[1] = vp->y();
148   _lastViewport[2] = vp->width();
149   _lastViewport[3] = vp->height();
150   
151   _panel->draw(state);
152 }
153
154 osg::BoundingBox
155 FGPanelNode::computeBound() const
156 {
157     osg::BoundingBox bb;
158     bb.expandBy(_bottomLeft);
159     bb.expandBy(_bottomRight);
160     bb.expandBy(_topLeft);
161     return bb;
162 }
163
164 bool FGPanelNode::doMouseAction(int button, int updown, int x, int y)
165 {
166     if (_lastViewport[2] == 0 || _lastViewport[3] == 0) {
167         // we haven't been drawn yet, presumably
168         return false;
169     }
170
171     // Covert the screen coordinates to viewport coordinates in the
172     // range [0:1], then transform to OpenGL "post projection" coords
173     // in [-1:1].  Remember the difference in Y direction!
174     float vx = (x + 0.5 - _lastViewport[0]) / _lastViewport[2];
175     float vy = (y + 0.5 - _lastViewport[1]) / _lastViewport[3];
176     vx = 2*vx - 1;
177     vy = 1 - 2*vy;
178
179     // Make two vectors in post-projection coordinates at the given
180     // screen, one in the near field and one in the far field.
181     osg::Vec3 a, b;
182     a[0] = b[0] = vx;
183     a[1] = b[1] = vy;
184     a[2] =  0.75; // "Near" Z value
185     b[2] = -0.75; // "Far" Z value
186
187     // Run both vectors "backwards" through the OpenGL matrix
188     // transformation.  Remember to w-normalize the vectors!
189     osg::Matrix m = _lastModelview*_lastProjection;
190     m = osg::Matrix::inverse(m);
191
192     a = m.preMult(a);
193     b = m.preMult(b);
194
195     // And find their intersection on the z=0 plane.  The resulting X
196     // and Y coordinates are the hit location in panel coordinates.
197     float dxdz = (b[0] - a[0]) / (b[2] - a[2]);
198     float dydz = (b[1] - a[1]) / (b[2] - a[2]);
199     int panelX = (int)(a[0] - a[2]*dxdz + 0.5);
200     int panelY = (int)(a[1] - a[2]*dydz + 0.5);
201
202     return _panel->doLocalMouseAction(button, updown, panelX, panelY);
203 }
204