Hi Robert,

Here a first version to give you an overview of clone of UpdateCallback.
I dont know if it's important to do the same things for others
NodeCallback like cull or event, it could make senses but it's maybe
something critical with current application. Maybe we could add a copyop
option to work as before ? What do you think ?

i modified CopyOp CopyOp.cpp and Node.cpp

Cheers,
Cedric

On Mon, 2009-06-15 at 11:55 +0100, Robert Osfield wrote:
> HI Cedric,
> 
> Added support for NodeCallback to CopyOp and beyond does sound
> reasonable.  The only issue I forsee right now is that some users
> might not fully implement their callbacks so miss out the full copy
> constructors.  If they are doing this are cloning they might meet with
> problems, but if they are combining these both then they really should
> implement their code fully...
> 
> So go for it.  I'll have a review of your final changes and if
> anything problematic jumps out I'll ping you with my thoughts.
> 
> Cheers,
> Robert.
> 
> On Sun, Jun 14, 2009 at 10:51 PM, Cedric
> Pinson<[email protected]> wrote:
> > Hi Robert,
> >
> > In osgAnimation i need an update callback per Bone, so when i clone a
> > scenegraph that contains bone it does not duplicate the updatecallback.
> > I would like to know if you would accept submission that add option in
> > the CopyOp to duplicate update callback chain.
> >
> > I could do that in the osgAnimation::Bone::Bone(const Bone& b, const
> > osg::CopyOp& copyop);
> > but maybe it could be better if it works for general case.
> >
> > Does it makes sense for you ?
> >
> > Cheers,
> > Cedric
> >
> > --
> > +33 (0) 6 63 20 03 56  Cedric Pinson mailto:[email protected]
> > http://www.plopbyte.net
> >
> >
> > _______________________________________________
> > osg-submissions mailing list
> > [email protected]
> > http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
> >
> >
-- 
+33 (0) 6 63 20 03 56  Cedric Pinson mailto:[email protected]
http://www.plopbyte.net

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library 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.  See the 
 * OpenSceneGraph Public License for more details.
*/

#ifndef OSG_COPYOP
#define OSG_COPYOP 1

#include <osg/Export>

namespace osg {

class Referenced;
class Object;
class Image;
class Texture;
class StateSet;
class StateAttribute;
class Uniform;
class Node;
class Drawable;
class Array;
class PrimitiveSet;
class Shape;
class NodeCallback;

/** Copy Op(erator) used to control whether shallow or deep copy is used
  * during copy construction and clone operation.*/
class OSG_EXPORT CopyOp
{

    public:
    
        enum Options
        {
            SHALLOW_COPY                = 0,
            DEEP_COPY_OBJECTS           = 1<<0,
            DEEP_COPY_NODES             = 1<<1,
            DEEP_COPY_DRAWABLES         = 1<<2,
            DEEP_COPY_STATESETS         = 1<<3,
            DEEP_COPY_STATEATTRIBUTES   = 1<<4,
            DEEP_COPY_TEXTURES          = 1<<5,
            DEEP_COPY_IMAGES            = 1<<6,
            DEEP_COPY_ARRAYS            = 1<<7,
            DEEP_COPY_PRIMITIVES        = 1<<8,
            DEEP_COPY_SHAPES            = 1<<9,
            DEEP_COPY_UNIFORMS          = 1<<10,
            DEEP_COPY_UPDATECALLBACKS   = 1<<11,
            DEEP_COPY_ALL               = 0x7FFFFFFF
        };

        typedef unsigned int CopyFlags;

        inline CopyOp(CopyFlags flags=SHALLOW_COPY):_flags(flags) {}
        virtual ~CopyOp() {}

        virtual Referenced*     operator() (const Referenced* ref) const;
        virtual Object*         operator() (const Object* obj) const;
        virtual Node*           operator() (const Node* node) const;
        virtual Drawable*       operator() (const Drawable* drawable) const;
        virtual StateSet*       operator() (const StateSet* stateset) const;
        virtual StateAttribute* operator() (const StateAttribute* attr) const;
        virtual Texture*        operator() (const Texture* text) const;
        virtual Image*          operator() (const Image* image) const;
        virtual Array*          operator() (const Array* array) const;
        virtual PrimitiveSet*   operator() (const PrimitiveSet* primitives) const;
        virtual Shape*          operator() (const Shape* shape) const;
        virtual Uniform*        operator() (const Uniform* shape) const;
        virtual NodeCallback*   operator() (const NodeCallback* nodecallback) const;

    protected:
    
