C++ & openFrameworks - Hello TUIO

From GestureWorks Tutorials
Jump to: navigation, search

Logo gestureworks core.png

 

OpenFrameworks.png

C++ & openFrameworks: Hello TUIO

Contents

Introduction

This tutorial will take you through the process of altering the simple Hello Multitouch to feed events to Gestureworks from a TUIO listener instead of the operating system. We will briefly cover the TUIO listener class, creating touchpoint data from TUIO data, and how to pass touch events directly to Gestureworks. For this tutorial you will need the Gestureworks Core multitouch framework; a free trial is available.

Download the code for all of the C++ & openFrameworks multitouch tutorials here: tutorials_cpp_openframeworks.zip


Requirements

Estimated time to complete: 30 minutes


Process Overview

  1. Setup
  2. Creating the TUIO client extension
  3. Creating touchpoint objects
  4. Passing events to Gestureworks
  5. The event thread
  6. The Update and Draw Functions

1. Setup

Like the code itself, setup for this project is nearly identical to that for the Hello Multitouch tutorial.

  • You'll need to create a Visual Studio project, link in openFrameworks, and the Gestureworks bindings as before. Following that, a little setup is needed for TUIO.
  • Download the TUIOClient reference implementation from TUIO.org and unpack it. The only files we’ll be using directly are TuioClient.h and TuioListener.h, however you’ll need to have all of the files in the TUIO, osc, and ip folders added to your solution and and the folders where they’re stored added to your include directories in order to compile correctly.
#pragma once

#include "tuio/TuioListener.h"
#include "tuio/TuioClient.h"
#include "GestureWorksCore.h"
#include <math.h>

using namespace TUIO;

class touchListener : public TuioListener {
       
public:
        void addTuioObject(TuioObject *tobj);
        void updateTuioObject(TuioObject *tobj);
        void removeTuioObject(TuioObject *tobj);

        void addTuioCursor(TuioCursor *tcur);
        void updateTuioCursor(TuioCursor *tcur);
        void removeTuioCursor(TuioCursor *tcur);

        void refresh(TuioTime frameTime);
};

There are seven functions to implement here, however we are only concerned with TuioCursors, and not TuioObjects (since TUIO-enabled multitouch devices generally send out touch data as TuioCursors only), so we can leave the first three functions as stubs:

include "touchListener.h"

void touchListener::addTuioObject(TuioObject *tobj) {
        //Do nothing
}

void touchListener::updateTuioObject(TuioObject *tobj) {
        //Do nothing
}

void touchListener::removeTuioObject(TuioObject *tobj) {
        //Do nothing
}

The TUIO protocol handles touchpoint data in a manner virtually identical to that of Gestureworks, by handling three distinct events: point additions, point status updates, and point removals. In order to pass information about these events to Gestureworks, however, we have to normalize it; we do that by sticking it into a gwc::touchpoint object:

class touchpoint{
public:
        clock_t timestamp;
        touchStatus status;
        long int id;
        float x;
        float y;
        float z;
        float w;
        float h;
        float r;
        float p;
        void init(long int _id, float _x, float _y, float _z, float _w, float _h);
};

The status member tells us whether the point is being added, updated, or removed. All members are public, so they can be set manually with ease. However, the init function is designed for easy event creation; requiring only an ID number and point dimensions, it automatically sets the timestamp to the current tick count. The status must then be set manually. With this object, handling all TUIO cursor events is trivial:

void touchListener::addTuioCursor(TuioCursor *tcur) {
        gwc::touchpoint new_point;
        new_point.init(tcur->getCursorID(), tcur->getX(), tcur->getY(), 0, 1, 1);
        new_point.status = gwc::TOUCHADDED;
        addEvent(new_point);
}

void touchListener::updateTuioCursor(TuioCursor *tcur) {
        gwc::touchpoint updated_point;
        updated_point.init(tcur->getCursorID(), tcur->getX(), tcur->getY(), 0, 1, 1);
        updated_point.status = gwc::TOUCHUPDATE;
        addEvent(updated_point);
}

void touchListener::removeTuioCursor(TuioCursor *tcur) {
        gwc::touchpoint removed_point;
        removed_point.init(tcur->getCursorID(), tcur->getX(), tcur->getY(), 0, 1, 1);
        removed_point.status = gwc::TOUCHREMOVED;
        addEvent(removed_point);
}

For each TUIO event received, we merely create a touchpoint with the dimensions and ID provided by the event, and set the status according to the callback being used. The event is then passed on to Gestureworks via addEvent(). Critically, this function is thread-aware, such that if it’s called in one thread while Gestureworks is executing in another thread, it will wait until that processing is finished to continue execution.

3. The event thread

TUIOClient objects can be made to connect() as a perpetually-blocking call that passes all received events to the given listener’s handler functions. This makes it extremely convenient to create a thread whose sole purpose is to connect a TUIOClient with our listener, so we have a thread function that does precisely this:

DWORD TUIO_thread(LPVOID lpdwThreadParam) {
        //This starts the client and runs forever
        tuio_client.connect(true);
        return 0;
}

We actually get the thread going in the setup function. First, we add our TUIO listener to our TUIO client, and then create the thread and let it run:

        tuio_client.addTuioListener(&listener);
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&TUIO_thread, 0, 0, 0);

Now our event listener will capture all TUIO events sent our way and pass them along to Gestureworks.

4. The Update and Draw Functions

You may have noticed that these are rather conspicuously identical to their Hello Multitouch counterparts. Because we’ve created a separate thread to handle receipt of external input and pass it to Gestureworks, the working of the rest of the program can be left unaltered. Events can be received from Gestureworks exactly as before, and we needn’t worry about threading troubles interacting with the data. While Gestureworks is usually only accessed by one thread at a time, it is designed in such a way that additional threads attempting access won’t cause any data corruption.


Notes on LEAP

There is a set of source files for a similar tutorial called Hello LEAP. It is so similar, in fact, that reading this tutorial and substituting "LEAP" for "TUIO" should show you just about all the steps necessary for creating a LEAP-aware application with Gestureworks.

There are, however, a couple of differences that are noteworthy:

  • The thread function for LEAP is set to run as an infinite loop, because the listen function returns after receiving an event.
  • The LEAP event handler is a bit more complex than the TUIO event handler. This is mostly because the LEAP events are less verbose than the TUIO event. Whereas TUIO tells us when a point has been added, removed, or updated, LEAP just tell us what points are present, and we need to track state to determine which of those points are new, and whether any points have disappeared. There is, additionally, a Z component that is available from the LEAP and we add this to the point coordinates for each event. Finally, we do some bounds-checking to confine the points we track to a reasonable volume above the LEAP.

More information on LEAP can be found on the Leap Motion website.

Personal tools

Variants
Actions
Navigation
Toolbox