Hi,

The following basically simulates the use case that's causing problems.


Code:
#include <random>

#include <osg/AutoTransform>
#include <osg/Group>
#include <osg/PositionAttitudeTransform>
#include <osg/ShapeDrawable>

#include <osgText/Text>

#include <osgViewer/Viewer>

/** A representation of something that gets edited.
 *  Pretend it's actually more complicated than this so that reference counting 
the number of attached text nodes is a nuisance. */
class World
{
public:
    World() : mScene(new osg::Group)
    {
        // add things so the viewer doesn't automatically zoom too far in to 
see the 'objects'
        
        auto worldCorners = { osg::Vec3(-11, -11, -11), osg::Vec3(-11, -11, 11),
                              osg::Vec3(-11, 11, -11), osg::Vec3(-11, 11, 11),
                              osg::Vec3(11, -11, -11), osg::Vec3(11, -11, 11),
                              osg::Vec3(11, 11, -11), osg::Vec3(11, 11, 11) };

        for (auto corner : worldCorners)
        {
            osg::ref_ptr<osg::PositionAttitudeTransform> pat = new 
osg::PositionAttitudeTransform();
            pat->setPosition(corner);
            pat->addChild(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0, 
0, 0), 0.1)));
            mScene->addChild(pat);
        }
    }

    osg::ref_ptr<osg::Group> getScene() { return mScene; }

    /** Adds an object with a label to the scene based on something the user 
has done. */
    void addObject(std::string name)
    {
        osg::ref_ptr<osg::PositionAttitudeTransform> object = new 
osg::PositionAttitudeTransform();
        object->setName(name);

        static std::random_device r;
        static std::default_random_engine randEngine(r());
        static std::uniform_real_distribution<> dist(-10, 10);

        object->setPosition(osg::Vec3(dist(randEngine), dist(randEngine), 
dist(randEngine)));

        osg::ref_ptr<osgText::Text> objectLabel = new osgText::Text();
        osg::ref_ptr<osg::AutoTransform> autoTransform = new 
osg::AutoTransform();
        autoTransform->addChild(objectLabel);
        autoTransform->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN);
        object->addChild(autoTransform);
        autoTransform->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, 
osg::StateAttribute::OFF);
        objectLabel->setText(name);
        objectLabel->setCharacterSize(1);

        osg::ref_ptr<osg::Shape> shape = new osg::Sphere(osg::Vec3(0, 0, 0), 1);
        object->addChild(new osg::ShapeDrawable(shape));

        mScene->addChild(object);
    }

    /** Removes an object from the scene based on something the user has done. 
*/
    void removeObject(std::string name)
    {
        osg::ref_ptr<osg::Node> child;
        for (unsigned int i = 0; i < mScene->getNumChildren(); ++i)
        {
            if (mScene->getChild(i)->getName() == name)
            {
                child = mScene->getChild(i);
                mScene->removeChild(i);
                break;
            }
        }

        // If we call child->releaseGLObjects() here, the text program will be 
released, too, even though it could still be being used by other labels.
        // If we don't, we may be detaching the last text node from the scene 
graph, and so the text program may never get released.
    }

private:
    osg::ref_ptr<osg::Group> mScene;
};


World world;
bool useNewViewer = true;

class EventHandler : public osgGA::GUIEventHandler
{
public:
    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
    {
        if (ea.getEventType() != osgGA::GUIEventAdapter::KEYDOWN)
            return false;

        // The user may wish to close the 3D view to work on something else and 
then reopen it later.
        // That would be a pain to implement directly, so instead, we simulate 
it by reopening the 3D view when it's closed if Return was pressed while it was 
open.
        if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Return)
        {
            useNewViewer = true;
            return true;
        }
        // Press a letter key without shift to add an object.
        else if (ea.getKey() >= 'a' && ea.getKey() <= 'z')
        {
            std::string name(1, ea.getKey());
            world.addObject(name);
            return true;
        }
        // Press a letter key with shift to remove an object.
        else if (ea.getKey() >= 'A' && ea.getKey() <= 'Z')
        {
            std::string name(1, ea.getKey() - ('Z' - 'z'));
            world.removeObject(name);
            return true;
        }

        return false;
    }
};

int main()
{
    osg::ref_ptr<osgViewer::Viewer> currentViewer;

    int returnCode = 0;

    while (useNewViewer)
    {
        currentViewer = new osgViewer::Viewer;
        currentViewer->setSceneData(world.getScene());
        currentViewer->addEventHandler(new EventHandler);

        useNewViewer = false;
        
        returnCode = currentViewer->run();
    }

    return returnCode;
}




Imagine this is part of a world editor for a game or CAD software or one of any 
number of types of software packages where you edit something and might want a 
3D view of it, but wouldn't necessarily always want that 3D view taking up 
space in the window, so you'd possibly close it and then reopen it later. So I 
didn't have to make a load of extra stuff, this is simulated by opening a new 
viewer after the previous one closed if enter was pressed.

Pressing a letter key will add an object, and pressing the same letter with the 
shift key will remove it. Pretend that there are actually loads of different 
object types and not all of them have text labels so that keeping track of when 
the last text label is removed via reference counting or similar becomes a pain.

If you add at least one object, then remove all of them, then open a new view, 
you'll see OpenGL errors and text won't render correctly. This is because there 
were no osgText::Text nodes attached to the scene graph when the view was 
reset, so the program never got released, and the new text nodes try and reuse 
it with the new context, where it's not valid.

The obvious solution is to releaseGLObjects in the removeObject method, but if 
there are other objects still using text nodes, that's problematic as they'll 
still be using the program, and it would need recreating immediately.

Alternatively, removed objects could be kept around forever, attached to a 
hidden part of the scene graph so that anything that was ever used gets 
released when the context gets torn down. That means a bunch of junk needs 
keeping around forever, though.

Another option would just be to always have a hidden text node attached to the 
scene graph even when there are no objects. This solves the problem without 
much overhead but doesn't really seem like a tidy solution.

Thank you!

Cheers,
Chris

------------------
Read this topic online here:
http://forum.openscenegraph.org/viewtopic.php?p=75849#75849





_______________________________________________
osg-users mailing list
osg-users@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Reply via email to