}
void
-FGPanelAction::addBinding (FGBinding * binding, int updown)
+FGPanelAction::addBinding (SGBinding * binding, int updown)
{
_bindings[updown].push_back(binding);
}
// Setters.
// transfer pointer ownership
- virtual void addBinding (FGBinding * binding, int updown);
+ virtual void addBinding (SGBinding * binding, int updown);
virtual void setButton (int button) { _button = button; }
virtual void setX (int x) { _x = x; }
virtual void setY (int y) { _y = y; }
virtual bool doAction (int updown);
private:
- typedef vector<FGBinding *> binding_list_t;
+ typedef vector<SGBinding *> binding_list_t;
int _button;
int _x;
// button-less actions are fired initially
if (!node->hasValue("w") || !node->hasValue("h")) {
for (i = 0; i < bindings.size(); i++) {
- FGBinding b(bindings[i]);
+ SGBinding b(bindings[i], globals->get_props());
b.fire();
}
return 0;
binding = dest->getChild("binding", j, true);
copyProperties(bindings[i], binding);
- action->addBinding(new FGBinding(binding), 0);
+ action->addBinding(new SGBinding(binding, globals->get_props()), 0);
}
if (node->hasChild("mod-up")) {
binding = dest->getChild("binding", j, true);
copyProperties(bindings[i], binding);
- action->addBinding(new FGBinding(binding), 1);
+ action->addBinding(new SGBinding(binding, globals->get_props()), 1);
}
}
virtual ~GUIInfo ();
FGDialog * dialog;
- vector <FGBinding *> bindings;
+ vector <SGBinding *> bindings;
int key;
};
binding = dest->getChild("binding", j, true);
copyProperties(bindings[i], binding);
- info->bindings.push_back(new FGBinding(binding));
+ info->bindings.push_back(new SGBinding(binding, globals->get_props()));
}
object->setCallback(action_callback);
class FGDialog;
-class FGBinding;
class NewGUI;
class FGColor;
FGMenuBar::fireItem (puObject * item)
{
const char * name = item->getLegend();
- vector<FGBinding *> &bindings = _bindings[name];
+ vector<SGBinding *> &bindings = _bindings[name];
int nBindings = bindings.size();
for (int i = 0; i < nBindings; i++)
binding = dest->getChild("binding", m, true);
copyProperties(bindings[k], binding);
- _bindings[items[j]].push_back(new FGBinding(binding));
+ _bindings[items[j]].push_back(new SGBinding(binding, globals->get_props()));
}
}
// Delete all those bindings
SG_LOG(SG_GENERAL, SG_INFO, "Deleting bindings");
- map<string,vector<FGBinding *> >::iterator it;
+ map<string,vector<SGBinding *> >::iterator it;
it = _bindings.begin();
for (it = _bindings.begin(); it != _bindings.end(); it++) {
SG_LOG(SG_GENERAL, SG_INFO, "Deleting bindings for " << it->first);
class puMenuBar;
class puObject;
-class FGBinding;
+class SGBinding;
/**
puMenuBar * _menuBar;
// A map of bindings for the menubar.
- map<string,vector<FGBinding *> > _bindings;
+ map<string,vector<SGBinding *> > _bindings;
// These are hoops that we have to jump through because PUI doesn't
// do memory management for lists. We have to allocate the arrays,
#include <Main/fg_props.hxx>
+class SGBinding;
+
class FGMenuBar;
class FGDialog;
-class FGBinding;
class FGColor;
class FGFontCache;
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
+#include <simgear/math/SGMath.hxx>
#include <simgear/props/props.hxx>
+#include <simgear/scene/util/SGSceneUserData.hxx>
#include <Aircraft/aircraft.hxx>
#include <Autopilot/xmlauto.hxx>
#include <Scenery/scenery.hxx>
#include <Main/renderer.hxx>
-#include <simgear/math/sg_geodesy.hxx>
SG_USING_STD(ifstream);
SG_USING_STD(string);
return bool(fgGetKeyModifiers() & KEYMOD_ALT);
}
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of FGBinding.
-////////////////////////////////////////////////////////////////////////
-
-FGBinding::FGBinding ()
- : _command(0),
- _arg(new SGPropertyNode),
- _setting(0)
-{
-}
-
-FGBinding::FGBinding (const SGPropertyNode * node)
- : _command(0),
- _arg(0),
- _setting(0)
-{
- read(node);
-}
-
-FGBinding::~FGBinding ()
-{
- _arg->getParent()->removeChild(_arg->getName(), _arg->getIndex(), false);
-}
-
-void
-FGBinding::read (const SGPropertyNode * node)
-{
- const SGPropertyNode * conditionNode = node->getChild("condition");
- if (conditionNode != 0)
- setCondition(sgReadCondition(globals->get_props(), conditionNode));
-
- _command_name = node->getStringValue("command", "");
- if (_command_name.empty()) {
- SG_LOG(SG_INPUT, SG_WARN, "No command supplied for binding.");
- _command = 0;
- return;
- }
-
- _arg = (SGPropertyNode *)node;
- _setting = 0;
-}
-
-void
-FGBinding::fire () const
-{
- if (test()) {
- if (_command == 0)
- _command = globals->get_commands()->getCommand(_command_name);
- if (_command == 0) {
- SG_LOG(SG_INPUT, SG_WARN, "No command attached to binding");
- } else if (!(*_command)(_arg)) {
- SG_LOG(SG_INPUT, SG_ALERT, "Failed to execute command "
- << _command_name);
- }
- }
-}
-
-void
-FGBinding::fire (double offset, double max) const
-{
- if (test()) {
- _arg->setDoubleValue("offset", offset/max);
- fire();
- }
-}
-
-void
-FGBinding::fire (double setting) const
-{
- if (test()) {
- // A value is automatically added to
- // the args
- if (_setting == 0) // save the setting node for efficiency
- _setting = _arg->getChild("setting", 0, true);
- _setting->setDoubleValue(setting);
- fire();
- }
-}
-
-
\f
////////////////////////////////////////////////////////////////////////
// Implementation of FGInput.
// Pass on to PUI and the panel if
// requested, and return if one of
// them consumes the event.
+
+ if (updown != MOUSE_BUTTON_DOWN) {
+ // Execute the mouse up event in any case, may be we should
+ // stop processing here?
+ while (!_activePickCallbacks[b].empty()) {
+ _activePickCallbacks[b].front()->buttonReleased();
+ _activePickCallbacks[b].pop_front();
+ }
+ }
+
if (mode.pass_through) {
if (puMouse(b, updown, x, y))
return;
else if (fgHandle3DPanelMouseEvent(b, updown, x, y))
return;
else {
- // pui and the panel didn't want the click event so compute a
- // terrain intersection point corresponding to the mouse click
- // and be happy.
- FGScenery* scenery = globals->get_scenery();
- SGVec3d start, dir, hit;
- if (!b && updown == MOUSE_BUTTON_DOWN
- && FGRenderer::getPickInfo(start, dir, x, y)
- && scenery->get_cart_ground_intersection(start, dir, hit)) {
-
- SGGeod geod = SGGeod::fromCart(hit);
- SGPropertyNode *c = fgGetNode("/sim/input/click", true);
- c->setDoubleValue("longitude-deg", geod.getLongitudeDeg());
- c->setDoubleValue("latitude-deg", geod.getLatitudeDeg());
- c->setDoubleValue("elevation-m", geod.getElevationM());
- c->setDoubleValue("elevation-ft", geod.getElevationFt());
-
- fgSetBool("/sim/signals/click", 1);
+ // pui didn't want the click event so compute a
+ // scenegraph intersection point corresponding to the mouse click
+ if (updown == MOUSE_BUTTON_DOWN) {
+ FGScenery* scenery = globals->get_scenery();
+ SGVec3d start, dir;
+
+ // Get the list of hit callbacks. Take the first callback that
+ // accepts the mouse button press and ignore the rest of them
+ // That is they get sorted by distance and by scenegraph depth.
+ // The nearest one is the first one and the deepest
+ // (the most specialized one in the scenegraph) is the first.
+ if (FGRenderer::getPickInfo(start, dir, x, y)) {
+ std::vector<SGSceneryPick> pickList;
+ scenery->pick(start, dir, pickList);
+ std::vector<SGSceneryPick>::const_iterator i;
+ for (i = pickList.begin(); i != pickList.end(); ++i) {
+ if (i->callback->buttonPressed(b, i->info)) {
+ _activePickCallbacks[b].push_back(i->callback);
+ break;
+ }
+ }
+ }
}
}
}
- // OK, PUI and the panel didn't want the click
+ // OK, PUI and the panel didn't want the click
if (b >= MAX_MOUSE_BUTTONS) {
SG_LOG(SG_INPUT, SG_ALERT, "Mouse button " << b
<< " where only " << MAX_MOUSE_BUTTONS << " expected");
m.save_x = m.x;
m.save_y = m.y;
}
+
+ // handle repeatable mouse press events
+ std::map<int, std::list<SGSharedPtr<SGPickCallback> > >::iterator mi;
+ for (mi = _activePickCallbacks.begin();
+ mi != _activePickCallbacks.end(); ++mi) {
+ std::list<SGSharedPtr<SGPickCallback> >::iterator li;
+ for (li = mi->second.begin(); li != mi->second.end(); ++li) {
+ (*li)->update(dt);
+ }
+ }
}
void
// The press event may be repeated.
if (!b.last_state || b.is_repeatable) {
SG_LOG( SG_INPUT, SG_DEBUG, "Button has been pressed" );
- for (unsigned int k = 0; k < b.bindings[modifiers].size(); k++)
+ for (unsigned int k = 0; k < b.bindings[modifiers].size(); k++) {
b.bindings[modifiers][k]->fire(x, y);
+ }
}
} else {
// The release event is never repeated.
if (!strcmp(cmd, "nasal") && _module[0])
bindings[i]->setStringValue("module", _module);
- binding_list[modifiers].push_back(new FGBinding(bindings[i]));
+ binding_list[modifiers].push_back(new SGBinding(bindings[i], globals->get_props()));
}
// Read nested bindings for modifiers
}
-const vector<FGBinding *> &
+const FGInput::binding_list_t&
FGInput::_find_key_bindings (unsigned int k, int modifiers)
{
unsigned char kc = (unsigned char)k;
#include <simgear/misc/sg_path.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
-#include <simgear/structure/commands.hxx>
+#include <simgear/structure/SGBinding.hxx>
#include <simgear/props/condition.hxx>
#include <simgear/props/props.hxx>
+#include <simgear/scene/util/SGSceneUserData.hxx>
#include <Main/fg_os.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <map>
+#include <list>
#include <vector>
SG_USING_STD(map);
#endif
-\f
-////////////////////////////////////////////////////////////////////////
-// General binding support.
-////////////////////////////////////////////////////////////////////////
-
-
-/**
- * An input binding of some sort.
- *
- * <p>This class represents a binding that can be assigned to a
- * keyboard key, a joystick button or axis, or even a panel
- * instrument.</p>
- */
-class FGBinding : public SGConditional
-{
-public:
-
- /**
- * Default constructor.
- */
- FGBinding ();
-
-
- /**
- * Convenience constructor.
- *
- * @param node The binding will be built from this node.
- */
- FGBinding (const SGPropertyNode * node);
-
-
- /**
- * Destructor.
- */
- virtual ~FGBinding ();
-
-
- /**
- * Get the command name.
- *
- * @return The string name of the command for this binding.
- */
- virtual const string &getCommandName () const { return _command_name; }
-
-
- /**
- * Get the command itself.
- *
- * @return The command associated with this binding, or 0 if none
- * is present.
- */
- virtual SGCommandMgr::command_t getCommand () const { return _command; }
-
-
- /**
- * Get the argument that will be passed to the command.
- *
- * @return A property node that will be passed to the command as its
- * argument, or 0 if none was supplied.
- */
- virtual const SGPropertyNode * getArg () { return _arg; }
-
-
- /**
- * Read a binding from a property node.
- *
- * @param node The property node containing the binding.
- */
- virtual void read (const SGPropertyNode * node);
-
-
- /**
- * Fire a binding.
- */
- virtual void fire () const;
-
-
- /**
- * Fire a binding with a scaled movement (rather than absolute position).
- */
- virtual void fire (double offset, double max) const;
-
-
- /**
- * Fire a binding with a setting (i.e. joystick axis).
- *
- * A double 'setting' property will be added to the arguments.
- *
- * @param setting The input setting, usually between -1.0 and 1.0.
- */
- virtual void fire (double setting) const;
-
-
-private:
- // just to be safe.
- FGBinding (const FGBinding &binding);
-
- string _command_name;
- mutable SGCommandMgr::command_t _command;
- mutable SGPropertyNode_ptr _arg;
- mutable SGPropertyNode_ptr _setting;
-};
-
-
\f
////////////////////////////////////////////////////////////////////////
// General input mapping support.
struct mouse;
friend struct mouse;
- typedef vector<FGBinding *> binding_list_t;
+ typedef vector<SGSharedPtr<SGBinding> > binding_list_t;
/**
* Settings for a key or button.
/**
* Look up the bindings for a key code.
*/
- const vector<FGBinding *> &_find_key_bindings (unsigned int k,
- int modifiers);
+ const binding_list_t& _find_key_bindings (unsigned int k,
+ int modifiers);
button _key_bindings[MAX_KEYS];
joystick _joystick_bindings[MAX_JOYSTICKS];
* Nasal module name/namespace.
*/
char _module[32];
+
+ /**
+ * List of currently pressed mouse button events
+ */
+ std::map<int, std::list<SGSharedPtr<SGPickCallback> > > _activePickCallbacks;
};
#endif // _INPUT_HXX
#include <osg/LightSource>
#include <osg/NodeCallback>
#include <osg/Notify>
-#include <osg/MatrixTransform>
-#include <osg/Multisample>
-#include <osg/Point>
#include <osg/PolygonMode>
+#include <osg/PolygonOffset>
#include <osg/ShadeModel>
#include <osg/TexEnv>
-#include <osg/TexEnvCombine>
-#include <osg/TexGen>
-#include <osg/TexMat>
-#include <osg/ColorMatrix>
#include <osgUtil/SceneView>
#include <osgUtil/UpdateVisitor>
|| (fgGetBool("/sim/ai-traffic/enabled")))
globals->get_ATC_display()->update(delta_time_sec, state);
-
puDisplay();
// Fade out the splash screen over the first three seconds.
osg::initNotifyLevel();
+ // The number of polygon-offset "units" to place between layers. In
+ // principle, one is supposed to be enough. In practice, I find that
+ // my hardware/driver requires many more.
+ osg::PolygonOffset::setUnitsMultiplier(1);
+ osg::PolygonOffset::setFactorMultiplier(1);
+
// Go full screen if requested ...
if ( fgGetBool("/sim/startup/fullscreen") )
fgOSFullScreen();
mUpdateVisitor->setLight(direction, l->scene_ambient(),
l->scene_diffuse(), l->scene_specular());
mUpdateVisitor->setVisibility(actual_visibility);
+
+ if (fgGetBool("/sim/panel-hotspots"))
+ sceneView->setCullMask(~0u);
+ else
+ sceneView->setCullMask(~SG_NODEMASK_PICK_BIT);
+
sceneView->update();
sceneView->cull();
sceneView->draw();
setFOV( viewmgr->get_current_view()->get_h_fov(),
viewmgr->get_current_view()->get_v_fov() );
- // cout << "setFOV(" << viewmgr->get_current_view()->get_h_fov()
- // << ", " << viewmgr->get_current_view()->get_v_fov() << ")"
- // << endl;
}
}
#include <simgear/scene/model/placementtrans.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/util/SGNodeMasks.hxx>
+#include <simgear/scene/util/SGSceneUserData.hxx>
#include <Main/fg_props.hxx>
#include "scenery.hxx"
+class FGGroundPickCallback : public SGPickCallback {
+public:
+ virtual bool buttonPressed(int button, const Info& info)
+ {
+ // only on left mouse button
+ if (button != 0)
+ return false;
+
+ SGGeod geod = SGGeod::fromCart(info.wgs84);
+ SG_LOG( SG_TERRAIN, SG_INFO, "Got ground pick at " << geod );
+
+ SGPropertyNode *c = fgGetNode("/sim/input/click", true);
+ c->setDoubleValue("longitude-deg", geod.getLongitudeDeg());
+ c->setDoubleValue("latitude-deg", geod.getLatitudeDeg());
+ c->setDoubleValue("elevation-m", geod.getElevationM());
+ c->setDoubleValue("elevation-ft", geod.getElevationFt());
+ fgSetBool("/sim/signals/click", 1);
+
+ return true;
+ }
+};
// Scenery Management system
FGScenery::FGScenery() :
terrain_branch = new osg::Group;
terrain_branch->setName( "Terrain" );
scene_graph->addChild( terrain_branch.get() );
+ SGSceneUserData* userData;
+ userData = SGSceneUserData::getOrCreateSceneUserData(terrain_branch.get());
+ userData->setPickCallback(new FGGroundPickCallback);
models_branch = new osg::Group;
models_branch->setName( "Models" );
return hits;
}
+static const osgUtil::Hit*
+getNearestHit(const osgUtil::IntersectVisitor::HitList& hitList,
+ const SGVec3d& start)
+{
+ const osgUtil::Hit* nearestHit = 0;
+ double dist = SGLimitsd::max();
+ osgUtil::IntersectVisitor::HitList::const_iterator hit;
+ for (hit = hitList.begin(); hit != hitList.end(); ++hit) {
+ SGVec3d point(hit->getWorldIntersectPoint());
+ double newdist = length(start - point);
+ if (newdist < dist) {
+ dist = newdist;
+ nearestHit = &*hit;
+ }
+ }
+
+ return nearestHit;
+}
+
bool
FGScenery::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir,
SGVec3d& nearestHit, bool exact)
// Make really sure the direction is normalized, is really cheap compared to
// computation of ground intersection.
SGVec3d start = pos - center;
- SGVec3d end = start + 1e5*dir;
+ SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ???
osgUtil::IntersectVisitor intersectVisitor;
intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
return hits;
}
+void
+FGScenery::pick(const SGVec3d& pos, const SGVec3d& dir,
+ std::vector<SGSceneryPick>& pickList)
+{
+ pickList.clear();
+
+ // Make really sure the direction is normalized, is really cheap compared to
+ // computation of ground intersection.
+ SGVec3d start = pos - center;
+ SGVec3d end = start + 1e5*normalize(dir); // FIXME visibility ???
+
+ osgUtil::IntersectVisitor intersectVisitor;
+// osgUtil::PickVisitor intersectVisitor;
+// intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
+ osg::ref_ptr<osg::LineSegment> lineSegment;
+ lineSegment = new osg::LineSegment(start.osg(), end.osg());
+ intersectVisitor.addLineSegment(lineSegment.get());
+ get_scene_graph()->accept(intersectVisitor);
+ if (!intersectVisitor.hits())
+ return;
+
+ // collect all interaction callbacks on the pick ray.
+ // They get stored in the pickCallbacks list where they are sorted back
+ // to front and croasest to finest wrt the scenery node they are attached to
+ osgUtil::IntersectVisitor::HitList::const_iterator hi;
+ for (hi = intersectVisitor.getHitList(lineSegment.get()).begin();
+ hi != intersectVisitor.getHitList(lineSegment.get()).end();
+ ++hi) {
+
+ // ok, go back the nodes and ask for intersection callbacks,
+ // execute them in top down order
+ const osg::NodePath& np = hi->getNodePath();
+ osg::NodePath::const_reverse_iterator npi;
+ for (npi = np.rbegin(); npi != np.rend(); ++npi) {
+ SGSceneUserData* ud = SGSceneUserData::getSceneUserData(*npi);
+ if (!ud)
+ continue;
+ SGPickCallback* pickCallback = ud->getPickCallback();
+ if (!pickCallback)
+ continue;
+
+ SGSceneryPick sceneryPick;
+ sceneryPick.info.wgs84 = center + SGVec3d(hi->getWorldIntersectPoint());
+ sceneryPick.info.local = SGVec3d(hi->getLocalIntersectPoint());
+ sceneryPick.callback = pickCallback;
+ pickList.push_back(sceneryPick);
+ }
+ }
+}
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/math/point3d.hxx>
#include <simgear/scene/model/placementtrans.hxx>
+#include <simgear/scene/util/SGPickCallback.hxx>
SG_USING_STD(list);
/// On success, true is returned.
bool get_cart_ground_intersection(const SGVec3d& start, const SGVec3d& dir,
SGVec3d& nearestHit, bool exact = false);
+ void pick(const SGVec3d& pos, const SGVec3d& dir,
+ std::vector<SGSceneryPick>& pickList);
const SGVec3d& get_center() const { return center; }
void set_center( const SGVec3d& p );