        CopyFlags _flags;
};

}

#endif
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library 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.  See the 
 * OpenSceneGraph Public License for more details.
*/
#include <osg/CopyOp>
#include <osg/Node>
#include <osg/StateSet>
#include <osg/Texture>
#include <osg/Drawable>
#include <osg/Array>
#include <osg/PrimitiveSet>
#include <osg/Shape>

using namespace osg;

#define COPY_OP( TYPE, FLAG ) \
TYPE* CopyOp::operator() (const TYPE* obj) const \
{ \
    if (obj && _flags&FLAG) \
        return dynamic_cast<TYPE*>( obj->clone(*this) ); \
    else \
        return const_cast<TYPE*>(obj); \
} 

COPY_OP( Object,         DEEP_COPY_OBJECTS )
COPY_OP( Node,           DEEP_COPY_NODES )
COPY_OP( Drawable,       DEEP_COPY_DRAWABLES )
COPY_OP( StateSet,       DEEP_COPY_STATESETS )
COPY_OP( Texture,        DEEP_COPY_TEXTURES )
COPY_OP( Image,          DEEP_COPY_IMAGES )
COPY_OP( Array,          DEEP_COPY_ARRAYS )
COPY_OP( PrimitiveSet,   DEEP_COPY_PRIMITIVES )
COPY_OP( Shape,          DEEP_COPY_SHAPES )
COPY_OP( Uniform,        DEEP_COPY_UNIFORMS )

Referenced* CopyOp::operator() (const Referenced* ref) const
{
    return const_cast<Referenced*>(ref);
}

StateAttribute* CopyOp::operator() (const StateAttribute* attr) const
{
    if (attr && _flags&DEEP_COPY_STATEATTRIBUTES)
    {
        const Texture* textbase = dynamic_cast<const Texture*>(attr);
        if (textbase)
        {
            return operator()(textbase);
        }
        else 
        {
            return dynamic_cast<StateAttribute*>(attr->clone(*this));
        }
    }
    else
        return const_cast<StateAttribute*>(attr);
}


NodeCallback* CopyOp::operator() (const NodeCallback* nc) const
{
    if (nc && _flags&DEEP_COPY_UPDATECALLBACKS)
    {
        // deep copy the full chain of callback
        osg::NodeCallback* first = dynamic_cast<osg::NodeCallback*>(nc->clone(*this));
        first->setNestedCallback(0);
        nc = nc->getNestedCallback();
        while (nc) 
        {
            osg::NodeCallback* ucb = dynamic_cast<osg::NodeCallback*>(nc->clone(*this));
            ucb->setNestedCallback(0);
            first->addNestedCallback(ucb);
            nc = nc->getNestedCallback();
        }
        return first;
    }
    else
        return const_cast<NodeCallback*>(nc);
}
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library 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.  See the 
 * OpenSceneGraph Public License for more details.
*/

#include <osg/Node>
#include <osg/Group>
#include <osg/NodeVisitor>
#include <osg/Notify>
#include <osg/OccluderNode>
#include <osg/Transform>

#include <algorithm>

using namespace osg;

namespace osg
{
    /// Helper class for generating NodePathList.
    class CollectParentPaths : public NodeVisitor
    {
    public:
        CollectParentPaths(const osg::Node* haltTraversalAtNode=0) : 
            osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_PARENTS),
            _haltTraversalAtNode(haltTraversalAtNode)
        {
        }

        virtual void apply(osg::Node& node)
        {
            if (node.getNumParents()==0 || &node==_haltTraversalAtNode)
            {
                _nodePaths.push_back(getNodePath());
            }
            else
            {
                traverse(node);
            }
       }

        const Node*     _haltTraversalAtNode;
        NodePath        _nodePath;
        NodePathList    _nodePaths;
    };
}

Node::Node()
    :Object(true)
{
    _boundingSphereComputed = false;
    _nodeMask = 0xffffffff;
    
    _numChildrenRequiringUpdateTraversal = 0;

    _numChildrenRequiringEventTraversal = 0;

    _cullingActive = true;
    _numChildrenWithCullingDisabled = 0;

    _numChildrenWithOccluderNodes = 0;
}

