From e0d3ab44126c1a66db0ebb9a971837f83296bfeb Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Mon, 18 Nov 2013 13:25:55 +0000 Subject: [PATCH] From Stephan Huber, * GUIEventAdapter: add support for getting normalized touch points * MultiTouchTrackball: some code cleanup and support for normalized touch-points * oscdevice: receiving and sending multi-touch-events via the Cursor2D-profile from TUIO * added some documentation --- examples/osgmultitouch/osgmultitouch.cpp | 57 +++- examples/osgoscdevice/osgoscdevice.cpp | 17 +- include/osgGA/GUIEventAdapter | 12 +- include/osgGA/MultiTouchTrackballManipulator | 4 +- src/osgGA/MultiTouchTrackballManipulator.cpp | 34 ++- src/osgPlugins/osc/OscReceivingDevice.cpp | 267 +++++++++++++++++-- src/osgPlugins/osc/OscReceivingDevice.hpp | 53 +++- src/osgPlugins/osc/OscSendingDevice.cpp | 61 ++++- src/osgPlugins/osc/OscSendingDevice.hpp | 37 +++ src/osgPlugins/osc/ReaderWriterOscDevice.cpp | 7 - src/osgViewer/GraphicsWindowCocoa.mm | 6 +- 11 files changed, 496 insertions(+), 59 deletions(-) diff --git a/examples/osgmultitouch/osgmultitouch.cpp b/examples/osgmultitouch/osgmultitouch.cpp index b7ad2b54d..04ce3bd54 100644 --- a/examples/osgmultitouch/osgmultitouch.cpp +++ b/examples/osgmultitouch/osgmultitouch.cpp @@ -98,9 +98,11 @@ osg::Camera* createHUD(unsigned int w, unsigned int h) class TestMultiTouchEventHandler : public osgGA::GUIEventHandler { public: - TestMultiTouchEventHandler(osg::Group* parent_group) + TestMultiTouchEventHandler(osg::Group* parent_group, float w, float h) : osgGA::GUIEventHandler(), - _cleanupOnNextFrame(false) + _cleanupOnNextFrame(false), + _w(w), + _h(h) { createTouchRepresentations(parent_group, 10); } @@ -149,6 +151,18 @@ private: virtual bool handle (const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa, osg::Object *, osg::NodeVisitor *) { + if (ea.getEventType() != osgGA::GUIEventAdapter::FRAME) { + std::cout << ea.getTime() << ": "; + switch(ea.getEventType()) { + case osgGA::GUIEventAdapter::PUSH: std::cout << "PUSH"; break; + case osgGA::GUIEventAdapter::DRAG: std::cout << "DRAG"; break; + case osgGA::GUIEventAdapter::MOVE: std::cout << "MOVE"; break; + case osgGA::GUIEventAdapter::RELEASE: std::cout << "RELEASE"; break; + default: std::cout << ea.getEventType(); + } + std::cout << std::endl; + } + switch(ea.getEventType()) { case osgGA::GUIEventAdapter::FRAME: @@ -174,7 +188,12 @@ private: for(osgGA::GUIEventAdapter::TouchData::iterator i = ea.getTouchData()->begin(); i != ea.getTouchData()->end(); ++i, ++j) { const osgGA::GUIEventAdapter::TouchData::TouchPoint& tp = (*i); - _mats[j]->setMatrix(osg::Matrix::translate(tp.x, ea.getWindowHeight() - tp.y, 0)); + float x = ea.getTouchPointNormalizedX(j); + float y = ea.getTouchPointNormalizedY(j); + + // std::cout << j << ": " << tp.x << "/" << tp.y <<" "<< x << " " << y << " " << _w << " " << _h << std::endl; + + _mats[j]->setMatrix(osg::Matrix::translate((1+x) * 0.5 * _w, (1+y) * 0.5 * _h, 0)); _mats[j]->setNodeMask(0xffff); std::ostringstream ss; @@ -238,6 +257,8 @@ private: std::vector _mats; std::vector _texts; bool _cleanupOnNextFrame; + + float _w, _h; }; @@ -246,7 +267,21 @@ int main( int argc, char **argv ) { // use an ArgumentParser object to manage the program arguments. osg::ArgumentParser arguments(&argc,argv); + + unsigned int helpType = 0; + if ((helpType = arguments.readHelpType())) + { + arguments.getApplicationUsage()->write(std::cout, helpType); + return 1; + } + + // report any errors if they have occurred when parsing the program arguments. + if (arguments.errors()) + { + arguments.writeErrorMessages(std::cout); + return 1; + } // read the scene from the list of file specified commandline args. osg::ref_ptr scene = osgDB::readNodeFiles(arguments); @@ -265,7 +300,19 @@ int main( int argc, char **argv ) // construct the viewer. - osgViewer::Viewer viewer; + osgViewer::Viewer viewer(arguments); + + + //opening devices + std::string device; + while(arguments.read("--device", device)) + { + osg::ref_ptr dev = osgDB::readFile(device); + if (dev.valid()) + { + viewer.addDevice(dev.get()); + } + } osg::ref_ptr group = new osg::Group; @@ -288,7 +335,7 @@ int main( int argc, char **argv ) osg::Camera* hud_camera = createHUD(gc->getTraits()->width, gc->getTraits()->height); - viewer.addEventHandler(new TestMultiTouchEventHandler(hud_camera)); + viewer.addEventHandler(new TestMultiTouchEventHandler(hud_camera, gc->getTraits()->width, gc->getTraits()->height)); group->addChild(hud_camera); diff --git a/examples/osgoscdevice/osgoscdevice.cpp b/examples/osgoscdevice/osgoscdevice.cpp index 80bea179d..be66ca150 100644 --- a/examples/osgoscdevice/osgoscdevice.cpp +++ b/examples/osgoscdevice/osgoscdevice.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,8 @@ #include +#include + // class to handle events with a pick class PickHandler : public osgGA::GUIEventHandler { @@ -404,8 +407,11 @@ int main( int argc, char **argv ) if (!scene) { - std::cout << argv[0] << ": requires filename argument." << std::endl; - return 1; + osg::Geode* geode = new osg::Geode(); + osg::ShapeDrawable* drawable = new osg::ShapeDrawable(new osg::Box()); + geode->addDrawable(drawable); + + scene = geode; } bool use_zeroconf(false); @@ -490,6 +496,13 @@ int main( int argc, char **argv ) traits->windowName = "Sender / view one"; osg::ref_ptr gc = osg::GraphicsContext::createGraphicsContext(traits.get()); + + + #ifdef __APPLE__ + // as multitouch is disabled by default, enable it now + osgViewer::GraphicsWindowCocoa* win = dynamic_cast(gc.get()); + if (win) win->setMultiTouchEnabled(true); + #endif osgViewer::View* view = new osgViewer::View; diff --git a/include/osgGA/GUIEventAdapter b/include/osgGA/GUIEventAdapter index ca3241e03..def0d1684 100644 --- a/include/osgGA/GUIEventAdapter +++ b/include/osgGA/GUIEventAdapter @@ -677,7 +677,17 @@ public: TouchData* getTouchData() const { return _touchData.get(); } bool isMultiTouchEvent() const { return (_touchData.valid()); } - + + inline float getTouchPointNormalizedX(unsigned int ndx) const { + return (getTouchData()->get(ndx).x-_Xmin)/(_Xmax-_Xmin)*2.0f-1.0f; + } + + inline float getTouchPointNormalizedY(unsigned int ndx) const { + if (_mouseYOrientation==Y_INCREASING_UPWARDS) + return (getTouchData()->get(ndx).y-_Ymin)/(_Ymax-_Ymin)*2.0f-1.0f; + else + return -((getTouchData()->get(ndx).y-_Ymin)/(_Ymax-_Ymin)*2.0f-1.0f); + } typedef std::vector< osg::ref_ptr > PointerDataList; void setPointerDataList(const PointerDataList& pdl) { _pointerDataList = pdl; } diff --git a/include/osgGA/MultiTouchTrackballManipulator b/include/osgGA/MultiTouchTrackballManipulator index 5b8ba08e2..ba70f310e 100644 --- a/include/osgGA/MultiTouchTrackballManipulator +++ b/include/osgGA/MultiTouchTrackballManipulator @@ -36,9 +36,9 @@ class OSGGA_EXPORT MultiTouchTrackballManipulator : public TrackballManipulator protected: - void handleMultiTouchDrag(GUIEventAdapter::TouchData* now, GUIEventAdapter::TouchData* last, const double eventTimeDelta); + void handleMultiTouchDrag(const GUIEventAdapter* now, const GUIEventAdapter* last, const double eventTimeDelta); - osg::ref_ptr _lastTouchData; + osg::ref_ptr _lastEvent; }; diff --git a/src/osgGA/MultiTouchTrackballManipulator.cpp b/src/osgGA/MultiTouchTrackballManipulator.cpp index 2ac46d4c4..e612fbd36 100644 --- a/src/osgGA/MultiTouchTrackballManipulator.cpp +++ b/src/osgGA/MultiTouchTrackballManipulator.cpp @@ -35,14 +35,14 @@ MultiTouchTrackballManipulator::MultiTouchTrackballManipulator( const MultiTouch } -void MultiTouchTrackballManipulator::handleMultiTouchDrag(GUIEventAdapter::TouchData* now, GUIEventAdapter::TouchData* last, const double eventTimeDelta) +void MultiTouchTrackballManipulator::handleMultiTouchDrag(const GUIEventAdapter* now, const GUIEventAdapter* last, const double eventTimeDelta) { - const float zoom_threshold = 1.0f; + const float zoom_threshold = 0.0001f; - osg::Vec2 pt_1_now(now->get(0).x,now->get(0).y); - osg::Vec2 pt_2_now(now->get(1).x,now->get(1).y); - osg::Vec2 pt_1_last(last->get(0).x,last->get(0).y); - osg::Vec2 pt_2_last(last->get(1).x,last->get(1).y); + osg::Vec2 pt_1_now(now->getTouchPointNormalizedX(0),now->getTouchPointNormalizedY(0)); + osg::Vec2 pt_2_now(now->getTouchPointNormalizedX(1),now->getTouchPointNormalizedY(1)); + osg::Vec2 pt_1_last(last->getTouchPointNormalizedX(0),last->getTouchPointNormalizedY(0)); + osg::Vec2 pt_2_last(last->getTouchPointNormalizedX(1),last->getTouchPointNormalizedY(1)); @@ -51,21 +51,19 @@ void MultiTouchTrackballManipulator::handleMultiTouchDrag(GUIEventAdapter::Touch // osg::notify(osg::ALWAYS) << gap_now << " " << gap_last << std::endl; - if (fabs(gap_last - gap_now) >= zoom_threshold) - { - // zoom gesture - zoomModel( (gap_last - gap_now) * eventTimeDelta, true ); - } + + // zoom gesture + if (fabs(gap_last - gap_now) > 0.02) + zoomModel( (gap_last - gap_now) , true ); // drag gesture - osg::Vec2 delta = ((pt_1_last - pt_1_now) + (pt_2_last - pt_2_now)) / 2.0f; - float scale = 0.2f * _distance * eventTimeDelta; + float scale = _distance / 3.0f; // osg::notify(osg::ALWAYS) << "drag: " << delta << " scale: " << scale << std::endl; - panModel( delta.x() * scale, delta.y() * scale * (-1)); // flip y-coord because of different origins. + panModel( delta.x() * scale, delta.y() * scale); } @@ -101,15 +99,15 @@ bool MultiTouchTrackballManipulator::handle( const GUIEventAdapter& ea, GUIActio else if (data->getNumTouchPoints() >= 2) { - if ((_lastTouchData.valid()) && (_lastTouchData->getNumTouchPoints() >= 2)) + if ((_lastEvent.valid()) && (_lastEvent->getTouchData()->getNumTouchPoints() >= 2)) { - handleMultiTouchDrag(data, _lastTouchData.get(), eventTimeDelta); + handleMultiTouchDrag(&ea, _lastEvent, eventTimeDelta); } handled = true; } - _lastTouchData = data; + _lastEvent = new GUIEventAdapter(ea); // check if all touches ended unsigned int num_touches_ended(0); @@ -121,7 +119,7 @@ bool MultiTouchTrackballManipulator::handle( const GUIEventAdapter& ea, GUIActio if(num_touches_ended == data->getNumTouchPoints()) { - _lastTouchData = NULL; + _lastEvent = NULL; } } diff --git a/src/osgPlugins/osc/OscReceivingDevice.cpp b/src/osgPlugins/osc/OscReceivingDevice.cpp index d6bc22539..92a8a57cf 100644 --- a/src/osgPlugins/osc/OscReceivingDevice.cpp +++ b/src/osgPlugins/osc/OscReceivingDevice.cpp @@ -92,7 +92,7 @@ public: , _treatFirstArgumentAsValueName(treat_first_argument_as_value_name) { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m); + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint); virtual void describeTo(std::ostream& out) const { @@ -135,7 +135,7 @@ private: -bool StandardRequestHandler::operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) +bool StandardRequestHandler::operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { try { @@ -298,7 +298,7 @@ public: { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { try { float x_min(-1.0f), y_min(-1.0f), x_max(1.0f), y_max(1.0f); @@ -330,7 +330,7 @@ public: { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { try { bool increasing_upwards(false); @@ -364,7 +364,7 @@ public: { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { try { osc::int32 keycode(0); @@ -401,7 +401,7 @@ public: { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { try { osc::int32 keycode(0); @@ -440,7 +440,7 @@ public: { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { try { @@ -475,7 +475,7 @@ public: { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { try { @@ -516,7 +516,7 @@ public: { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { float down(0.0f); @@ -568,7 +568,7 @@ public: } } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { float x(0.0f), y(0.0f); osc::int32 btn(0); @@ -621,7 +621,7 @@ public: { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { try { float pressure(0.0f); @@ -652,7 +652,7 @@ public: { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { try { osc::int32 pt(osgGA::GUIEventAdapter::UNKNOWN); @@ -686,7 +686,7 @@ public: { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) { try { float rotation(0.0f), tilt_x(0.0f), tilt_y(0.0f); @@ -710,6 +710,240 @@ public: }; +class TUIO2DCursorRequestHandler : public OscReceivingDevice::RequestHandler { + +public: + + struct Cursor { + std::string source; + unsigned int id, frameId; + osg::Vec2f pos, vel; + float accel; + osgGA::GUIEventAdapter::TouchPhase phase; + + Cursor() : source(), id(0), frameId(0), pos(), vel(), accel(), phase(osgGA::GUIEventAdapter::TOUCH_UNKNOWN) {} + + }; + struct EndpointData { + std::string source; + osc::int32 frameId; + bool mayClearUnhandledPointer; + std::set unhandled; + }; + + typedef std::map EndpointDataMap; + typedef std::map CursorMap; + typedef std::map ApplicationCursorMap; + typedef std::map SourceIdMap; + TUIO2DCursorRequestHandler() + : OscReceivingDevice::RequestHandler("/tuio/2Dcur") + { + } + + virtual void setDevice(OscReceivingDevice* device) { + OscReceivingDevice::RequestHandler::setDevice(device); + device->addHandleOnCheckEvents(this); + } + + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) + { + // std::cout << m << std::endl; + + OpenThreads::ScopedLock lock(_mutex); + + std::string end_point(' ', IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH); + remoteEndPoint.AddressAndPortAsString(&end_point[0]); + + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + + + const char* str; + args >> str; + std::string what(str); + + if (what == "source") + { + args >> str; + _endpointData[end_point].source = std::string(str); + updateSourceIdMap(_endpointData[end_point].source); + + return true; + } + else if (what == "fseq") + { + args >> _endpointData[end_point].frameId; + return true; + } + else + { + std::string source = _endpointData[end_point].source; + unsigned int frame_id = _endpointData[end_point].frameId; + + if (what == "alive") + { + while (!args.Eos()) + { + osc::int32 id; + args >> id; + _endpointData[source].unhandled.insert(id); + } + _endpointData[source].mayClearUnhandledPointer = true; + + return true; + } + else if (what == "set") + { + osc::int32 id; + args >> id; + if (_alive[source].find(id) == _alive[source].end()) + { + _alive[source][id] = Cursor(); + } + + Cursor& c(_alive[source][id]); + args >> c.pos.x() >> c.pos.y() >> c.vel.x() >> c.vel.y() >> c.accel >> osc::EndMessage; + c.source = source; + c.frameId = frame_id; + _endpointData[source].unhandled.insert(id); + + return true; + } + } + + return false; + } + + virtual void operator()(osgGA::EventQueue* queue) + { + // dispatch all touchpoints in one GUIEventAdapter + + OpenThreads::ScopedLock lock(_mutex); + + osg::ref_ptr event = NULL; + + + for(ApplicationCursorMap::iterator i = _alive.begin(); i != _alive.end(); ++i) + { + const std::string& source(i->first); + + /* + std::cout << source << ": "; + for(std::set::iterator k = _endpointData[source].unhandled.begin(); + k != _endpointData[source].unhandled.end(); + ++k) + { + std::cout << *k << " "; + } + std::cout << std::endl; + */ + + // remove all touchpoints which are not transmitted via alive-message, dispatching TOUCH_ENDED + + EndpointData& endpoint_data(_endpointData[source]); + if (endpoint_data.mayClearUnhandledPointer) + { + unsigned int source_id = getSourceId(source); + + std::vector to_delete; + + for(CursorMap::iterator k = i->second.begin(); k != i->second.end(); ++k) + { + //create a unique touchpoint-id + unsigned int touch_id = (source_id << 16) + k->first; + + std::set& unhandled(endpoint_data.unhandled); + if ((unhandled.find(k->first) == unhandled.end())) + { + std::cout << "deleting: " << k->first << std::endl; + to_delete.push_back(k->first); + + float win_x = k->second.pos.x(); + float win_y = k->second.pos.y(); + + if (!event) + event = queue->touchEnded(touch_id, osgGA::GUIEventAdapter::TOUCH_ENDED, win_x, win_y, 1); + else + event->addTouchPoint(touch_id, osgGA::GUIEventAdapter::TOUCH_ENDED, win_x, win_y, 1); + } + } + // remove "dead" cursors + for(std::vector::iterator k = to_delete.begin(); k != to_delete.end(); ++k) + { + _alive[source].erase(i->second.find(*k)); + } + + endpoint_data.mayClearUnhandledPointer = false; + endpoint_data.unhandled.clear(); + } + + if (i->second.size() == 0) + { + // std::cout << "removing endpoint" << source << std::endl; + _endpointData.erase(_endpointData.find(source)); + _alive.erase(_alive.find(source)); + } + } + + // send all alive touchpoints + for(ApplicationCursorMap::iterator i = _alive.begin(); i != _alive.end(); ++i) + { + const std::string& source(i->first); + unsigned int source_id = getSourceId(source); + + for(CursorMap::iterator k = i->second.begin(); k != i->second.end(); ++k) + { + unsigned int id = k->first; + unsigned int touch_id = (source_id << 16) + id; + + Cursor& c(k->second); + float win_x = c.pos.x(); + float win_y = c.pos.y(); + + bool down = c.phase != osgGA::GUIEventAdapter::TOUCH_MOVED && c.phase != osgGA::GUIEventAdapter::TOUCH_STATIONERY; + if(!event) + { + if(down) + event = queue->touchBegan(touch_id, osgGA::GUIEventAdapter::TOUCH_BEGAN, win_x, win_y); + else + event = queue->touchMoved(touch_id, osgGA::GUIEventAdapter::TOUCH_MOVED, win_x, win_y); + } + else + { + event->addTouchPoint(touch_id, down ? osgGA::GUIEventAdapter::TOUCH_BEGAN : osgGA::GUIEventAdapter::TOUCH_MOVED, win_x, win_y); + } + c.phase = osgGA::GUIEventAdapter::TOUCH_MOVED; + } + } + + // adjust time + input range + if (event) + { + event->setInputRange(0, 0, 1.0, 1.0); + event->setTime(queue->getTime()); + } + } + + + inline void updateSourceIdMap(const std::string& source) + { + if (_sourceIdMap.find(source) == _sourceIdMap.end()) + _sourceIdMap[source] = _sourceIdMap.size(); + } + + inline unsigned int getSourceId(const std::string& source) + { + return _sourceIdMap[source]; + } + +private: + EndpointDataMap _endpointData; + ApplicationCursorMap _alive; + OpenThreads::Mutex _mutex; + SourceIdMap _sourceIdMap; +}; + + } // end of namespace @@ -757,6 +991,8 @@ OscReceivingDevice::OscReceivingDevice(const std::string& server_address, int li addRequestHandler(new OscDevice::PenOrientationRequestHandler()); addRequestHandler(new OscDevice::PenProximityRequestHandler(true)); addRequestHandler(new OscDevice::PenProximityRequestHandler(false)); + + addRequestHandler(new OscDevice::TUIO2DCursorRequestHandler()); addRequestHandler(new OscDevice::StandardRequestHandler("/osg/set_user_value", true)); @@ -803,7 +1039,7 @@ void OscReceivingDevice::ProcessMessage( const osc::ReceivedMessage& m, const Ip { // OSG_INFO << "OscDevice :: handling " << mangled_path << " with " << i->second << std::endl; - if (i->second->operator()(mangled_path, in_request_path, m) && !handled) + if (i->second->operator()(mangled_path, in_request_path, m, remoteEndpoint) && !handled) handled = true; } @@ -862,6 +1098,7 @@ void OscReceivingDevice::ProcessBundle( const osc::ReceivedBundle& b, } + void OscReceivingDevice::ProcessPacket( const char *data, int size, const IpEndpointName& remoteEndpoint ) { try { @@ -882,7 +1119,7 @@ void OscReceivingDevice::ProcessPacket( const char *data, int size, const IpEndp remoteEndpoint.AddressAndPortAsString(address); _userDataEvent->setUserValue("osc/remote_end_point", std::string(address)); - + _userDataEvent->setTime(getEventQueue()->getTime()); getEventQueue()->addEvent(_userDataEvent.get()); _userDataEvent = NULL; } diff --git a/src/osgPlugins/osc/OscReceivingDevice.hpp b/src/osgPlugins/osc/OscReceivingDevice.hpp index 5c814d0f3..d74d8cdb3 100644 --- a/src/osgPlugins/osc/OscReceivingDevice.hpp +++ b/src/osgPlugins/osc/OscReceivingDevice.hpp @@ -14,6 +14,40 @@ #pragma once +/** + * OscReceivingDevice can be used to receive osg-events via OSC from other hosts/ applications + * It can even translate custom osc-message to user-events with attached user-data. + * + * It uses the TUIO 1.1 Cursor2D-profile to receive multitouch-events + * + * This device adds to every message-bundle a message-id, so you can check, if you miss events or similar. + * + * receiving custom osc-data: + * /my_user_event/value_1 23 + * /my_user_event/value_2 42 + * + * this will result in one osgGA:Event (when both messages are bundled) witht the name "/my_user_event", + * and two user-values (value_1 and value_2) + * To get value_1 you'll do something like event->getUserValue("value_1", my_int); + * + * Currently osg's user-data can not cast to different types, they have to match, so + * event->getUserValue("value_1", my_string) will fail. + * + * The receiving device will try to combine multiple osc arguments intelligently, multiple osc-arguments are + * bundled into Vec2, Vec3, Vec4 or Matrix, it depends on the number of arguments. + + * TUIO-specific notes: + * If multiple TUIO-applications are transmitting their touch-points to one oscReceivingDevice, all + * touchpoints get multiplexed, so you'll get one event with x touchpoints. + * You can differentiate the specific applications by the touch_ids, the upper 16bits + * are specific to an application, the lower 16bits contain the touch-id for that application. + * If you need "better" separation, use multiple oscReceivingDevices listening on different ports. + * + * @TODO implement other TUIO-profiles + * + */ + + #include #include #include @@ -37,7 +71,8 @@ public: { } - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) = 0; + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint) = 0; + virtual void operator()(osgGA::EventQueue* queue) {} const std::string& getRequestPath() const { return _requestPath; } @@ -47,7 +82,7 @@ public: } protected: - void setDevice(OscReceivingDevice* device) { _device = device; } + virtual void setDevice(OscReceivingDevice* device) { _device = device; } OscReceivingDevice* getDevice() const { return _device; } /// set the request-path, works only from the constructor @@ -98,6 +133,17 @@ public: virtual const char* className() const { return "OSC receiving device"; } + void addHandleOnCheckEvents(RequestHandler* handler) { _handleOnCheckEvents.push_back(handler); } + + virtual bool checkEvents() { + osgGA::EventQueue* queue = getEventQueue(); + + for(std::vector::iterator i = _handleOnCheckEvents.begin(); i != _handleOnCheckEvents.end(); ++i) { + (*i)->operator()(queue); + } + return osgGA::Device::checkEvents(); + } + private: std::string _listeningAddress; unsigned int _listeningPort; @@ -106,6 +152,7 @@ private: osg::ref_ptr _userDataEvent; MsgIdType _lastMsgId; osg::Timer_t _lastMsgTimeStamp; + std::vector _handleOnCheckEvents; }; @@ -113,7 +160,7 @@ private: class SendKeystrokeRequestHandler : public OscReceivingDevice::RequestHandler { public: SendKeystrokeRequestHandler(const std::string& request_path, int key) : OscReceivingDevice::RequestHandler(request_path), _key(key) {} - virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& arguments) + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& arguments, const IpEndpointName& remoteEndPoint) { getDevice()->getEventQueue()->keyPress(_key); getDevice()->getEventQueue()->keyRelease(_key); diff --git a/src/osgPlugins/osc/OscSendingDevice.cpp b/src/osgPlugins/osc/OscSendingDevice.cpp index f7c35585e..59896cb6c 100644 --- a/src/osgPlugins/osc/OscSendingDevice.cpp +++ b/src/osgPlugins/osc/OscSendingDevice.cpp @@ -12,11 +12,13 @@ */ + #include "OscSendingDevice.hpp" #include "osc/OscHostEndianness.h" #include #include #include +#include static const unsigned long BUFFER_SIZE = 2048; @@ -27,9 +29,14 @@ OscSendingDevice::OscSendingDevice(const std::string& address, int port, unsigne , _oscStream(_buffer, BUFFER_SIZE) , _numMessagesPerEvent(osg::maximum(1u,num_messages_per_event)) , _delayBetweenSendsInMilliSecs( (_numMessagesPerEvent > 1) ? delay_between_sends_in_millisecs : 0) + , _msgId(0) + , _lastEvent(NULL) { setCapabilities(SEND_EVENTS); + + + OSG_NOTICE << "OscDevice :: sending events to " << address << ":" << port << " "; #ifdef OSC_HOST_LITTLE_ENDIAN OSG_NOTICE << "(little endian)"; @@ -49,7 +56,6 @@ OscSendingDevice::~OscSendingDevice() void OscSendingDevice::sendEvent(const osgGA::Event &ea) { - static osc::int64 msg_id(0); bool msg_sent(false); unsigned int num_messages = _numMessagesPerEvent; @@ -59,12 +65,12 @@ void OscSendingDevice::sendEvent(const osgGA::Event &ea) num_messages = 1; for(unsigned int i = 0; i < num_messages; ++i) { - msg_sent = ui_event ? sendUIEventImpl(*ui_event, msg_id) : sendEventImpl(ea, msg_id); + msg_sent = ui_event ? sendUIEventImpl(*ui_event, _msgId) : sendEventImpl(ea, _msgId); if ((_delayBetweenSendsInMilliSecs > 0) && (i < num_messages-1)) OpenThreads::Thread::microSleep(1000 * _delayBetweenSendsInMilliSecs); } if (msg_sent) - msg_id++; + _msgId++; } @@ -158,6 +164,7 @@ bool OscSendingDevice::sendUIEventImpl(const osgGA::GUIEventAdapter &ea, MsgIdTy case osgGA::GUIEventAdapter::PUSH: beginSendInputRange(ea, msg_id); + sendMultiTouchData(ea); _oscStream << osc::BeginMessage("/osgga/mouse/press") << ea.getX() << ea.getY() << getButtonNum(ea) << osc::EndMessage; _oscStream << osc::EndBundle; do_send = true; @@ -165,6 +172,7 @@ bool OscSendingDevice::sendUIEventImpl(const osgGA::GUIEventAdapter &ea, MsgIdTy case osgGA::GUIEventAdapter::RELEASE: beginSendInputRange(ea, msg_id); + sendMultiTouchData(ea); _oscStream << osc::BeginMessage("/osgga/mouse/release") << ea.getX() << ea.getY() << getButtonNum(ea) << osc::EndMessage; _oscStream << osc::EndBundle; do_send = true; @@ -180,6 +188,7 @@ bool OscSendingDevice::sendUIEventImpl(const osgGA::GUIEventAdapter &ea, MsgIdTy case osgGA::GUIEventAdapter::MOVE: case osgGA::GUIEventAdapter::DRAG: beginSendInputRange(ea, msg_id); + sendMultiTouchData(ea); _oscStream << osc::BeginMessage("/osgga/mouse/motion") << ea.getX() << ea.getY() << osc::EndMessage; _oscStream << osc::EndBundle; do_send = true; @@ -246,12 +255,16 @@ int OscSendingDevice::getButtonNum(const osgGA::GUIEventAdapter& ea) return -1; } + + void OscSendingDevice::beginBundle(MsgIdType msg_id) { _oscStream << osc::BeginBundle(); _oscStream << osc::BeginMessage("/osc/msg_id") << msg_id << osc::EndMessage; } + + void OscSendingDevice::beginSendInputRange(const osgGA::GUIEventAdapter &ea, MsgIdType msg_id) { beginBundle(msg_id); @@ -260,6 +273,48 @@ void OscSendingDevice::beginSendInputRange(const osgGA::GUIEventAdapter &ea, Msg } + +void OscSendingDevice::sendMultiTouchData(const osgGA::GUIEventAdapter &ea) +{ + if(!ea.isMultiTouchEvent()) + return; + + std::string application_name; + getUserValue("tuio_application_name", application_name); + + if (application_name.empty()) + application_name = std::string("OpenSceneGraph ") + osgGetVersion() + "@127.0.0.1"; + + osgGA::GUIEventAdapter::TouchData* touch_data = ea.getTouchData(); + + _oscStream << osc::BeginMessage("/tuio/2Dcur") << "source" << application_name.c_str() << osc::EndMessage; + + _oscStream << osc::BeginMessage("/tuio/2Dcur") << "alive"; + for(osgGA::GUIEventAdapter::TouchData::iterator i = touch_data->begin(); i != touch_data->end(); ++i) + _oscStream << static_cast(i->id); + _oscStream << osc::EndMessage; + + unsigned int j(0); + for(osgGA::GUIEventAdapter::TouchData::iterator i = touch_data->begin(); i != touch_data->end(); ++i, ++j) + { + float x = (ea.getTouchPointNormalizedX(j) + 1.0) / 2.0; + float y =(ea.getTouchPointNormalizedY(j) + 1.0) / 2.0; + + float vel_x(0), vel_y(0), accel(0); + if (_lastEvent.valid()) + { + + } + + _oscStream << osc::BeginMessage("/tuio/2Dcur") << "set" << static_cast(i->id) << x << y << vel_x << vel_y << accel << osc::EndMessage; + } + + _oscStream << osc::BeginMessage("/tuio/2Dcur") << "fseq" << static_cast(_msgId) << osc::EndMessage; + + _lastEvent = new osgGA::GUIEventAdapter(ea); +} + + class OscSendingDeviceGetValueVisitor : public osg::ValueObject::GetValueVisitor { public: OscSendingDeviceGetValueVisitor(osc::OutboundPacketStream& stream) diff --git a/src/osgPlugins/osc/OscSendingDevice.hpp b/src/osgPlugins/osc/OscSendingDevice.hpp index fa5a4ac89..9d41a65be 100644 --- a/src/osgPlugins/osc/OscSendingDevice.hpp +++ b/src/osgPlugins/osc/OscSendingDevice.hpp @@ -14,6 +14,38 @@ #pragma once +/** + * OscSendingDevice can be used to send osg-events via OSC to other hosts/ applications + * It can even transmit user-events with attached user-data. + * + * It uses the TUIO 1.1 Cursor2D-profile to send multitouch-events + * + * This device adds to every message-bundle a message-id, so you can check, if you miss events or similar. + * + * sending custom data via an event's user-data: + * osgGA::Event* event = new osgGA::Event(); + * event->setName("/my_user_event"); + * event->setUserValue("value_1", 23); + * event->setUserValue("value_2", 42); + * device->sendEvent(event); + * + * The receiver gets a message-bundle with 2 messages: + * /my_user_event/value_1 23 + * /my_user_event/value_2 42 + * + * The sending device knows how to handle Vec2, Vec3, Vec4, Quat, Plane and Matrix values + * + * TUIO-specific notes: + * If you want to explicitely set the application-name when sending multi-touch-events, use + * osc_device->setUserValue("tuio_application_name", ""); + * You'll also have to add the address-part, e.g. + * osc_device->setUserValue("tuio_application_name", "my_tuio_application@192.168.1.1"); + * + * @TODO get local address for a correct TUIO-application-name + * @TODO implement other TUIO-profiles + * @TODO compute velocity + acceleration for TUIO-messages + * + */ #include @@ -33,12 +65,17 @@ private: bool sendUIEventImpl(const osgGA::GUIEventAdapter &ea,MsgIdType msg_id); void beginBundle(MsgIdType msg_id); void beginSendInputRange(const osgGA::GUIEventAdapter& ea, MsgIdType msg_id); + void sendMultiTouchData(const osgGA::GUIEventAdapter& ea); int getButtonNum(const osgGA::GUIEventAdapter& ea); void sendUserDataContainer(const std::string& key, const osg::UserDataContainer* udc, bool asBundle, MsgIdType msg_id); std::string transliterateKey(const std::string& key) const; + UdpTransmitSocket _transmitSocket; char* _buffer; osc::OutboundPacketStream _oscStream; unsigned int _numMessagesPerEvent, _delayBetweenSendsInMilliSecs; + osc::int64 _msgId; + osg::ref_ptr _lastEvent; + }; diff --git a/src/osgPlugins/osc/ReaderWriterOscDevice.cpp b/src/osgPlugins/osc/ReaderWriterOscDevice.cpp index 70ddc8845..6069f649f 100644 --- a/src/osgPlugins/osc/ReaderWriterOscDevice.cpp +++ b/src/osgPlugins/osc/ReaderWriterOscDevice.cpp @@ -30,12 +30,6 @@ * dump all registered handlers to the console. The device registers some convenient * handlers to remote control a p3d-presentation. * - * you can feed a osgPresentation::PropertyManager into the plugin and set values on it via - * "/p3d/set_value key value" or "/p3d/set_value/key value" - * Additionally the plugin listens for - * "/osg/set_user_value key value" or "/osg/set_user_value/key value" and set the transmitted value on the - * UserDataContainer of the device. - * * * The plugin supports forwarding most of the events per osc to another host. * It uses a special event-handler, which forwards the events. To get this @@ -48,7 +42,6 @@ * * * TODO: - * - add multitouch support, preferably using the TUIO-protocol * - be more tolerant with given filenames */ diff --git a/src/osgViewer/GraphicsWindowCocoa.mm b/src/osgViewer/GraphicsWindowCocoa.mm index cb72652e5..b30f9499f 100644 --- a/src/osgViewer/GraphicsWindowCocoa.mm +++ b/src/osgViewer/GraphicsWindowCocoa.mm @@ -876,7 +876,7 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect) NSTouch *touch = [[allTouches allObjects] objectAtIndex:i]; NSPoint pos = [touch normalizedPosition]; - osg::Vec2 pixelPos(pos.x * bounds.size.width, (1-pos.y) * bounds.size.height); + osg::Vec2 pixelPos(pos.x * bounds.size.width, (pos.y) * bounds.size.height); unsigned int touch_id = [self computeTouchId: touch mayCleanup:FALSE]; if (!osg_event) { osg_event = _win->getEventQueue()->touchBegan(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y()); @@ -897,7 +897,7 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect) { NSTouch *touch = [[allTouches allObjects] objectAtIndex:i]; NSPoint pos = [touch normalizedPosition]; - osg::Vec2 pixelPos(pos.x * bounds.size.width, (1 - pos.y) * bounds.size.height); + osg::Vec2 pixelPos(pos.x * bounds.size.width, (pos.y) * bounds.size.height); unsigned int touch_id = [self computeTouchId: touch mayCleanup:FALSE]; if (!osg_event) { osg_event = _win->getEventQueue()->touchMoved(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y()); @@ -919,7 +919,7 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect) { NSTouch *touch = [[allTouches allObjects] objectAtIndex:i]; NSPoint pos = [touch normalizedPosition]; - osg::Vec2 pixelPos(pos.x * bounds.size.width, (1 - pos.y) * bounds.size.height); + osg::Vec2 pixelPos(pos.x * bounds.size.width, (pos.y) * bounds.size.height); unsigned int touch_id = [self computeTouchId: touch mayCleanup: TRUE]; if (!osg_event) { osg_event = _win->getEventQueue()->touchEnded(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y(), 1);