189 lines
5.9 KiB
C++
189 lines
5.9 KiB
C++
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <simgear/compiler.h>
|
|
#include <vector>
|
|
|
|
#include <GL/gl.h>
|
|
#include <plib/sg.h>
|
|
|
|
#include <Main/fg_props.hxx>
|
|
#include <Cockpit/panel.hxx>
|
|
#include <Cockpit/panel_io.hxx>
|
|
#include "panelnode.hxx"
|
|
|
|
// Static (!) handling for all 3D panels in the program. Very
|
|
// clumsy. Replace with per-aircraft handling.
|
|
vector<FGPanelNode*> all_3d_panels;
|
|
bool fgHandle3DPanelMouseEvent(int button, int updown, int x, int y)
|
|
{
|
|
for(int i=0; i<all_3d_panels.size(); i++)
|
|
if(all_3d_panels[i]->doMouseAction(button, updown, x, y))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void fgUpdate3DPanels()
|
|
{
|
|
for(int i=0; i<all_3d_panels.size(); i++)
|
|
all_3d_panels[i]->getPanel()->updateMouseDelay();
|
|
}
|
|
|
|
FGPanelNode::FGPanelNode(SGPropertyNode* props)
|
|
{
|
|
int i;
|
|
|
|
// Make an FGPanel object. But *don't* call init() or bind() on
|
|
// it -- those methods touch static state.
|
|
_panel = fgReadPanel(props->getStringValue("path"));
|
|
|
|
// Never mind. We *have* to call init to make sure the static
|
|
// state is initialized (it's not, if there aren't any 2D
|
|
// panels). This is a memory leak and should be fixed!`
|
|
_panel->init();
|
|
|
|
// Initialize the matrices to the identity. PLib prints warnings
|
|
// when trying to invert singular matrices (e.g. when not using a
|
|
// 3D panel).
|
|
for(i=0; i<4; i++)
|
|
for(int j=0; j<4; j++)
|
|
_lastModelview[4*i+j] = _lastProjection[4*i+j] = i==j ? 1 : 0;
|
|
|
|
// Read out the pixel-space info
|
|
_xmax = _panel->getWidth();
|
|
_ymax = _panel->getHeight();
|
|
|
|
// And the corner points
|
|
SGPropertyNode* pt = props->getChild("bottom-left");
|
|
_bottomLeft[0] = pt->getFloatValue("x-m");
|
|
_bottomLeft[1] = pt->getFloatValue("y-m");
|
|
_bottomLeft[2] = pt->getFloatValue("z-m");
|
|
|
|
pt = props->getChild("top-left");
|
|
_topLeft[0] = pt->getFloatValue("x-m");
|
|
_topLeft[1] = pt->getFloatValue("y-m");
|
|
_topLeft[2] = pt->getFloatValue("z-m");
|
|
|
|
pt = props->getChild("bottom-right");
|
|
_bottomRight[0] = pt->getFloatValue("x-m");
|
|
_bottomRight[1] = pt->getFloatValue("y-m");
|
|
_bottomRight[2] = pt->getFloatValue("z-m");
|
|
|
|
// Now generate our transformation matrix. For shorthand, use
|
|
// "a", "b", and "c" as our corners and "m" as the matrix. The
|
|
// vector u goes from a to b, v from a to c, and w is a
|
|
// perpendicular cross product.
|
|
float *a = _bottomLeft, *b = _bottomRight, *c = _topLeft, *m = _xform;
|
|
float u[3], v[3], w[3];
|
|
for(i=0; i<3; i++) u[i] = b[i] - a[i]; // U = B - A
|
|
for(i=0; i<3; i++) v[i] = c[i] - a[i]; // V = C - A
|
|
|
|
w[0] = u[1]*v[2] - v[1]*u[2]; // W = U x V
|
|
w[1] = u[2]*v[0] - v[2]*u[0];
|
|
w[2] = u[0]*v[1] - v[0]*u[1];
|
|
|
|
// Now generate a trivial basis transformation matrix. If we want
|
|
// to map the three unit vectors to three arbitrary vectors U, V,
|
|
// and W, then those just become the columns of the 3x3 matrix.
|
|
m[0] = u[0]; m[4] = v[0]; m[8] = w[0]; m[12] = a[0]; // |Ux Vx Wx|
|
|
m[1] = u[1]; m[5] = v[1]; m[9] = w[1]; m[13] = a[1]; // m = |Uy Vy Wy|
|
|
m[2] = u[2]; m[6] = v[2]; m[10] = w[2]; m[14] = a[2]; // |Uz Vz Wz|
|
|
m[3] = 0; m[7] = 0; m[11] = 0; m[15] = 1;
|
|
|
|
// The above matrix maps the unit (!) square to the panel
|
|
// rectangle. Postmultiply scaling factors that match the
|
|
// pixel-space size of the panel.
|
|
for(i=0; i<4; i++) {
|
|
m[0+i] *= 1.0/_xmax;
|
|
m[4+i] *= 1.0/_ymax;
|
|
}
|
|
|
|
// Now plib initialization. The bounding sphere is defined nicely
|
|
// by our corner points:
|
|
float cx = (b[0]+c[0])/2;
|
|
float cy = (b[1]+c[1])/2;
|
|
float cz = (b[2]+c[2])/2;
|
|
float r = sqrt((cx-a[0])*(cx-a[0]) +
|
|
(cy-a[1])*(cy-a[1]) +
|
|
(cz-a[2])*(cz-a[2]));
|
|
bsphere.setCenter(cx, cy, cz);
|
|
bsphere.setRadius(r);
|
|
|
|
// All done. Add us to the list
|
|
all_3d_panels.push_back(this);
|
|
}
|
|
|
|
FGPanelNode::~FGPanelNode()
|
|
{
|
|
delete _panel;
|
|
}
|
|
|
|
void FGPanelNode::draw()
|
|
{
|
|
// What's the difference?
|
|
draw_geometry();
|
|
}
|
|
|
|
void FGPanelNode::draw_geometry()
|
|
{
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
glMultMatrixf(_xform);
|
|
|
|
// Grab the matrix state, so that we can get back from screen
|
|
// coordinates to panel coordinates when the user clicks the
|
|
// mouse.
|
|
glGetFloatv(GL_MODELVIEW_MATRIX, _lastModelview);
|
|
glGetFloatv(GL_PROJECTION_MATRIX, _lastProjection);
|
|
glGetIntegerv(GL_VIEWPORT, _lastViewport);
|
|
|
|
_panel->draw();
|
|
|
|
|
|
glPopMatrix();
|
|
}
|
|
|
|
bool FGPanelNode::doMouseAction(int button, int updown, int x, int y)
|
|
{
|
|
// Covert the screen coordinates to viewport coordinates in the
|
|
// range [0:1], then transform to OpenGL "post projection" coords
|
|
// in [-1:1]. Remember the difference in Y direction!
|
|
float vx = (x + 0.5 - _lastViewport[0]) / _lastViewport[2];
|
|
float vy = (y + 0.5 - _lastViewport[1]) / _lastViewport[3];
|
|
vx = 2*vx - 1;
|
|
vy = 1 - 2*vy;
|
|
|
|
// Make two vectors in post-projection coordinates at the given
|
|
// screen, one in the near field and one in the far field.
|
|
sgVec3 a, b;
|
|
a[0] = b[0] = vx;
|
|
a[1] = b[1] = vy;
|
|
a[2] = 0.75; // "Near" Z value
|
|
b[2] = -0.75; // "Far" Z value
|
|
|
|
// Run both vectors "backwards" through the OpenGL matrix
|
|
// transformation. Remember to w-normalize the vectors!
|
|
sgMat4 m;
|
|
sgMultMat4(m, *(sgMat4*)_lastProjection, *(sgMat4*)_lastModelview);
|
|
sgInvertMat4(m);
|
|
|
|
sgFullXformPnt3(a, m);
|
|
sgFullXformPnt3(b, m);
|
|
|
|
// And find their intersection on the z=0 plane. The resulting X
|
|
// and Y coordinates are the hit location in panel coordinates.
|
|
float dxdz = (b[0] - a[0]) / (b[2] - a[2]);
|
|
float dydz = (b[1] - a[1]) / (b[2] - a[2]);
|
|
int panelX = (int)(a[0] - a[2]*dxdz + 0.5);
|
|
int panelY = (int)(a[1] - a[2]*dydz + 0.5);
|
|
|
|
return _panel->doLocalMouseAction(button, updown, panelX, panelY);
|
|
}
|
|
|
|
void FGPanelNode::die()
|
|
{
|
|
SG_LOG(SG_ALL,SG_ALERT,"Unimplemented function called on FGPanelNode");
|
|
exit(1);
|
|
}
|
|
|