Node::Node(const Node& node,const CopyOp& copyop):
        Object(node,copyop),
        _initialBound(node._initialBound),
        _boundingSphere(node._boundingSphere),
        _boundingSphereComputed(node._boundingSphereComputed),
        _parents(), // leave empty as parentList is managed by Group.
        _numChildrenRequiringUpdateTraversal(0), // assume no children yet.
        _numChildrenRequiringEventTraversal(0), // assume no children yet.
        _cullCallback(node._cullCallback),
        _cullingActive(node._cullingActive),
        _numChildrenWithCullingDisabled(0), // assume no children yet.
        _numChildrenWithOccluderNodes(0),
        _nodeMask(node._nodeMask), 
        _descriptions(node._descriptions)
{
    setStateSet(copyop(node._stateset.get()));
    setUpdateCallback(copyop(node._updateCallback.get()));
}

Node::~Node()
{
    // cleanly detatch any associated stateset (include remove parent links)
    setStateSet(0);   
}

void Node::addParent(osg::Group* node)
{
    OpenThreads::ScopedPointerLock<OpenThreads::Mutex> lock(getRefMutex());

    _parents.push_back(node);
}

void Node::removeParent(osg::Group* node)
{
    OpenThreads::ScopedPointerLock<OpenThreads::Mutex> lock(getRefMutex());

    ParentList::iterator pitr = std::find(_parents.begin(),_parents.end(),node);
    if (pitr!=_parents.end()) _parents.erase(pitr);
}

void Node::accept(NodeVisitor& nv)
{
    if (nv.validNodeMask(*this)) 
    {
        nv.pushOntoNodePath(this);
        nv.apply(*this);
        nv.popFromNodePath();
    }
}


void Node::ascend(NodeVisitor& nv)
{
    std::for_each(_parents.begin(),_parents.end(),NodeAcceptOp(nv));
}

void Node::setStateSet(osg::StateSet* stateset)
{
    // do nothing if nothing changed.
    if (_stateset==stateset) return;
    
    // track whether we need to account for the need to do a update or event traversal.
    int delta_update = 0;
    int delta_event = 0;

    // remove this node from the current statesets parent list 
    if (_stateset.valid())
    {
        _stateset->removeParent(this);
        if (_stateset->requiresUpdateTraversal()) --delta_update;
        if (_stateset->requiresEventTraversal()) --delta_event;
    }
    
    // set the stateset.
    _stateset = stateset;
    
    // add this node to the new stateset to the parent list.
    if (_stateset.valid())
    {
        _stateset->addParent(this);
        if (_stateset->requiresUpdateTraversal()) ++delta_update;
        if (_stateset->requiresEventTraversal()) ++delta_event;
    }
    
    if (delta_update!=0)
    {
        setNumChildrenRequiringUpdateTraversal(getNumChildrenRequiringUpdateTraversal()+delta_update);
    }

    if (delta_event!=0)
    {
        setNumChildrenRequiringEventTraversal(getNumChildrenRequiringEventTraversal()+delta_event);
    }
}

osg::StateSet* Node::getOrCreateStateSet()
{
    if (!_stateset) setStateSet(new StateSet);
    return _stateset.get();
}

NodePathList Node::getParentalNodePaths(osg::Node* haltTraversalAtNode) const
{
    CollectParentPaths cpp(haltTraversalAtNode);
    const_cast<Node*>(this)->accept(cpp);
    return cpp._nodePaths;
}

MatrixList Node::getWorldMatrices(const osg::Node* haltTraversalAtNode) const
{
    CollectParentPaths cpp(haltTraversalAtNode);
    const_cast<Node*>(this)->accept(cpp);
    
    MatrixList matrices;
    
    for(NodePathList::iterator itr = cpp._nodePaths.begin();
        itr != cpp._nodePaths.end();
        ++itr)
    {
        NodePath& nodePath = *itr;
        if (nodePath.empty())
        {
            matrices.push_back(osg::Matrix::identity());
        }
        else
        {
            matrices.push_back(osg::computeLocalToWorld(nodePath));
        }
    }
    
    return matrices;
}

void Node::setUpdateCallback(NodeCallback* nc)
{
    // if no changes just return.
    if (_updateCallback==nc) return;
    
    // updated callback has been changed, will need to update
    // both _updateCallback and possibly the numChildrenRequiringAppTraversal
    // if the number of callbacks changes.


    // update the parents numChildrenRequiringAppTraversal
    // note, if _numChildrenRequiringUpdateTraversal!=0 then the
    // parents won't be affected by any app callback change,
    // so no need to inform them.
    if (_numChildrenRequiringUpdateTraversal==0 && !_parents.empty())
    {
        int delta = 0;
        if (_updateCallback.valid()) --delta;
        if (nc) ++delta;
        if (delta!=0)
        {
            // the number of callbacks has changed, need to pass this
            // on to parents so they know whether app traversal is
            // required on this subgraph.
            for(ParentList::iterator itr =_parents.begin();
                itr != _parents.end();
                ++itr)
            {    
                (*itr)->setNumChildrenRequiringUpdateTraversal(
                        (*itr)->getNumChildrenRequiringUpdateTraversal()+delta );
            }

        }
    }

    // set the app callback itself.
    _updateCallback = nc;

}

