5 #include <simgear/compiler.h>
10 #include <Cockpit/panel.hxx>
11 #include <Cockpit/panel_io.hxx>
12 #include "panelnode.hxx"
17 // Static (!) handling for all 3D panels in the program. Very
18 // clumsy. Replace with per-aircraft handling.
19 vector<FGPanelNode*> all_3d_panels;
20 bool fgHandle3DPanelMouseEvent( int button, int updown, int x, int y )
22 for ( unsigned int i = 0; i < all_3d_panels.size(); i++ ) {
23 if ( all_3d_panels[i]->doMouseAction(button, updown, x, y) ) {
30 void fgUpdate3DPanels()
32 for ( unsigned int i = 0; i < all_3d_panels.size(); i++ ) {
33 all_3d_panels[i]->getPanel()->updateMouseDelay();
37 FGPanelNode::FGPanelNode(SGPropertyNode* props)
41 // Make an FGPanel object. But *don't* call init() or bind() on
42 // it -- those methods touch static state.
43 _panel = fgReadPanel(props->getStringValue("path"));
45 // Never mind. We *have* to call init to make sure the static
46 // state is initialized (it's not, if there aren't any 2D
47 // panels). This is a memory leak and should be fixed!`
50 _panel->setDepthTest( props->getBoolValue("depth-test") );
52 // Initialize the matrices to the identity. PLib prints warnings
53 // when trying to invert singular matrices (e.g. when not using a
56 for(int j=0; j<4; j++)
57 _lastModelview[4*i+j] = _lastProjection[4*i+j] = i==j ? 1 : 0;
59 // Read out the pixel-space info
60 _xmax = _panel->getWidth();
61 _ymax = _panel->getHeight();
63 // And the corner points
64 SGPropertyNode* pt = props->getChild("bottom-left");
65 _bottomLeft[0] = pt->getFloatValue("x-m");
66 _bottomLeft[1] = pt->getFloatValue("y-m");
67 _bottomLeft[2] = pt->getFloatValue("z-m");
69 pt = props->getChild("top-left");
70 _topLeft[0] = pt->getFloatValue("x-m");
71 _topLeft[1] = pt->getFloatValue("y-m");
72 _topLeft[2] = pt->getFloatValue("z-m");
74 pt = props->getChild("bottom-right");
75 _bottomRight[0] = pt->getFloatValue("x-m");
76 _bottomRight[1] = pt->getFloatValue("y-m");
77 _bottomRight[2] = pt->getFloatValue("z-m");
79 // Now generate our transformation matrix. For shorthand, use
80 // "a", "b", and "c" as our corners and "m" as the matrix. The
81 // vector u goes from a to b, v from a to c, and w is a
82 // perpendicular cross product.
83 float *a = _bottomLeft, *b = _bottomRight, *c = _topLeft, *m = _xform;
84 float u[3], v[3], w[3];
85 for(i=0; i<3; i++) u[i] = b[i] - a[i]; // U = B - A
86 for(i=0; i<3; i++) v[i] = c[i] - a[i]; // V = C - A
88 w[0] = u[1]*v[2] - v[1]*u[2]; // W = U x V
89 w[1] = u[2]*v[0] - v[2]*u[0];
90 w[2] = u[0]*v[1] - v[0]*u[1];
92 // Now generate a trivial basis transformation matrix. If we want
93 // to map the three unit vectors to three arbitrary vectors U, V,
94 // and W, then those just become the columns of the 3x3 matrix.
95 m[0] = u[0]; m[4] = v[0]; m[8] = w[0]; m[12] = a[0]; // |Ux Vx Wx|
96 m[1] = u[1]; m[5] = v[1]; m[9] = w[1]; m[13] = a[1]; // m = |Uy Vy Wy|
97 m[2] = u[2]; m[6] = v[2]; m[10] = w[2]; m[14] = a[2]; // |Uz Vz Wz|
98 m[3] = 0; m[7] = 0; m[11] = 0; m[15] = 1;
100 // The above matrix maps the unit (!) square to the panel
101 // rectangle. Postmultiply scaling factors that match the
102 // pixel-space size of the panel.
108 // Now plib initialization. The bounding sphere is defined nicely
109 // by our corner points:
110 float cx = (b[0]+c[0])/2;
111 float cy = (b[1]+c[1])/2;
112 float cz = (b[2]+c[2])/2;
113 float r = sqrt((cx-a[0])*(cx-a[0]) +
114 (cy-a[1])*(cy-a[1]) +
115 (cz-a[2])*(cz-a[2]));
116 bsphere.setCenter(cx, cy, cz);
117 bsphere.setRadius(r);
119 // All done. Add us to the list
120 all_3d_panels.push_back(this);
123 FGPanelNode::~FGPanelNode()
128 void FGPanelNode::draw()
130 // What's the difference?
134 void FGPanelNode::draw_geometry()
136 glMatrixMode(GL_MODELVIEW);
138 glMultMatrixf(_xform);
140 // Grab the matrix state, so that we can get back from screen
141 // coordinates to panel coordinates when the user clicks the
143 glGetFloatv(GL_MODELVIEW_MATRIX, _lastModelview);
144 glGetFloatv(GL_PROJECTION_MATRIX, _lastProjection);
145 glGetIntegerv(GL_VIEWPORT, _lastViewport);
153 bool FGPanelNode::doMouseAction(int button, int updown, int x, int y)
155 // Covert the screen coordinates to viewport coordinates in the
156 // range [0:1], then transform to OpenGL "post projection" coords
157 // in [-1:1]. Remember the difference in Y direction!
158 float vx = (x + 0.5 - _lastViewport[0]) / _lastViewport[2];
159 float vy = (y + 0.5 - _lastViewport[1]) / _lastViewport[3];
163 // Make two vectors in post-projection coordinates at the given
164 // screen, one in the near field and one in the far field.
168 a[2] = 0.75; // "Near" Z value
169 b[2] = -0.75; // "Far" Z value
171 // Run both vectors "backwards" through the OpenGL matrix
172 // transformation. Remember to w-normalize the vectors!
174 sgMultMat4(m, *(sgMat4*)_lastProjection, *(sgMat4*)_lastModelview);
177 sgFullXformPnt3(a, m);
178 sgFullXformPnt3(b, m);
180 // And find their intersection on the z=0 plane. The resulting X
181 // and Y coordinates are the hit location in panel coordinates.
182 float dxdz = (b[0] - a[0]) / (b[2] - a[2]);
183 float dydz = (b[1] - a[1]) / (b[2] - a[2]);
184 int panelX = (int)(a[0] - a[2]*dxdz + 0.5);
185 int panelY = (int)(a[1] - a[2]*dydz + 0.5);
187 return _panel->doLocalMouseAction(button, updown, panelX, panelY);
190 void FGPanelNode::die()
192 SG_LOG(SG_ALL,SG_ALERT,"Unimplemented function called on FGPanelNode");