5 #include <simgear/compiler.h>
6 #include <simgear/structure/exception.hxx>
11 #include <Cockpit/panel.hxx>
12 #include <Cockpit/panel_io.hxx>
13 #include "panelnode.hxx"
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 )
24 for ( unsigned int i = 0; i < all_3d_panels.size(); i++ ) {
25 if ( all_3d_panels[i]->doMouseAction(button, updown, x, y) ) {
32 void fgUpdate3DPanels()
34 for ( unsigned int i = 0; i < all_3d_panels.size(); i++ ) {
35 all_3d_panels[i]->getPanel()->updateMouseDelay();
39 FGPanelNode::FGPanelNode(SGPropertyNode* props)
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);
48 throw sg_io_exception(string("Failed to load panel ") + path);
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!`
56 _panel->setDepthTest( props->getBoolValue("depth-test") );
58 // Read out the pixel-space info
59 _xmax = _panel->getWidth();
60 _ymax = _panel->getHeight();
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");
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");
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");
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;
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;
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.
106 _lastViewport[0] = 0;
107 _lastViewport[1] = 0;
108 _lastViewport[2] = 0;
109 _lastViewport[3] = 0;
113 // All done. Add us to the list
114 all_3d_panels.push_back(this);
116 setUseDisplayList(false);
117 getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
118 getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
121 FGPanelNode::~FGPanelNode()
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);
132 FGPanelNode::drawImplementation(osg::State& state) const
134 osg::ref_ptr<osg::RefMatrix> mv = new osg::RefMatrix;
135 mv->set(_xform*state.getModelViewMatrix());
136 state.applyModelViewMatrix(mv.get());
138 // Grab the matrix state, so that we can get back from screen
139 // coordinates to panel coordinates when the user clicks the
141 // OSGFIXME: we don't need that when we can really pick
142 _lastModelview = state.getModelViewMatrix();
143 _lastProjection = state.getProjectionMatrix();
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();
155 FGPanelNode::computeBound() const
158 bb.expandBy(_bottomLeft);
159 bb.expandBy(_bottomRight);
160 bb.expandBy(_topLeft);
164 bool FGPanelNode::doMouseAction(int button, int updown, int x, int y)
166 if (_lastViewport[2] == 0 || _lastViewport[3] == 0) {
167 // we haven't been drawn yet, presumably
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];
179 // Make two vectors in post-projection coordinates at the given
180 // screen, one in the near field and one in the far field.
184 a[2] = 0.75; // "Near" Z value
185 b[2] = -0.75; // "Far" Z value
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);
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);
202 return _panel->doLocalMouseAction(button, updown, panelX, panelY);