void Node::setNumChildrenRequiringUpdateTraversal(unsigned int num)
{
    // if no changes just return.
    if (_numChildrenRequiringUpdateTraversal==num) return;

    // note, if _updateCallback is set then the
    // parents won't be affected by any changes to
    // _numChildrenRequiringUpdateTraversal so no need to inform them.
    if (!_updateCallback && !_parents.empty())
    {
    
        // need to pass on changes to parents.        
        int delta = 0;
        if (_numChildrenRequiringUpdateTraversal>0) --delta;
        if (num>0) ++delta;
        if (delta!=0)
        {
            // the number of callbacks has changed, need to pass this
            // on to parents so they know whether app traversal is
            // required on this subgraph.
            for(ParentList::iterator itr =_parents.begin();
                itr != _parents.end();
                ++itr)
            {    
                (*itr)->setNumChildrenRequiringUpdateTraversal(
                    (*itr)->getNumChildrenRequiringUpdateTraversal()+delta
                    );
            }

        }
    }
    
    // finally update this objects value.
    _numChildrenRequiringUpdateTraversal=num;
    
}


void Node::setEventCallback(NodeCallback* nc)
{
    // if no changes just return.
    if (_eventCallback==nc) return;
    
    // event callback has been changed, will need to Event
    // both _EventCallback and possibly the numChildrenRequiringAppTraversal
    // if the number of callbacks changes.


    // Event the parents numChildrenRequiringAppTraversal
    // note, if _numChildrenRequiringEventTraversal!=0 then the
    // parents won't be affected by any app callback change,
    // so no need to inform them.
    if (_numChildrenRequiringEventTraversal==0 && !_parents.empty())
    {
        int delta = 0;
        if (_eventCallback.valid()) --delta;
        if (nc) ++delta;
        if (delta!=0)
        {
            // the number of callbacks has changed, need to pass this
            // on to parents so they know whether app traversal is
            // required on this subgraph.
            for(ParentList::iterator itr =_parents.begin();
                itr != _parents.end();
                ++itr)
            {    
                (*itr)->setNumChildrenRequiringEventTraversal(
                        (*itr)->getNumChildrenRequiringEventTraversal()+delta );
            }

        }
    }

    // set the app callback itself.
    _eventCallback = nc;

}

void Node::setNumChildrenRequiringEventTraversal(unsigned int num)
{
    // if no changes just return.
    if (_numChildrenRequiringEventTraversal==num) return;

    // note, if _EventCallback is set then the
    // parents won't be affected by any changes to
    // _numChildrenRequiringEventTraversal so no need to inform them.
    if (!_eventCallback && !_parents.empty())
    {
    
        // need to pass on changes to parents.        
        int delta = 0;
        if (_numChildrenRequiringEventTraversal>0) --delta;
        if (num>0) ++delta;
        if (delta!=0)
        {
            // the number of callbacks has changed, need to pass this
            // on to parents so they know whether app traversal is
            // required on this subgraph.
            for(ParentList::iterator itr =_parents.begin();
                itr != _parents.end();
                ++itr)
            {    
                (*itr)->setNumChildrenRequiringEventTraversal(
                    (*itr)->getNumChildrenRequiringEventTraversal()+delta
                    );
            }

        }
    }
    
    // finally Event this objects value.
    _numChildrenRequiringEventTraversal=num;
    
}

void Node::setCullingActive(bool active)
{
    // if no changes just return.
    if (_cullingActive == active) return;
    
    // culling active has been changed, will need to update
    // both _cullActive and possibly the parents numChildrenWithCullingDisabled
    // if culling disabled changes.

    // update the parents _numChildrenWithCullingDisabled
    // note, if _numChildrenWithCullingDisabled!=0 then the
    // parents won't be affected by any app callback change,
    // so no need to inform them.
    if (_numChildrenWithCullingDisabled==0 && !_parents.empty())
    {
        int delta = 0;
        if (!_cullingActive) --delta;
        if (!active) ++delta;
        if (delta!=0)
        {
            // the number of callbacks has changed, need to pass this
            // on to parents so they know whether app traversal is
            // required on this subgraph.
            for(ParentList::iterator itr =_parents.begin();
                itr != _parents.end();
                ++itr)
            {    
                (*itr)->setNumChildrenWithCullingDisabled(
                        (*itr)->getNumChildrenWithCullingDisabled()+delta );
            }

        }
    }

    // set the cullingActive itself.
    _cullingActive = active;
}

