osg 场景节点(事件,更新,裁剪)遍历状态开启机制

时间:2022-08-17 16:07:42

OSG 显示引擎为优化其显示效率,节点的更新遍历,事件处理遍历默认情况下是关闭的,内部通过更新计数来控制是否将事件处理,节点更新应用到子场景中。

void Node::setUpdateCallback(Callback* nc);

void Node::setEventCallback(Callback* nc);

void Node::setCullingActive(bool active);

下面我们看,新节点先加入到场景图中再设置回调函数 新节点设置回调函数再加入到场景图中  控制计数的改变有何不同:

a) 新节点先加入到场景图中再设置回调函数

    新节点添加前没有设置回调函数:                                                                       
bool Group::addChild( Node *child )
{
    return Group::insertChild( _children.size(), child );
}

bool Group::insertChild( unsigned int index, Node *child )
{
    if (!child) return false;

#if ENSURE_CHILD_IS_UNIQUE
    if (containsNode(child))
    {
        OSG_WARN<<"Adding non unique child to osg::Group, ignoring call"<<std::endl;
        return false;
    }
#endif

    if (child)
    {
        // handle deprecated geometry configurations by calling fixDeprecatedData().
        osg::Geometry* geometry = child->asGeometry();
        if (geometry && geometry->containsDeprecatedData()) geometry->fixDeprecatedData();


        // note ref_ptr<> automatically handles incrementing child's reference count.
        if (index >= _children.size())
        {
            index = _children.size();      // set correct index value to be passed to the "childInserted" method
            _children.push_back(child);
        }
        else
        {
            _children.insert(_children.begin()+index, child);
        }

        // register as parent of child.
        child->addParent(this);

        // tell any subclasses that a child has been inserted so that they can update themselves.
        childInserted(index);

        dirtyBound();

        // could now require app traversal thanks to the new subgraph,
        // so need to check and update if required.
        if (child->getNumChildrenRequiringUpdateTraversal()>0 ||
            child->getUpdateCallback())
        {
            setNumChildrenRequiringUpdateTraversal(
                getNumChildrenRequiringUpdateTraversal()+1
                );
        }

        // could now require app traversal thanks to the new subgraph,
        // so need to check and update if required.
        if (child->getNumChildrenRequiringEventTraversal()>0 ||
            child->getEventCallback())
        {
            setNumChildrenRequiringEventTraversal(
                getNumChildrenRequiringEventTraversal()+1
                );
        }

        // could now require disabling of culling thanks to the new subgraph,
        // so need to check and update if required.
        if (child->getNumChildrenWithCullingDisabled()>0 ||
            !child->getCullingActive())
        {
            setNumChildrenWithCullingDisabled(
                getNumChildrenWithCullingDisabled()+1
                );
        }

        if (child->getNumChildrenWithOccluderNodes()>0 ||
            dynamic_cast<osg::OccluderNode*>(child))
        {
            setNumChildrenWithOccluderNodes(
                getNumChildrenWithOccluderNodes()+1
                );
        }

        return true;
    }
    else return false;
}
从以上代码可以看出由于关联的回调函数函数指针为空且子节点遍历控制计数为零不会改变子节点所有相关的父节点的遍历计数。 这段代码也提醒我们在子节点关联回调函数或遍历控制计数不为零的情况下都会改变父节点遍历控制计数(看红色代码部分)。

 然后设置回调函数:

void Node::setUpdateCallback(Callback* 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;

}

从红色代码处可以看出,当节点已关联回调函数,再次设置新的回调函数时,不会影响父节点的遍历控制计数。


b)新节点设置回调函数再加入到场景图中

void Node::setUpdateCallback(Callback* nc)
bool Group::insertChild( unsigned int index, Node *child )

    从以上两个函数中可以看出,父节点遍历控制计数的改变是在加入到场景图中时自动处理的, 给节点设置回调函数或更改节点遍历控制计数都能将遍历下放到子节点,这两种方式有各自的用途,比如,我们需要在节点的virtual void traverse(osg::NodeVisitor& nv) 方法中处理所有的遍历情况,但该节点没有设置相关的回调函数或是子节点的访问是在过程中完成的,而不是串在场景图中,这里我们只需要将父节点遍历控制计数设置成大于0的数,节点遍历就会由根节点往下传递。