: Documentation : Interaction Tutorials

Introduction

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

Proximity Based Interaction - Target to Target

Because we know the 3D positions of all tracked targets, it is trivial to compute the distances between targets. 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 targets.

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

Each target will have two different models associated with it. Whenever the distance between the targets 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 target 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 targets are close together).

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

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

TargetProximityUpdateCallback

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

To do this we define a new update callback, which holds information about both the the targets’ 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 TargetProximityUpdateCallback : public osg::NodeCallback {

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

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

        float mThreshold;

        osgART::Scene* mScene;

    public:

        TargetProximityUpdateCallback(
        osg::MatrixTransform* mA, osg::MatrixTransform* mB,
            osg::Switch* switchA, osg::Switch* switchB,
            float threshold, osgART::Scene *scene) :
                                 osg::NodeCallback(),
                        mtA(mA), mtB(mB),
                        mSwitchA(switchA), mSwitchB(switchB),
                        mThreshold(threshold),mScene(scene) { }


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

            /** CALCULATE INTER-TARGET PROXIMITY:
                Here we obtain the current position of each target, 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();
            mScene->setUpdateCallback(new TargetProximityUpdateCallback(mtA, mtB, mSwitchA,
                                                                        mSwitchB, 200.0f,mScene));

            /** LOAD APPROPRIATE MODELS:
                Here we use each target's OSG Switch node to swap between
                models, depending on the inter-target 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 TargetProximityUpdateCallback(mtA, mtB,
        switchA.get(), switchB.get(), 200.0f,scene));

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

markerToMarkerProximityGraph.png Link title