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
This commit is contained in:
Robert Osfield 2013-11-18 13:25:55 +00:00
parent d7442e7456
commit e0d3ab4412
11 changed files with 496 additions and 59 deletions

View File

@ -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;
@ -239,6 +258,8 @@ private:
std::vector<osgText::Text*> _texts;
bool _cleanupOnNextFrame;
float _w, _h;
};
@ -248,6 +269,20 @@ int main( int argc, char **argv )
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<osg::Node> 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<osgGA::Device> dev = osgDB::readFile<osgGA::Device>(device);
if (dev.valid())
{
viewer.addDevice(dev.get());
}
}
osg::ref_ptr<osg::Group> 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);

View File

@ -31,6 +31,7 @@
#include <osg/Camera>
#include <osg/ValueObject>
#include <osg/FrontFace>
#include <osg/ShapeDrawable>
#include <osgDB/ReadFile>
#include <osgText/Text>
@ -47,6 +48,8 @@
#include <osg/io_utils>
#include <osgViewer/api/Cocoa/GraphicsWindowCocoa>
// 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);
@ -492,6 +498,13 @@ int main( int argc, char **argv )
osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
#ifdef __APPLE__
// as multitouch is disabled by default, enable it now
osgViewer::GraphicsWindowCocoa* win = dynamic_cast<osgViewer::GraphicsWindowCocoa*>(gc.get());
if (win) win->setMultiTouchEnabled(true);
#endif
osgViewer::View* view = new osgViewer::View;
view->setName("View one");
viewer.addView(view);

View File

@ -678,6 +678,16 @@ 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<PointerData> > PointerDataList;
void setPointerDataList(const PointerDataList& pdl) { _pointerDataList = pdl; }

View File

@ -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<GUIEventAdapter::TouchData> _lastTouchData;
osg::ref_ptr<GUIEventAdapter> _lastEvent;
};

View File

@ -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;
}
}

View File

@ -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<unsigned int> unhandled;
};
typedef std::map<std::string, EndpointData> EndpointDataMap;
typedef std::map<unsigned int, Cursor> CursorMap;
typedef std::map<std::string, CursorMap> ApplicationCursorMap;
typedef std::map<std::string, unsigned int> 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<OpenThreads::Mutex> 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<OpenThreads::Mutex> lock(_mutex);
osg::ref_ptr<osgGA::GUIEventAdapter> event = NULL;
for(ApplicationCursorMap::iterator i = _alive.begin(); i != _alive.end(); ++i)
{
const std::string& source(i->first);
/*
std::cout << source << ": ";
for(std::set<unsigned int>::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<unsigned int> 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<unsigned int>& 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<unsigned int>::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
@ -758,6 +992,8 @@ OscReceivingDevice::OscReceivingDevice(const std::string& server_address, int li
addRequestHandler(new OscDevice::PenProximityRequestHandler(true));
addRequestHandler(new OscDevice::PenProximityRequestHandler(false));
addRequestHandler(new OscDevice::TUIO2DCursorRequestHandler());
addRequestHandler(new OscDevice::StandardRequestHandler("/osg/set_user_value", true));
addRequestHandler(new OscDevice::StandardRequestHandler("", false));
@ -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;
}

View File

@ -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 <osg/Referenced>
#include <OpenThreads/Thread>
#include <osgGA/Device>
@ -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<RequestHandler*>::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<osgGA::Event> _userDataEvent;
MsgIdType _lastMsgId;
osg::Timer_t _lastMsgTimeStamp;
std::vector<RequestHandler*> _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);

View File

@ -12,11 +12,13 @@
*/
#include "OscSendingDevice.hpp"
#include "osc/OscHostEndianness.h"
#include <osg/UserDataContainer>
#include <osg/ValueObject>
#include <osg/Math>
#include <osg/Version>
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<osc::int32>(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<osc::int32>(i->id) << x << y << vel_x << vel_y << accel << osc::EndMessage;
}
_oscStream << osc::BeginMessage("/tuio/2Dcur") << "fseq" << static_cast<osc::int32>(_msgId) << osc::EndMessage;
_lastEvent = new osgGA::GUIEventAdapter(ea);
}
class OscSendingDeviceGetValueVisitor : public osg::ValueObject::GetValueVisitor {
public:
OscSendingDeviceGetValueVisitor(osc::OutboundPacketStream& stream)

View File

@ -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", "<your 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 <osgGA/Device>
@ -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<osgGA::GUIEventAdapter> _lastEvent;
};

View File

@ -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
*/

View File

@ -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);