Allow Canvas placed on 3D objects receiving mouse events.
- Add option 'capture-events' to canvas aircraft and scenery placements to allow events being forwarded to the respective canvas. - Clean up and restructure parts of the mouse event/picking handling to support forwarding events to canvasses.
This commit is contained in:
parent
98bfbb9560
commit
d9881aecf8
6 changed files with 187 additions and 132 deletions
|
@ -50,7 +50,8 @@ static sc::Placements addSceneObjectPlacement( SGPropertyNode* placement,
|
|||
model_data->getNode(),
|
||||
placement,
|
||||
canvas->getTexture(),
|
||||
canvas->getCullCallback()
|
||||
canvas->getCullCallback(),
|
||||
canvas
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -76,7 +77,8 @@ CanvasMgr::CanvasMgr():
|
|||
&FGODGauge::set_aircraft_texture,
|
||||
_1,
|
||||
boost::bind(&sc::Canvas::getTexture, _2),
|
||||
boost::bind(&sc::Canvas::getCullCallback, _2)
|
||||
boost::bind(&sc::Canvas::getCullCallback, _2),
|
||||
_2
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -294,8 +294,7 @@ bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
|
|||
return false;
|
||||
|
||||
namespace sc = simgear::canvas;
|
||||
sc::MouseEventPtr event(new sc::MouseEvent);
|
||||
event->time = ea.getTime();
|
||||
sc::MouseEventPtr event(new sc::MouseEvent(ea));
|
||||
|
||||
event->screen_pos.x() = 0.5 * (ea.getXnormalized() + 1) * _width + 0.5;
|
||||
event->screen_pos.y() = 0.5 * (ea.getYnormalized() + 1) * _height + 0.5;
|
||||
|
@ -310,9 +309,6 @@ bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
|
|||
_last_y = event->getScreenY();
|
||||
|
||||
event->client_pos = event->screen_pos;
|
||||
event->button = ea.getButton();
|
||||
event->state = ea.getButtonMask();
|
||||
event->mod = ea.getModKeyMask();
|
||||
|
||||
if( !_resize_window.expired() )
|
||||
{
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
#include <osg/Camera>
|
||||
#include <osg/Geode>
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osg/Material>
|
||||
#include <osg/Matrix>
|
||||
#include <osg/PolygonMode>
|
||||
#include <osg/ShadeModel>
|
||||
|
@ -45,6 +44,7 @@
|
|||
|
||||
#include <osgDB/FileNameUtils>
|
||||
|
||||
#include <simgear/canvas/CanvasObjectPlacement.hxx>
|
||||
#include <simgear/scene/material/EffectGeode.hxx>
|
||||
#include <simgear/scene/util/RenderConstants.hxx>
|
||||
|
||||
|
@ -86,12 +86,15 @@ class ReplaceStaticTextureVisitor:
|
|||
osg::Texture2D* new_texture ):
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
|
||||
_tex_name( osgDB::getSimpleFileName(name) ),
|
||||
_new_texture(new_texture)
|
||||
_new_texture(new_texture),
|
||||
_cull_callback(0)
|
||||
{}
|
||||
|
||||
ReplaceStaticTextureVisitor( SGPropertyNode* placement,
|
||||
osg::Texture2D* new_texture,
|
||||
osg::NodeCallback* cull_callback = 0 ):
|
||||
osg::NodeCallback* cull_callback = 0,
|
||||
const simgear::canvas::CanvasWeakPtr& canvas =
|
||||
simgear::canvas::CanvasWeakPtr() ):
|
||||
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
|
||||
_tex_name( osgDB::getSimpleFileName(
|
||||
placement->getStringValue("texture"))
|
||||
|
@ -100,7 +103,8 @@ class ReplaceStaticTextureVisitor:
|
|||
_parent_name( placement->getStringValue("parent") ),
|
||||
_node(placement),
|
||||
_new_texture(new_texture),
|
||||
_cull_callback(cull_callback)
|
||||
_cull_callback(cull_callback),
|
||||
_canvas(canvas)
|
||||
{
|
||||
if( _tex_name.empty()
|
||||
&& _node_name.empty()
|
||||
|
@ -200,7 +204,7 @@ class ReplaceStaticTextureVisitor:
|
|||
osg::StateAttribute::ON );
|
||||
|
||||
_placements.push_back( simgear::canvas::PlacementPtr(
|
||||
new ObjectPlacement(_node, group)
|
||||
new simgear::canvas::ObjectPlacement(_node, group, _canvas)
|
||||
));
|
||||
|
||||
SG_LOG
|
||||
|
@ -218,77 +222,6 @@ class ReplaceStaticTextureVisitor:
|
|||
|
||||
protected:
|
||||
|
||||
class ObjectPlacement:
|
||||
public simgear::canvas::Placement
|
||||
{
|
||||
public:
|
||||
|
||||
ObjectPlacement( SGPropertyNode* node,
|
||||
GroupPtr group ):
|
||||
Placement(node),
|
||||
_group(group)
|
||||
{
|
||||
// TODO make more generic and extendable for more properties
|
||||
if( node->hasValue("emission") )
|
||||
setEmission( node->getFloatValue("emission") );
|
||||
}
|
||||
|
||||
virtual bool childChanged(SGPropertyNode* node)
|
||||
{
|
||||
if( node->getParent() != _node )
|
||||
return false;
|
||||
|
||||
if( node->getNameString() == "emission" )
|
||||
setEmission( node->getFloatValue() );
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void setEmission(float emit)
|
||||
{
|
||||
emit = SGMiscf::clip(emit, 0, 1);
|
||||
|
||||
if( !_material )
|
||||
{
|
||||
_material = new osg::Material;
|
||||
_material->setColorMode(osg::Material::OFF);
|
||||
_material->setDataVariance(osg::Object::DYNAMIC);
|
||||
_group->getOrCreateStateSet()
|
||||
->setAttribute(_material, ( osg::StateAttribute::ON
|
||||
| osg::StateAttribute::OVERRIDE ) );
|
||||
}
|
||||
|
||||
_material->setEmission(
|
||||
osg::Material::FRONT_AND_BACK,
|
||||
osg::Vec4(emit, emit, emit, emit)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove placement from the scene
|
||||
*/
|
||||
virtual ~ObjectPlacement()
|
||||
{
|
||||
assert( _group->getNumChildren() == 1 );
|
||||
osg::Node *child = _group->getChild(0);
|
||||
|
||||
if( _group->getNumParents() )
|
||||
{
|
||||
osg::Group *parent = _group->getParent(0);
|
||||
parent->addChild(child);
|
||||
parent->removeChild(_group);
|
||||
}
|
||||
|
||||
_group->removeChild(child);
|
||||
}
|
||||
|
||||
private:
|
||||
GroupPtr _group;
|
||||
MaterialPtr _material;
|
||||
};
|
||||
|
||||
std::string _tex_name, ///<! Name of texture to be replaced
|
||||
_node_name, ///<! Only replace if node name matches
|
||||
_parent_name; ///<! Only replace if any parent node matches
|
||||
|
@ -298,7 +231,8 @@ class ReplaceStaticTextureVisitor:
|
|||
osg::Texture2D *_new_texture;
|
||||
osg::NodeCallback *_cull_callback;
|
||||
|
||||
simgear::canvas::Placements _placements;
|
||||
simgear::canvas::CanvasWeakPtr _canvas;
|
||||
simgear::canvas::Placements _placements;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -330,9 +264,13 @@ simgear::canvas::Placements
|
|||
FGODGauge::set_texture( osg::Node* branch,
|
||||
SGPropertyNode* placement,
|
||||
osg::Texture2D* new_texture,
|
||||
osg::NodeCallback* cull_callback )
|
||||
osg::NodeCallback* cull_callback,
|
||||
const simgear::canvas::CanvasWeakPtr& canvas )
|
||||
{
|
||||
ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback);
|
||||
ReplaceStaticTextureVisitor visitor( placement,
|
||||
new_texture,
|
||||
cull_callback,
|
||||
canvas );
|
||||
branch->accept(visitor);
|
||||
return visitor.getPlacements();
|
||||
}
|
||||
|
@ -341,13 +279,15 @@ FGODGauge::set_texture( osg::Node* branch,
|
|||
simgear::canvas::Placements
|
||||
FGODGauge::set_aircraft_texture( SGPropertyNode* placement,
|
||||
osg::Texture2D* new_texture,
|
||||
osg::NodeCallback* cull_callback )
|
||||
osg::NodeCallback* cull_callback,
|
||||
const simgear::canvas::CanvasWeakPtr& canvas )
|
||||
{
|
||||
return set_texture
|
||||
(
|
||||
globals->get_scenery()->get_aircraft_branch(),
|
||||
placement,
|
||||
new_texture,
|
||||
cull_callback
|
||||
cull_callback,
|
||||
canvas
|
||||
);
|
||||
}
|
||||
|
|
|
@ -86,7 +86,9 @@ class FGODGauge:
|
|||
set_texture( osg::Node* branch,
|
||||
SGPropertyNode* placement,
|
||||
osg::Texture2D* new_texture,
|
||||
osg::NodeCallback* cull_callback = 0 );
|
||||
osg::NodeCallback* cull_callback = 0,
|
||||
const simgear::canvas::CanvasWeakPtr& canvas =
|
||||
simgear::canvas::CanvasWeakPtr() );
|
||||
|
||||
/**
|
||||
* Replace an opengl texture name inside the aircraft scene graph.
|
||||
|
@ -101,7 +103,9 @@ class FGODGauge:
|
|||
simgear::canvas::Placements
|
||||
set_aircraft_texture( SGPropertyNode* placement,
|
||||
osg::Texture2D* new_texture,
|
||||
osg::NodeCallback* cull_callback = 0 );
|
||||
osg::NodeCallback* cull_callback = 0,
|
||||
const simgear::canvas::CanvasWeakPtr& canvas =
|
||||
simgear::canvas::CanvasWeakPtr() );
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -49,13 +49,19 @@ using std::ios_base;
|
|||
const int MAX_MICE = 1;
|
||||
const int MAX_MOUSE_BUTTONS = 8;
|
||||
|
||||
typedef std::vector<SGSceneryPick> SGSceneryPicks;
|
||||
typedef SGSharedPtr<SGPickCallback> SGPickCallbackPtr;
|
||||
typedef std::list<SGPickCallbackPtr> SGPickCallbackList;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* List of currently pressed mouse button events
|
||||
*/
|
||||
class ActivePickCallbacks : public std::map<int, std::list<SGSharedPtr<SGPickCallback> > > {
|
||||
public:
|
||||
class ActivePickCallbacks:
|
||||
public std::map<int, SGPickCallbackList>
|
||||
{
|
||||
public:
|
||||
void update( double dt, unsigned int keyModState );
|
||||
void init( int button, const osgGA::GUIEventAdapter* ea );
|
||||
};
|
||||
|
@ -71,14 +77,14 @@ void ActivePickCallbacks::init( int button, const osgGA::GUIEventAdapter* ea )
|
|||
// 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.
|
||||
std::vector<SGSceneryPick> pickList;
|
||||
SGSceneryPicks pickList;
|
||||
if (!globals->get_renderer()->pick(pickList, windowPos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<SGSceneryPick>::const_iterator i;
|
||||
SGSceneryPicks::const_iterator i;
|
||||
for (i = pickList.begin(); i != pickList.end(); ++i) {
|
||||
if (i->callback->buttonPressed(button, ea, i->info)) {
|
||||
if (i->callback->buttonPressed(button, *ea, i->info)) {
|
||||
(*this)[button].push_back(i->callback);
|
||||
return;
|
||||
}
|
||||
|
@ -89,7 +95,7 @@ void ActivePickCallbacks::update( double dt, unsigned int keyModState )
|
|||
{
|
||||
// handle repeatable mouse press events
|
||||
for( iterator mi = begin(); mi != end(); ++mi ) {
|
||||
std::list<SGSharedPtr<SGPickCallback> >::iterator li;
|
||||
SGPickCallbackList::iterator li;
|
||||
for (li = mi->second.begin(); li != mi->second.end(); ++li) {
|
||||
(*li)->update(dt, keyModState);
|
||||
}
|
||||
|
@ -130,6 +136,18 @@ struct mouse {
|
|||
mouse_mode * modes;
|
||||
};
|
||||
|
||||
static
|
||||
const SGSceneryPick*
|
||||
getPick( const SGSceneryPicks& pick_list,
|
||||
const SGPickCallback* cb )
|
||||
{
|
||||
for(size_t i = 0; i < pick_list.size(); ++i)
|
||||
if( pick_list[i].callback == cb )
|
||||
return &pick_list[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class FGMouseInput::FGMouseInputPrivate : public SGPropertyChangeListener
|
||||
|
@ -197,37 +215,44 @@ public:
|
|||
|
||||
void doHoverPick(const osg::Vec2d& windowPos)
|
||||
{
|
||||
std::vector<SGSceneryPick> pickList;
|
||||
SGPickCallback::Priority priority = SGPickCallback::PriorityScenery;
|
||||
|
||||
FGMouseCursor::Cursor cur = FGMouseCursor::CURSOR_ARROW;
|
||||
bool explicitCursor = false;
|
||||
bool didPick = false;
|
||||
|
||||
if (globals->get_renderer()->pick(pickList, windowPos)) {
|
||||
|
||||
SGPickCallback::Priority priority = SGPickCallback::PriorityScenery;
|
||||
SGSceneryPicks pickList;
|
||||
globals->get_renderer()->pick(pickList, windowPos);
|
||||
|
||||
SGSceneryPicks::const_iterator i;
|
||||
for( i = pickList.begin(); i != pickList.end(); ++i )
|
||||
{
|
||||
bool done = i->callback->hover(windowPos, i->info);
|
||||
std::string curName(i->callback->getCursor());
|
||||
if (!curName.empty()) {
|
||||
explicitCursor = true;
|
||||
cur = FGMouseCursor::cursorFromString(curName.c_str());
|
||||
}
|
||||
|
||||
std::vector<SGSceneryPick>::const_iterator i;
|
||||
for (i = pickList.begin(); i != pickList.end(); ++i) {
|
||||
bool done = i->callback->hover(windowPos, i->info);
|
||||
std::string curName(i->callback->getCursor());
|
||||
if (!curName.empty()) {
|
||||
explicitCursor = true;
|
||||
cur = FGMouseCursor::cursorFromString(curName.c_str());
|
||||
}
|
||||
|
||||
// if the callback is of higher prioirty (lower enum index),
|
||||
// record that.
|
||||
if (i->callback->getPriority() < priority) {
|
||||
priority = i->callback->getPriority();
|
||||
}
|
||||
|
||||
if (done) {
|
||||
didPick = true;
|
||||
break;
|
||||
}
|
||||
} // of picks iteration
|
||||
} else { // of have valid pick
|
||||
if (i->callback->getPriority() < priority) {
|
||||
priority = i->callback->getPriority();
|
||||
}
|
||||
|
||||
if (done) {
|
||||
didPick = true;
|
||||
break;
|
||||
}
|
||||
} // of picks iteration
|
||||
|
||||
// Check if any pick from the previous iteration has disappeared. If so
|
||||
// notify the callback that the mouse has left its element.
|
||||
for( i = _previous_picks.begin(); i != _previous_picks.end(); ++i )
|
||||
{
|
||||
if( !getPick(pickList, i->callback) )
|
||||
i->callback->mouseLeave(windowPos);
|
||||
}
|
||||
_previous_picks = pickList;
|
||||
|
||||
if (!explicitCursor && (priority == SGPickCallback::PriorityPanel)) {
|
||||
cur = FGMouseCursor::CURSOR_HAND;
|
||||
|
@ -245,12 +270,27 @@ public:
|
|||
{
|
||||
FGMouseCursor::Cursor cur = FGMouseCursor::CURSOR_CLOSED_HAND;
|
||||
|
||||
BOOST_FOREACH(SGPickCallback* cb, activePickCallbacks[0]) {
|
||||
cb->mouseMoved(ea);
|
||||
std::string curName(cb->getCursor());
|
||||
if (!curName.empty()) {
|
||||
cur = FGMouseCursor::cursorFromString(curName.c_str());
|
||||
}
|
||||
osg::Vec2d windowPos;
|
||||
flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
|
||||
|
||||
SGSceneryPicks pickList;
|
||||
if( !globals->get_renderer()->pick(pickList, windowPos) )
|
||||
return;
|
||||
|
||||
for( ActivePickCallbacks::iterator mi = activePickCallbacks.begin();
|
||||
mi != activePickCallbacks.end();
|
||||
++mi )
|
||||
{
|
||||
SGPickCallbackList::iterator li;
|
||||
for( li = mi->second.begin(); li != mi->second.end(); ++li )
|
||||
{
|
||||
const SGSceneryPick* pick = getPick(pickList, *li);
|
||||
(*li)->mouseMoved(*ea, pick ? &pick->info : 0);
|
||||
|
||||
std::string curName((*li)->getCursor());
|
||||
if( !curName.empty() )
|
||||
cur = FGMouseCursor::cursorFromString(curName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
FGMouseCursor::instance()->setCursor(cur);
|
||||
|
@ -278,6 +318,7 @@ public:
|
|||
}
|
||||
|
||||
ActivePickCallbacks activePickCallbacks;
|
||||
SGSceneryPicks _previous_picks;
|
||||
|
||||
mouse mice[MAX_MICE];
|
||||
|
||||
|
@ -513,12 +554,26 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
|
|||
// requested, and return if one of
|
||||
// them consumes the event.
|
||||
|
||||
if (updown != MOUSE_BUTTON_DOWN) {
|
||||
osg::Vec2d windowPos;
|
||||
flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
|
||||
|
||||
SGSceneryPicks pickList;
|
||||
globals->get_renderer()->pick(pickList, windowPos);
|
||||
|
||||
if( updown != MOUSE_BUTTON_DOWN )
|
||||
{
|
||||
// Execute the mouse up event in any case, may be we should
|
||||
// stop processing here?
|
||||
while (!d->activePickCallbacks[b].empty()) {
|
||||
d->activePickCallbacks[b].front()->buttonReleased(ea->getModKeyMask());
|
||||
d->activePickCallbacks[b].pop_front();
|
||||
|
||||
SGPickCallbackList& callbacks = d->activePickCallbacks[b];
|
||||
|
||||
while( !callbacks.empty() )
|
||||
{
|
||||
SGPickCallbackPtr& cb = callbacks.front();
|
||||
const SGSceneryPick* pick = getPick(pickList, cb);
|
||||
cb->buttonReleased(ea->getModKeyMask(), *ea, pick ? &pick->info : 0);
|
||||
|
||||
callbacks.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -540,8 +595,6 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
|
|||
}
|
||||
} else {
|
||||
// do a hover pick now, to fix up cursor
|
||||
osg::Vec2d windowPos;
|
||||
flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
|
||||
d->doHoverPick(windowPos);
|
||||
} // mouse button was released
|
||||
} // of pass-through mode
|
||||
|
|
|
@ -1723,6 +1723,62 @@ FGRenderer::resize( int width, int height )
|
|||
}
|
||||
}
|
||||
|
||||
typedef osgUtil::LineSegmentIntersector::Intersection Intersection;
|
||||
SGVec2d uvFromIntersection(const Intersection& hit)
|
||||
{
|
||||
// Taken from http://trac.openscenegraph.org/projects/osg/browser/OpenSceneGraph/trunk/examples/osgmovie/osgmovie.cpp
|
||||
|
||||
osg::Drawable* drawable = hit.drawable.get();
|
||||
osg::Geometry* geometry = drawable ? drawable->asGeometry() : 0;
|
||||
osg::Vec3Array* vertices =
|
||||
geometry ? dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray()) : 0;
|
||||
|
||||
if( !vertices )
|
||||
{
|
||||
SG_LOG(SG_INPUT, SG_WARN, "Unable to get vertices for intersection.");
|
||||
return SGVec2d(-9999,-9999);
|
||||
}
|
||||
|
||||
// get the vertex indices.
|
||||
const Intersection::IndexList& indices = hit.indexList;
|
||||
const Intersection::RatioList& ratios = hit.ratioList;
|
||||
|
||||
if( indices.size() != 3 || ratios.size() != 3 )
|
||||
{
|
||||
SG_LOG( SG_INPUT,
|
||||
SG_WARN,
|
||||
"Intersection has insufficient indices to work with." );
|
||||
return SGVec2d(-9999,-9999);
|
||||
}
|
||||
|
||||
unsigned int i1 = indices[0];
|
||||
unsigned int i2 = indices[1];
|
||||
unsigned int i3 = indices[2];
|
||||
|
||||
float r1 = ratios[0];
|
||||
float r2 = ratios[1];
|
||||
float r3 = ratios[2];
|
||||
|
||||
osg::Array* texcoords =
|
||||
(geometry->getNumTexCoordArrays() > 0) ? geometry->getTexCoordArray(0) : 0;
|
||||
osg::Vec2Array* texcoords_Vec2Array =
|
||||
dynamic_cast<osg::Vec2Array*>(texcoords);
|
||||
|
||||
if( !texcoords_Vec2Array )
|
||||
{
|
||||
SG_LOG(SG_INPUT, SG_WARN, "Unable to get texcoords for intersection.");
|
||||
return SGVec2d(-9999,-9999);
|
||||
}
|
||||
|
||||
// we have tex coord array so now we can compute the final tex coord at the
|
||||
// point of intersection.
|
||||
osg::Vec2 tc1 = (*texcoords_Vec2Array)[i1];
|
||||
osg::Vec2 tc2 = (*texcoords_Vec2Array)[i2];
|
||||
osg::Vec2 tc3 = (*texcoords_Vec2Array)[i3];
|
||||
|
||||
return toSG( osg::Vec2d(tc1 * r1 + tc2 * r2 + tc3 * r3) );
|
||||
}
|
||||
|
||||
bool
|
||||
FGRenderer::pick(std::vector<SGSceneryPick>& pickList, const osg::Vec2& windowPos)
|
||||
{
|
||||
|
@ -1750,6 +1806,10 @@ FGRenderer::pick(std::vector<SGSceneryPick>& pickList, const osg::Vec2& windowPo
|
|||
SGSceneryPick sceneryPick;
|
||||
sceneryPick.info.local = toSG(hit->getLocalIntersectPoint());
|
||||
sceneryPick.info.wgs84 = toSG(hit->getWorldIntersectPoint());
|
||||
|
||||
if( pickCallback->needsUV() )
|
||||
sceneryPick.info.uv = uvFromIntersection(*hit);
|
||||
|
||||
sceneryPick.callback = pickCallback;
|
||||
pickList.push_back(sceneryPick);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue