]> git.mxchange.org Git - flightgear.git/blob - src/Model/panelnode.cxx
b257a3bee2884fe1828ff07a8fd32d5c202b2089
[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 <vector>
7
8 #include <GL/gl.h>
9 #include <plib/sg.h>
10
11 #include <Main/fg_props.hxx>
12 #include <Cockpit/panel.hxx>
13 #include <Cockpit/panel_io.hxx>
14 #include "panelnode.hxx"
15
16 // Static (!) handling for all 3D panels in the program.  Very
17 // clumsy.  Replace with per-aircraft handling.
18 vector<FGPanelNode*> all_3d_panels;
19 bool fgHandle3DPanelMouseEvent(int button, int updown, int x, int y)
20 {
21     for(int i=0; i<all_3d_panels.size(); i++)
22         if(all_3d_panels[i]->doMouseAction(button, updown, x, y))
23             return true;
24     return false;
25 }
26
27 void fgUpdate3DPanels()
28 {
29     for(int i=0; i<all_3d_panels.size(); i++)
30         all_3d_panels[i]->getPanel()->updateMouseDelay();
31 }
32
33 FGPanelNode::FGPanelNode(SGPropertyNode* props)
34 {
35     int i;
36
37     // Make an FGPanel object.  But *don't* call init() or bind() on
38     // it -- those methods touch static state.
39     _panel = fgReadPanel(props->getStringValue("path"));
40
41     // Never mind.  We *have* to call init to make sure the static
42     // state is initialized (it's not, if there aren't any 2D
43     // panels).  This is a memory leak and should be fixed!`
44     _panel->init();
45
46     // Initialize the matrices to the identity.  PLib prints warnings
47     // when trying to invert singular matrices (e.g. when not using a
48     // 3D panel).
49     for(i=0; i<4; i++)
50         for(int j=0; j<4; j++)
51             _lastModelview[4*i+j] = _lastProjection[4*i+j] = i==j ? 1 : 0;
52
53     // Read out the pixel-space info
54     _xmax = _panel->getWidth();
55     _ymax = _panel->getHeight();
56
57     // And the corner points
58     SGPropertyNode* pt = props->getChild("bottom-left");
59     _bottomLeft[0] = pt->getFloatValue("x-m");
60     _bottomLeft[1] = pt->getFloatValue("y-m");
61     _bottomLeft[2] = pt->getFloatValue("z-m");
62
63     pt = props->getChild("top-left");
64     _topLeft[0] = pt->getFloatValue("x-m");
65     _topLeft[1] = pt->getFloatValue("y-m");
66     _topLeft[2] = pt->getFloatValue("z-m");
67
68     pt = props->getChild("bottom-right");
69     _bottomRight[0] = pt->getFloatValue("x-m");
70     _bottomRight[1] = pt->getFloatValue("y-m");
71     _bottomRight[2] = pt->getFloatValue("z-m");
72
73     // Now generate our transformation matrix.  For shorthand, use
74     // "a", "b", and "c" as our corners and "m" as the matrix. The
75     // vector u goes from a to b, v from a to c, and w is a
76     // perpendicular cross product.
77     float *a = _bottomLeft, *b = _bottomRight, *c = _topLeft, *m = _xform;
78     float u[3], v[3], w[3];
79     for(i=0; i<3; i++) u[i] = b[i] - a[i]; // U = B - A
80     for(i=0; i<3; i++) v[i] = c[i] - a[i]; // V = C - A
81
82     w[0] = u[1]*v[2] - v[1]*u[2];          // W = U x V
83     w[1] = u[2]*v[0] - v[2]*u[0];
84     w[2] = u[0]*v[1] - v[0]*u[1];
85
86     // Now generate a trivial basis transformation matrix.  If we want
87     // to map the three unit vectors to three arbitrary vectors U, V,
88     // and W, then those just become the columns of the 3x3 matrix.
89     m[0] = u[0]; m[4] = v[0]; m[8]  = w[0]; m[12] = a[0]; //     |Ux Vx Wx|
90     m[1] = u[1]; m[5] = v[1]; m[9]  = w[1]; m[13] = a[1]; // m = |Uy Vy Wy|
91     m[2] = u[2]; m[6] = v[2]; m[10] = w[2]; m[14] = a[2]; //     |Uz Vz Wz|
92     m[3] = 0;    m[7] = 0;    m[11] = 0;    m[15] = 1;
93
94     // The above matrix maps the unit (!) square to the panel
95     // rectangle.  Postmultiply scaling factors that match the
96     // pixel-space size of the panel.
97     for(i=0; i<4; i++) {
98         m[0+i] *= 1.0/_xmax;
99         m[4+i] *= 1.0/_ymax;
100     }
101
102     // Now plib initialization.  The bounding sphere is defined nicely
103     // by our corner points:
104     float cx = (b[0]+c[0])/2;
105     float cy = (b[1]+c[1])/2;
106     float cz = (b[2]+c[2])/2;
107     float r = sqrt((cx-a[0])*(cx-a[0]) +
108                    (cy-a[1])*(cy-a[1]) +
109                    (cz-a[2])*(cz-a[2]));
110     bsphere.setCenter(cx, cy, cz);
111     bsphere.setRadius(r);
112
113     // All done.  Add us to the list
114     all_3d_panels.push_back(this);
115 }
116
117 FGPanelNode::~FGPanelNode()
118 {
119     delete _panel;
120 }
121
122 void FGPanelNode::draw()
123 {
124     // What's the difference?
125     draw_geometry();
126 }
127
128 void FGPanelNode::draw_geometry()
129 {
130     glMatrixMode(GL_MODELVIEW);
131     glPushMatrix();
132     glMultMatrixf(_xform);
133
134     // Grab the matrix state, so that we can get back from screen
135     // coordinates to panel coordinates when the user clicks the
136     // mouse.
137     glGetFloatv(GL_MODELVIEW_MATRIX, _lastModelview);
138     glGetFloatv(GL_PROJECTION_MATRIX, _lastProjection);
139     glGetIntegerv(GL_VIEWPORT, _lastViewport);
140
141     _panel->draw();
142
143
144     glPopMatrix();
145 }
146
147 bool FGPanelNode::doMouseAction(int button, int updown, int x, int y)
148 {
149     // Covert the screen coordinates to viewport coordinates in the
150     // range [0:1], then transform to OpenGL "post projection" coords
151     // in [-1:1].  Remember the difference in Y direction!
152     float vx = (x + 0.5 - _lastViewport[0]) / _lastViewport[2];
153     float vy = (y + 0.5 - _lastViewport[1]) / _lastViewport[3];
154     vx = 2*vx - 1;
155     vy = 1 - 2*vy;
156
157     // Make two vectors in post-projection coordinates at the given
158     // screen, one in the near field and one in the far field.
159     sgVec3 a, b;
160     a[0] = b[0] = vx;
161     a[1] = b[1] = vy;
162     a[2] =  0.75; // "Near" Z value
163     b[2] = -0.75; // "Far" Z value
164
165     // Run both vectors "backwards" through the OpenGL matrix
166     // transformation.  Remember to w-normalize the vectors!
167     sgMat4 m;
168     sgMultMat4(m, *(sgMat4*)_lastProjection, *(sgMat4*)_lastModelview);
169     sgInvertMat4(m);
170
171     sgFullXformPnt3(a, m);
172     sgFullXformPnt3(b, m);
173
174     // And find their intersection on the z=0 plane.  The resulting X
175     // and Y coordinates are the hit location in panel coordinates.
176     float dxdz = (b[0] - a[0]) / (b[2] - a[2]);
177     float dydz = (b[1] - a[1]) / (b[2] - a[2]);
178     int panelX = (int)(a[0] - a[2]*dxdz + 0.5);
179     int panelY = (int)(a[1] - a[2]*dydz + 0.5);
180
181     return _panel->doLocalMouseAction(button, updown, panelX, panelY);
182 }
183
184 void FGPanelNode::die()
185 {
186     SG_LOG(SG_ALL,SG_ALERT,"Unimplemented function called on FGPanelNode");
187     exit(1);
188 }
189