From 72017fc671e64a8c613e792ae65232fb67f0df92 Mon Sep 17 00:00:00 2001 From: curt Date: Tue, 29 Oct 2002 19:44:03 +0000 Subject: [PATCH] Andy Ross: The biggest and coolest patch adds mouse sensitivity to the 3D cockpits, so we can finally work the radios. This ended up requiring significant modifications outside of the 3D cockpit code. Stuff folks will want to look at: + The list of all "3D" cockpits is stored statically in the panelnode.cxx file. This is clumsy, and won't migrate well to a multiple-aircraft feature. Really, there should be a per-model list of 3D panels, but I couldn't find a clean place to put this. The only handle you get back after parsing a model is a generic ssg node, to which I obviously can't add panel-specific methods. + The aircraft model is parsed *very* early in the initialization order. Earlier, in fact, than the static list of allowable command bindings is built in fgInitCommands(). This is bad, as it means that mouse bindings on the instruments can't work yet. I moved the call to fgInitCommands, but someone should look carefully to see that I picked the right place. There's a lot of initialization code, and I got a little lost in there... :) + I added yet another "update" hook to the fgRenderFrame routine to hook the updates for the 3D panels. This is only required for "mouse press delay", and it's a fairly clumsy mechanism based on frame rate instead of real time. There appears to be delay handling already in place in the Input stuff, and there's a discussion going on about different mouse behavior right now. Maybe this is a good time to unify these two (now three) approaches? --- src/Cockpit/panel.cxx | 93 ++++++++++++++++++++++++--------------- src/Cockpit/panel.hxx | 3 ++ src/Input/input.cxx | 5 ++- src/Main/fg_init.cxx | 6 --- src/Main/main.cxx | 8 ++++ src/Model/panelnode.cxx | 89 +++++++++++++++++++++++++++++++++++-- src/Model/panelnode.hxx | 10 ++++- src/Scenery/tileentry.cxx | 10 ++++- 8 files changed, 175 insertions(+), 49 deletions(-) diff --git a/src/Cockpit/panel.cxx b/src/Cockpit/panel.cxx index 6ec50fd59..d2332b9d8 100644 --- a/src/Cockpit/panel.cxx +++ b/src/Cockpit/panel.cxx @@ -279,14 +279,7 @@ FGPanel::update (double dt) return; } - // If the mouse is down, do something - if (_mouseDown) { - _mouseDelay--; - if (_mouseDelay < 0) { - _mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY); - _mouseDelay = 2; - } - } + updateMouseDelay(); // Now, draw the panel float aspect_adjust = get_aspect_adjust(_xsize_node->getIntValue(), @@ -297,6 +290,22 @@ FGPanel::update (double dt) update(WIN_X, WIN_W, WIN_Y, int(WIN_H / aspect_adjust)); } +/** + * Handle repeatable mouse events. Called from update() and from + * fgUpdate3DPanels(). This functionality needs to move into the + * input subsystem. Counting a tick every two frames is clumsy... + */ +void FGPanel::updateMouseDelay() +{ + if (_mouseDown) { + _mouseDelay--; + if (_mouseDelay < 0) { + _mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY); + _mouseDelay = 2; + } + } +} + void FGPanel::update (GLfloat winx, GLfloat winw, GLfloat winy, GLfloat winh) @@ -493,6 +502,42 @@ FGPanel::setYOffset (int offset) _y_offset = offset; } +/** + * Handle a mouse action in panel-local (not screen) coordinates. + * Used by the 3D panel code in Model/panelnode.cxx, in situations + * where the panel doesn't control its own screen location. + */ +bool +FGPanel::doLocalMouseAction(int button, int updown, int x, int y) +{ + // Note a released button and return + if (updown == 1) { + _mouseDown = false; + _mouseInstrument = 0; + return false; + } + + // Search for a matching instrument. + for (int i = 0; i < (int)_instruments.size(); i++) { + FGPanelInstrument *inst = _instruments[i]; + int ix = inst->getXPos(); + int iy = inst->getYPos(); + int iw = inst->getWidth() / 2; + int ih = inst->getHeight() / 2; + if (x >= ix - iw && x < ix + iw && y >= iy - ih && y < iy + ih) { + _mouseDown = true; + _mouseDelay = 20; + _mouseInstrument = inst; + _mouseButton = button; + _mouseX = x - ix; + _mouseY = y - iy; + // Always do the action once. + return _mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY); + } + } + return false; +} + /** * Perform a mouse action. */ @@ -504,14 +549,6 @@ FGPanel::doMouseAction (int button, int updown, int x, int y) int ysize = _ysize_node->getIntValue(); float aspect_adjust = get_aspect_adjust(xsize, ysize); - // Note a released button and return - // cerr << "Doing mouse action\n"; - if (updown == 1) { - _mouseDown = false; - _mouseInstrument = 0; - return false; - } - // Scale for the real window size. if (aspect_adjust < 1.0) { x = int(((float)x / xsize) * WIN_W * aspect_adjust); @@ -525,26 +562,10 @@ FGPanel::doMouseAction (int button, int updown, int x, int y) x -= _x_offset; y -= _y_offset; - // Search for a matching instrument. - for (int i = 0; i < (int)_instruments.size(); i++) { - FGPanelInstrument *inst = _instruments[i]; - int ix = inst->getXPos(); - int iy = inst->getYPos(); - int iw = inst->getWidth() / 2; - int ih = inst->getHeight() / 2; - if (x >= ix - iw && x < ix + iw && y >= iy - ih && y < iy + ih) { - _mouseDown = true; - _mouseDelay = 20; - _mouseInstrument = inst; - _mouseButton = button; - _mouseX = x - ix; - _mouseY = y - iy; - // Always do the action once. - return _mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY); - } - } - return false; -} + // Having fixed up the coordinates, fall through to the local + // coordinate handler. + doLocalMouseAction(button, updown, x, y); +} diff --git a/src/Cockpit/panel.hxx b/src/Cockpit/panel.hxx index d0d1b9805..0b41c2d25 100644 --- a/src/Cockpit/panel.hxx +++ b/src/Cockpit/panel.hxx @@ -145,6 +145,8 @@ public: virtual void update (double dt); virtual void update (GLfloat winx, GLfloat winw, GLfloat winy, GLfloat winh); + virtual void updateMouseDelay(); + // transfer pointer ownership!!! virtual void addInstrument (FGPanelInstrument * instrument); @@ -180,6 +182,7 @@ public: // Handle a mouse click. virtual bool doMouseAction (int button, int updown, int x, int y); + virtual bool doLocalMouseAction(int button, int updown, int x, int y); private: void setupVirtualCockpit(); diff --git a/src/Input/input.cxx b/src/Input/input.cxx index 818b7a352..21df71d57 100644 --- a/src/Input/input.cxx +++ b/src/Input/input.cxx @@ -54,6 +54,7 @@ #include #include #include +#include #include
#include
@@ -378,7 +379,9 @@ FGInput::doMouseClick (int b, int updown, int x, int y) if (puMouse(b, updown, x, y)) return; else if ((current_panel != 0) && - current_panel->doMouseAction(b, updown, x, y)) + current_panel->doMouseAction(b, updown, x, y)) + return; + else if (fgHandle3DPanelMouseEvent(b, updown, x, y)) return; } diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 65c52b1e3..af0c1d25c 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -1125,12 +1125,6 @@ bool fgInitSubsystems( void ) { globals->get_AI_mgr()->init(); } - //////////////////////////////////////////////////////////////////// - // Initialize the built-in commands. - //////////////////////////////////////////////////////////////////// - fgInitCommands(); - - #ifdef ENABLE_AUDIO_SUPPORT //////////////////////////////////////////////////////////////////// // Initialize the sound subsystem. diff --git a/src/Main/main.cxx b/src/Main/main.cxx index 4c402c8d3..1c1de3625 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -107,6 +107,7 @@ SG_USING_STD(endl); #include #include #include
+#include #ifdef FG_NETWORK_OLK #include #endif @@ -133,6 +134,7 @@ SG_USING_STD(endl); #include #include +#include "fg_commands.hxx" // #define FG_EXPERIMENTAL_LIGHTING #ifdef FG_EXPERIMENTAL_LIGHTING @@ -840,6 +842,7 @@ void fgRenderFrame() { if ( current_panel != NULL ) { current_panel->update(delta_time_sec); } + fgUpdate3DPanels(); // We can do translucent menus, so why not. :-) menus->apply(); @@ -1637,6 +1640,11 @@ int mainLoop( int argc, char **argv ) { globals->get_scenery()->init(); globals->get_scenery()->bind(); + //////////////////////////////////////////////////////////////////// + // Initialize the property-based built-in commands + //////////////////////////////////////////////////////////////////// + fgInitCommands(); + //////////////////////////////////////////////////////////////////// // Initialize the general model subsystem. //////////////////////////////////////////////////////////////////// diff --git a/src/Model/panelnode.cxx b/src/Model/panelnode.cxx index 5b979f6aa..ee6c9a0c6 100644 --- a/src/Model/panelnode.cxx +++ b/src/Model/panelnode.cxx @@ -2,18 +2,51 @@ # include #endif +#include + +#include +#include + #include
#include #include -#include #include "panelnode.hxx" +// Static (!) handling for all 3D panels in the program. Very +// clumsy. Replace with per-aircraft handling. +vector all_3d_panels; +bool fgHandle3DPanelMouseEvent(int button, int updown, int x, int y) +{ + for(int i=0; idoMouseAction(button, updown, x, y)) + return true; + return false; +} + +void fgUpdate3DPanels() +{ + for(int i=0; igetPanel()->updateMouseDelay(); +} + FGPanelNode::FGPanelNode(SGPropertyNode* props) { // 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(int 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(); @@ -34,7 +67,7 @@ FGPanelNode::FGPanelNode(SGPropertyNode* props) _bottomRight[1] = pt->getFloatValue("y-m"); _bottomRight[2] = pt->getFloatValue("z-m"); - // Now generate out transformation matrix. For shorthand, use + // 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. @@ -74,6 +107,9 @@ FGPanelNode::FGPanelNode(SGPropertyNode* props) (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() @@ -92,13 +128,60 @@ 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"); - *(int*)0=0; + exit(1); } diff --git a/src/Model/panelnode.hxx b/src/Model/panelnode.hxx index f3c75daa6..0aee8d7fb 100644 --- a/src/Model/panelnode.hxx +++ b/src/Model/panelnode.hxx @@ -8,6 +8,12 @@ class SGPropertyNode; // many methods, mostly involved with modelling and runtime // inspection, are unimplemented. +// Static mouse handler for all FGPanelNodes. Very clumsy; this +// should really be done through our container (an aircraft model, +// typically). +bool fgHandle3DPanelMouseEvent(int button, int updown, int x, int y); +void fgUpdate3DPanels(); + class FGPanelNode : public ssgLeaf { protected: @@ -18,7 +24,9 @@ public: virtual ~FGPanelNode(); virtual void draw(); - void mouseEvent(int button, int updown, int x, int y); + bool doMouseAction(int button, int updown, int x, int y); + + FGPanel* getPanel() { return _panel; } virtual void recalcBSphere() { bsphere_is_invalid = 0; } diff --git a/src/Scenery/tileentry.cxx b/src/Scenery/tileentry.cxx index 49029c3bb..80a1621cb 100644 --- a/src/Scenery/tileentry.cxx +++ b/src/Scenery/tileentry.cxx @@ -886,10 +886,16 @@ void FGTileEntry::prep_ssg_node( const Point3D& p, sgVec3 up, float vis) { sgVec3 lift_vec; sgCopyVec3( lift_vec, up ); + // we fudge agl by 30 meters so that the lifting function + // doesn't phase in until we are > 30m agl. double agl; agl = globals->get_current_view()->getAltitudeASL_ft() - * SG_FEET_TO_METER - globals->get_scenery()->get_cur_elev(); - + * SG_FEET_TO_METER - globals->get_scenery()->get_cur_elev() + - 30.0; + if ( agl < 30.0 ) { + agl = 0.0; + } + // sgTrans just happens to be the // vector from scenery center to the center of this tile which // is what we want to calculate the distance of