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:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user