Hi Ted,

I remember seeing this too. The trick is not to track the MatrixTransform itself, but to track its child (if there is no unique child, then reformat your graph by creating a single osg::Group below the MatrixTransform that contains the MatrixTransform's old children).

For some reason when you setTrackNode(some MatrixTransform) the transform's matrix is applied twice which is what I think you're seeing.

Actually I just realized that I had made a test case for this. It's attached.

The program will show a simple scene where a blue sphere rotates around the world origin, and a green sphere rotates around the blue sphere. There are also a few stationary cubes just for reference (to see that there is movement). The camera follows the green sphere with a NodeTrackerManipulator (unless the --NoNodeTracker argument is given).


The source is commented to explain what is being demonstrated. Note that the source comments talk about 2 bugs:

* Bug 1 is reproduced by the --TrackTransform argument. When you tell the NodeTracker to track a transform instead of the child, the transform's matrix seems to be applied twice, which is what I said above. The simplest way to see this is to run with --TrackTransform, and put your finger in the center of your screen. You'll see that the camera is always centered at 2x the transform from the blue sphere to the green one (the matrix, which is a translation, is applied with a factor of 2). Without the --TrackTransform argument, you'll see that the green sphere always stays right smack in the middle of the screen as it should.

* Bug 2 used to be reproduced by the --DoNotCreateGeometryUnderTransform argument. When there was no geometry under the tracked node (i.e. the tracked node has invalid bounds), then the NodeTracker seemed to revert to looking at the world origin. This seems to have been fixed at some point, because my test program can't reproduce it anymore.


The program has a few different command line options:

Running without --NoNodeTracker, --TrackTransform and --DoNotCreateGeometryUnderTransform will lead to the green sphere being tracked, and this shows normal behavior.

--MT, --PAT, --DOF : select the type of transform node used.

--NoNodeTracker : don't use a NodeTrackerManipulator - this allows you to see what the scene looks like in general before using the other options.

--TrackTransform : reproduces bug 1 above.

--DoNotCreateGeometryUnderTransform : used to reproduce bug 2 above, but doesn't anymore.

--AddBSphereCallback : used to be a possible workaround for bug 2...


I never went deep into the OSG code to find a real fix for these problems, and always assumed that it was our usage of the NodeTrackerManipulator that was wrong in these cases. I'm not so sure anymore... Perhaps if you're inclined to debug the issue for bug 1, you could look into it. We just found workarounds (track children of the transform instead of the transform itself for bug 1, and the BSphereCallback for bug 2) and used those instead.

Hope this helps,

J-S
--
______________________________________________________
Jean-Sebastien Guay    [email protected]
                               http://www.cm-labs.com/
                        http://whitestar02.webhop.org/
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This application is open source and may be redistributed and/or modified   
 * freely and without restriction, both in commericial and non commericial 
applications,
 * as long as this copyright notice is maintained.
 * 
 * This application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>
#include <osg/CoordinateSystemNode>

#include <osg/Switch>
#include <osgText/Text>

#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>

#include <osgGA/NodeTrackerManipulator>
#include <osgGA/StateSetManipulator>

#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osgSim/DOFTransform>

#include <osg/ShapeDrawable>

#include <iostream>


// This is a callback that will be attached to a transform which has no
// geometry under it, so that the transform itself has valid bounds (a
// small sphere at the position defined by the transform). Otherwise
// the NodeTrackerManipulator cannot follow the transform.
struct BSphereCallback : public osg::Node::ComputeBoundingSphereCallback
{
    virtual osg::BoundingSphere computeBound(const osg::Node &node) const
    {
        // If the node has no parents or if the parent is not a transform,
        // return a bounding sphere that will place this node in world 
        // space / in the same coordinate space as the parent.
        if (node.getNumParents() == 0 || node.getParent(0)->asTransform() == 
NULL)
            return osg::BoundingSphere(osg::Vec3(0,0,0), .1);

        // Do the right thing depending on the type of transform the node 
        // is a child of.
        const osg::MatrixTransform* mt = 
node.getParent(0)->asTransform()->asMatrixTransform();
        const osg::PositionAttitudeTransform* pat = 
node.getParent(0)->asTransform()->asPositionAttitudeTransform();
        const osgSim::DOFTransform* dof = dynamic_cast<const 
osgSim::DOFTransform*>(node.getParent(0));

        if (mt)
            return osg::BoundingSphere(mt->getMatrix().getTrans(), .1);
        else if (pat)
            return osg::BoundingSphere(pat->getPosition(), .1);
        else if (dof)
            return osg::BoundingSphere(dof->getInversePutMatrix().getTrans(), 
.1);
        else
        {
            // Unsupported transform type, return a default bounding sphere.
            return osg::BoundingSphere(osg::Vec3(0,0,0), .1);
        }
    }
};


// Makes the transform this is attached to move in a circle around its parent's
// coordinate frame. You can specify the diameter in scene units and the speed. 
// At a speed of 1 the transform will go full circle in 2*pi ~= 6.28 seconds.
struct MoveInCircle : public osg::NodeCallback
{
    MoveInCircle(double diameter = 1, double speed = 1) : _diameter(diameter), 
_speed(speed) {}

    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
    {
        if (!node->asTransform()) return;

        double time = nv->getFrameStamp()->getReferenceTime();
        osg::Vec3d pos(cos(time * _speed) * _diameter, 0, sin(time * _speed) * 
_diameter);

        osg::MatrixTransform* mt = node->asTransform()->asMatrixTransform();
        osg::PositionAttitudeTransform* pat = 
node->asTransform()->asPositionAttitudeTransform();
        osgSim::DOFTransform* dof = dynamic_cast<osgSim::DOFTransform*>(node);

        if (mt)
            mt->setMatrix(osg::Matrix::translate(pos));
        else if (pat)
            pat->setPosition(pos);
        else if (dof)
            dof->setCurrentTranslate(pos);
        else
        {
            // Unsupported transform type. Can't do anything.
        }

        traverse(node,nv);
    }

    double _diameter;
    double _speed;
};


int main(int argc, char** argv)
{
    // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc,argv);

    
arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
    
arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+"
 is an example that demonstrates two bugs related to using 
osgGA::NodeTrackerManipulator.");
    
arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+"
 [options] filename ...");
    arguments.getApplicationUsage()->addCommandLineOption("--NoNodeTracker","Do 
not use an osgGA::NodeTrackerManipulator, use the default TrackballManipulator, 
just to see the layout of the scene.");
    
arguments.getApplicationUsage()->addCommandLineOption("--TrackTransform","[Bug 
#1] Track the transform parent of the sphere instead of the sphere node itself. 
The result will be that the transform will be applied twice (the camera will 
rotate twice as fast as the object under the transform). The solution is to 
track a child of the transform (either one that has geometry, or see bug #2 and 
its workaround).");
    
arguments.getApplicationUsage()->addCommandLineOption("--DoNotCreateGeometryUnderTransform","[Bug
 #2] Do not create the sphere below the last moving transform, just create a 
default osg::Node. Mutually exclusive of --TrackTransform. See 
--AddBSphereCallback for a workaround to this.");
    
arguments.getApplicationUsage()->addCommandLineOption("--AddBSphereCallback","Add
 a ComputeBoundingSphereCallback to the osg::Node created by 
--DoNotCreateGeometryUnderTransform, so that it returns a valid BoundingSphere. 
This is a workaround to bug #2.");

    osgViewer::Viewer viewer(arguments);

    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;
    }


    // add the state manipulator
    viewer.addEventHandler( new 
osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
    
    // add the thread model handler
    viewer.addEventHandler(new osgViewer::ThreadingHandler);

    // add the window size toggle handler
    viewer.addEventHandler(new osgViewer::WindowSizeHandler);
        
    // add the stats handler
    viewer.addEventHandler(new osgViewer::StatsHandler);


    // ----------------------------------------------------------------------
    // Read the command line arguments.
    bool useNodeTracker = true;
    while (arguments.read("--NoNodeTracker")) useNodeTracker = false;

    // Bug 1: With all transform types, if the tracked node is the transform 
    // itself, the transform's matrix is applied twice, i.e. the camera will 
    // rotate twice as fast as the object under the transform. A workaround
    // for this is to track a child of the transform (which is not a transform
    // too). But see below if the child has no geometry under it.
    bool trackTransform = false;
    while (arguments.read("--TrackTransform")) trackTransform = true;

    // Bug 2: With all transform types, if the tracked node has no geometry
    // (i.e. its computeBound() returns invalid bounds) then it will not track
    // anything (i.e. it will look at the world space origin). A workaround
    // for this is below. This is mutually exclusive of --TrackTransform, so
    // enabling this will disable trackTransform.
    bool createGeometryUnderTransform = true;
    while (arguments.read("--DoNotCreateGeometryUnderTransform")) { 
createGeometryUnderTransform = false; trackTransform = false; }

    // This is a workaround for bug 2. It makes it so that the child of the
    // transform has a valid bounding sphere by attaching a 
    // ComputeBoundingSphereCallback to it which returns a small bounding
    // sphere at the transformed location.
    bool addBSphereCallback = false;
    while (arguments.read("--AddBSphereCallback")) addBSphereCallback = true;

    enum TransformType
    {
        MT = 0,
        PAT = 1,
        DOF = 2
    };
    TransformType transformType = MT;
    while (arguments.read("--MT")) transformType = MT;
    while (arguments.read("--PAT")) transformType = PAT;
    while (arguments.read("--DOF")) transformType = DOF;

    // any option left unread are converted into errors to write out later.
    arguments.reportRemainingOptionsAsUnrecognized();

    // report any errors if they have occurred when parsing the program 
arguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
        return 1;
    }


    // ----------------------------------------------------------------------
    // Build the scene.

    osg::Group* root = new osg::Group;

    // Just give us a reference to be able to see the movement.
    {
        osg::MatrixTransform* mt1 = new osg::MatrixTransform;
        mt1->setMatrix(osg::Matrix::translate(-10, 0, -10));
        root->addChild(mt1);
        osg::MatrixTransform* mt2 = new osg::MatrixTransform;
        mt2->setMatrix(osg::Matrix::translate(-10, 0, 10));
        root->addChild(mt2);
        osg::MatrixTransform* mt3 = new osg::MatrixTransform;
        mt3->setMatrix(osg::Matrix::translate(10, 0, -10));
        root->addChild(mt3);
        osg::MatrixTransform* mt4 = new osg::MatrixTransform;
        mt4->setMatrix(osg::Matrix::translate(10, 0, 10));
        root->addChild(mt4);

        osg::Geode* cubeGeode = new osg::Geode;
        osg::ShapeDrawable* cube = new osg::ShapeDrawable(new 
osg::Box(osg::Vec3(0,0,0), 1));
        cubeGeode->addDrawable(cube);
        mt1->addChild(cubeGeode);
        mt2->addChild(cubeGeode);
        mt3->addChild(cubeGeode);
        mt4->addChild(cubeGeode);
    }

    // Another cube at the origin.
    {
        osg::MatrixTransform* mt5 = new osg::MatrixTransform;
        mt5->setMatrix(osg::Matrix::identity());
        root->addChild(mt5);

        osg::Geode* cubeGeode = new osg::Geode;
        osg::ShapeDrawable* cube = new osg::ShapeDrawable(new 
osg::Box(osg::Vec3(0,0,0), 1));
        cube->setColor(osg::Vec4(1,0,0,1));
        cubeGeode->addDrawable(cube);
        mt5->addChild(cubeGeode);
    }


    // Now the first moving object
    osg::MatrixTransform* movingTransform = new osg::MatrixTransform;
    movingTransform->setName("Moving Transform");
    movingTransform->setUpdateCallback(new MoveInCircle(10));
    root->addChild(movingTransform);
    
    osg::Geode* sphereGeode = new osg::Geode;
    osg::ShapeDrawable* sphere = new osg::ShapeDrawable(new 
osg::Sphere(osg::Vec3(0,0,0), 1));
    sphere->setColor(osg::Vec4(0,0,1,1));
    sphereGeode->addDrawable(sphere);
    movingTransform->addChild(sphereGeode);


    // ----------------------------------------------------------------------
    // Now the transform we want to track, and the geometry under it if needed.
    osg::Group* group;
    switch (transformType)
    {
        case MT:
            group = new osg::MatrixTransform;
            break;
        case PAT:
            group = new osg::PositionAttitudeTransform;
            break;
        case DOF:
        {
            osgSim::DOFTransform* dof = new osgSim::DOFTransform;
            // Shouldn't DOFTransform's ctor set this? A default scale of 
(0,0,0) makes no sense.
            dof->setCurrentScale(osg::Vec3(1,1,1));
            group = dof;
            break;
        }
    };

    group->setName("Small Sphere Transform");
    group->setUpdateCallback(new MoveInCircle(2, 2));
    movingTransform->addChild(group);

    osg::Node* geometryUnderTransform;
    if (createGeometryUnderTransform)
    {
        osg::Geode* smallSphereGeode = new osg::Geode;
        osg::ShapeDrawable* smallSphere = new osg::ShapeDrawable(new 
osg::Sphere(osg::Vec3(0,0,0), 0.5));
        smallSphere->setColor(osg::Vec4(0,1,0,1));
        smallSphereGeode->addDrawable(smallSphere);
        
        geometryUnderTransform = smallSphereGeode;
    }
    else
    {
        // Will have invalid bounding sphere --> BoundingSphere(vec3(0,0,0), -1)
        // Unless we add the BSphereCallback.
        geometryUnderTransform = new osg::Node;
        if (addBSphereCallback)
            geometryUnderTransform->setComputeBoundingSphereCallback(new 
BSphereCallback);
    }

    group->addChild(geometryUnderTransform);


    // ----------------------------------------------------------------------
    // Set the viewer's manipulator to a NodeTrackerManipulator if we want to.
    // Else the Viewer will add a TrackballManipulator automatically.
    if (useNodeTracker)
    {
        osg::ref_ptr<osgGA::NodeTrackerManipulator> manipulator = new 
osgGA::NodeTrackerManipulator;
        viewer.setCameraManipulator(manipulator);

        manipulator->setHomePosition(osg::Vec3(0, -30, 0), osg::Vec3(0,0,0), 
osg::Vec3(0,0,1));

        if (trackTransform)
            manipulator->setTrackNode(group);
        else 
            manipulator->setTrackNode(geometryUnderTransform);

        
manipulator->setTrackerMode(osgGA::NodeTrackerManipulator::NODE_CENTER_AND_ROTATION);
    }


    viewer.setSceneData( root );

    viewer.realize();

    return viewer.run();

}
_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Reply via email to