: Documentation : Interaction Tutorials

Introduction

In this tutorial we extend the concept of proximity based interaction to multiple markers, using the spatial relationships between markers rather than to the camera.

Proximity Based Interaction - Marker to Marker

Because we know the 3D positions of all tracked markers, it is trivial to compute the distances between markers. When the distance decreases below a certain threshold, or exceeds a certain threshold, we can invoke an action to update the application.

MarkerToMarkerProximity.png

Setup

In this tutorial we will use two markers.

osg::MatrixTransform* mtA = scene->addTrackedTransform("single;data/patt.hiro;80;0;0");
osg::MatrixTransform* mtB = scene->addTrackedTransform("single;data/patt.kanji;80;0;0");

Each marker will have two different models associated with it. Whenever the distance between the markers is above a particular threshold, we will display one set of models, and whenever the distance drops below the threshold, we will swap the models for the other set.

Switch

To flip backwards and forwards between different models, we will use an osg::Switch node. A switch node in a scene graph is a specialised type of osg::Group node, in which the child nodes can be independently enabled or disabled. In our case, each marker will have one switch node, and each switch node will have two children (one for the model shown by default, and another for the model shown when the markers are close together).

osg::ref_ptr<osg::Switch> switchA = new osg::Switch();
switchA->addChild(osgDB::readNodeFile("data/voltmeter_low.osg"), true);
switchA->addChild(osgDB::readNodeFile("data/voltmeter_high.osg"), false);
mtA->addChild(switchA.get());

osg::ref_ptr<osg::Switch> switchB = new osg::Switch();
switchB->addChild(osgDB::readNodeFile("data/battery.osg"), true);
switchB->addChild(osgDB::readNodeFile("data/battery_spark.osg"), false);
mtB->addChild(switchB.get());

MarkerProximityUpdateCallback

All that is left to do is define how to continuously calculate the distances between the two markers, compare that distance with a particular threshold, and use each marker’s switch node to load the appropriate model.

To do this we define a new update callback, which holds information about both the the markers’ parent transform nodes, and the switch nodes. We will attach the update callback to our scene graph, so that the information is updated every traversal. Within this new callback we can then perform the necessary calculations.

Recall from the Animation tutorial that to create an update callback, we must subclass osg::NodeCallback, and override the operator() method. The code follows:

class MarkerProximityUpdateCallback : public osg::NodeCallback {

    private:
        osg::MatrixTransform* mtA;
        osg::MatrixTransform* mtB;

        osg::Switch* mSwitchA;
        osg::Switch* mSwitchB;

        float mThreshold;

    public:

        MarkerProximityUpdateCallback(
        osg::MatrixTransform* mA, osg::MatrixTransform* mB,
            osg::Switch* switchA, osg::Switch* switchB,
            float threshold) :
                                 osg::NodeCallback(),
                        mtA(mA), mtB(mB),
                        mSwitchA(switchA), mSwitchB(switchB),
                        mThreshold(threshold) { }


        virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) {

            /** CALCULATE INTER-MARKER PROXIMITY:
                Here we obtain the current position of each marker, and the
                distance between them by examining
                the translation components of their parent transformation
                matrices **/
            osg::Vec3 posA = mtA->getMatrix().getTrans();
            osg::Vec3 posB = mtB->getMatrix().getTrans();
            osg::Vec3 offset = posA - posB;
            float distance = offset.length();
            scene->setUpdateCallback(new MarkerProximityUpdateCallback(mtA, mtB,
                                                                                    switchA.get(),
                                                                                    switchB.get(),
                                                                                    200.0f));

            /** LOAD APPROPRIATE MODELS:
                Here we use each marker's OSG Switch node to swap between
                models, depending on the inter-marker distance we have just
                calculated. **/
            if (distance <= mThreshold) {
                if (mSwitchA->getNumChildren() > 1) mSwitchA->setSingleChildOn(1);
                if (mSwitchB->getNumChildren() > 1) mSwitchB->setSingleChildOn(1);
            } else {
                if (mSwitchA->getNumChildren() > 0) mSwitchA->setSingleChildOn(0);
                if (mSwitchB->getNumChildren() > 0) mSwitchB->setSingleChildOn(0);
            }


            traverse(node,nv);

        }
};

Finally …

Here we add the callback to the scene graph, by adding it to the Scene node’s list of update callbacks.

scene->setUpdateCallback(new MarkerProximityUpdateCallback(mtA, mtB,
                                                      switchA.get(), switchB.get(),
                                                      200.0f));

We then add the scene data to the viewer, and set up the viewer loop, as before.

markerToMarkerProximityGraph.png Link title