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:
parent
d7442e7456
commit
e0d3ab4412
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user