void Node::setNumChildrenWithCullingDisabled(unsigned int num)
{
    // if no changes just return.
    if (_numChildrenWithCullingDisabled==num) return;

    // note, if _cullingActive is false then the
    // parents won't be affected by any changes to
    // _numChildrenWithCullingDisabled so no need to inform them.
    if (_cullingActive && !_parents.empty())
    {
    
        // need to pass on changes to parents.        
        int delta = 0;
        if (_numChildrenWithCullingDisabled>0) --delta;
        if (num>0) ++delta;
        if (delta!=0)
        {
            // the number of callbacks has changed, need to pass this
            // on to parents so they know whether app traversal is
            // required on this subgraph.
            for(ParentList::iterator itr =_parents.begin();
                itr != _parents.end();
                ++itr)
            {    
                (*itr)->setNumChildrenWithCullingDisabled(
                    (*itr)->getNumChildrenWithCullingDisabled()+delta
                    );
            }

        }
    }
    
    // finally update this objects value.
    _numChildrenWithCullingDisabled=num;
}


void Node::setNumChildrenWithOccluderNodes(unsigned int num)
{
    // if no changes just return.
    if (_numChildrenWithOccluderNodes==num) return;

    // note, if this node is a OccluderNode then the
    // parents won't be affected by any changes to
    // _numChildrenWithOccluderNodes so no need to inform them.
    if (!dynamic_cast<OccluderNode*>(this) && !_parents.empty())
    {
    
        // need to pass on changes to parents.        
        int delta = 0;
        if (_numChildrenWithOccluderNodes>0) --delta;
        if (num>0) ++delta;
        if (delta!=0)
        {
            // the number of callbacks has changed, need to pass this
            // on to parents so they know whether app traversal is
            // required on this subgraph.
            for(ParentList::iterator itr =_parents.begin();
                itr != _parents.end();
                ++itr)
            {    
                (*itr)->setNumChildrenWithOccluderNodes(
                    (*itr)->getNumChildrenWithOccluderNodes()+delta
                    );
            }

        }
    }
    
    // finally update this objects value.
    _numChildrenWithOccluderNodes=num;
    
}

bool Node::containsOccluderNodes() const
{
    return _numChildrenWithOccluderNodes>0 || dynamic_cast<const OccluderNode*>(this);
}

BoundingSphere Node::computeBound() const
{
    return BoundingSphere();
}


void Node::dirtyBound()
{
    if (_boundingSphereComputed)
    {
        _boundingSphereComputed = false;

        // dirty parent bounding sphere's to ensure that all are valid.
        for(ParentList::iterator itr=_parents.begin();
            itr!=_parents.end();
            ++itr)
        {
            (*itr)->dirtyBound();
        }

    }
}

void Node::setThreadSafeRefUnref(bool threadSafe)
{
    Object::setThreadSafeRefUnref(threadSafe);
    
    if (_stateset.valid()) _stateset->setThreadSafeRefUnref(threadSafe);

    if (_updateCallback.valid()) _updateCallback->setThreadSafeRefUnref(threadSafe);
    if (_eventCallback.valid()) _eventCallback->setThreadSafeRefUnref(threadSafe);
    if (_cullCallback.valid()) _cullCallback->setThreadSafeRefUnref(threadSafe);

    if (_userData.valid()) _userData->setThreadSafeRefUnref(threadSafe);
}

void Node::resizeGLObjectBuffers(unsigned int maxSize)
{
    if (_stateset.valid()) _stateset->resizeGLObjectBuffers(maxSize);
    if (_updateCallback.valid()) _updateCallback->resizeGLObjectBuffers(maxSize);
    if (_eventCallback.valid()) _eventCallback->resizeGLObjectBuffers(maxSize);
    if (_cullCallback.valid()) _cullCallback->resizeGLObjectBuffers(maxSize);
}

void Node::releaseGLObjects(osg::State* state) const
{
    if (_stateset.valid()) _stateset->releaseGLObjects(state);
    if (_updateCallback.valid()) _updateCallback->releaseGLObjects(state);
    if (_eventCallback.valid()) _eventCallback->releaseGLObjects(state);
    if (_cullCallback.valid()) _cullCallback->releaseGLObjects(state);
}



Attachment: signature.asc
Description: This is a digitally signed message part

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

Reply via email to