Hi All,

I wasn't planning to revist the user data implementation this week,
but the weaknesses in design has been lurking at the back of my mind,
it just wasn't as clean as I'd have liked.  With the feature freeze
for 3.0 happening at the end of  next week I couldn't help but want to
get the implementation right for the stable release, so this morning I
revisted the implementation.

The key awkwardness in the version checked in earlier this week was
that osg::Object had a number of virtual access methods for user
objects with this overriden by the UserDataContainer, but also the
osg::Object agregated the UserDataContainer, so the releationship was
one of both inheritance and composition.  It worked and was easier to
use, but it wasn't clean conceptually.

Peter picked up on this design/implementation awkwardness and
suggested that this could be resolved by moving the virtual access
methods for  UserDataContainer out of osg::Object and into
UserDataContainer make this class a pure virtual class, with a
DefaultUserDataContainer subclassing from this to provide the standard
implementation.  Moving the access methods out of osg::Object does
mean that accessing the user objects will require an checks against
and usage of object.getUserDataContainer(),
and it introduces an implementation issue of osg::Object having a
ref_ptr<UserDataContainer> when UserDataContainer is a subclass from
osg::Object, which introduces a potential definition order problem for
getting the code compiled.

When mulling over the design I came to the conclusion that Peter's
suggestion was a more elegant design, I had originall consider such an
approach but the definition order issue is one that I wasn't original
comfortable with so didn't pursue it.  With thinking about
the definition order issue I realised it could easily be walked around
by using a C pointer to the UserDataContainer in osg::Object
and have the osg::Object::setUserDataContainer(UserDataContainer*)
implementation manage the ref()/unref() rather than rely
upon osg::ref_ptr<> that requires the class definiation for it be able
to be compiled.

With the C pointer trick in the bag it became a simple refactor of the
osg::Object and osg::UserDataContainer interfaces and implementations,
with the s/getUserObject(..) access methods moving from osg::Object
into pure virtual UserDataContainer base
class.  The setUserData/getUserData() stay put in osg::Object but are
virtual to allow the UserDataContainer to override them. The
Description list access methods were then moved back into osg::Node,
keeping the osg::Object cleaner and avoiding
the conceptual overlap between osg::Object and UserDataContainer.
Finally a DefaultUserDataContainer implementation is
provided, and is almost identical to my original UserDataContainer.

Now osg::Object has one set of access method that I didn't move - the
templated s/getUserValue(..) methods, I could have moved these to
UserDataContainer but as the methods are implementated with forward
declared templates there isn't an order definition issue so the
implementation is free to provided separately and use the full
UserDataContainer interface.  In terms of convinience it's couldn't be
simpler than having the g/setUserValue methods available in
osg::Object, and the osguserdata example itself didn't have to be
modified at all where s/getUserValue access was used so it works well.
 The only parts of the osguserdata example I have had to modify to
account for these changes is the adding of the osg::Geometry object as
a UserObject, and output of the UserObject list as both of these
require the access methods only available in osg::UserDataContainer.

I have been able to update the serialization support to handle the new
implementation so everything functions as well as it did on the
previous rev - only with a cleaner class relationship.   These changes
are now checked into svn/trunk so please test them out.

One suggestion of Peter's that I have gone with is passing in an
osg::Object* to the UserDataContainer methods that would have enabled
him to share a single custom UserDataContainer implementation between
objects in the scene and have the custom container work the correct
data for each object.  I rejected this suggestion in my previous email
and still feel it's inappropriate.  If one does want centralized
access to user data associated with objects then there is no reason to
place this in the scene graph - if you are going to have a data
structure that is mapping Object pointers to internal data objects
then it's global operation already, it's not something that needs or
should be distributed throughut the scene graph.  Even if one did
create such a global mapping I'm doubtful that a Object pointer would
even be the appropriate key is to use to access it as pointers to
Object change when being serialized in and out - so re-run your app
and all your pointers are different for the same scene graph objects.
For this type of task I would lean towards have a user manage unique
ObjectID that is used as the key, and the natural place to put this
would be... in a custom UserDataContainer.

Right, I'm hoping that this user object/meta object support is now
good to go for 3.0, so I'm heading off to tackle other topics.  Please
test it - after lots of discussion on this thread it's going rather
quite now that an implementation is live in svn/trunk.

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

Reply via email to