osg 場景節點(事件,更新,裁剪)遍歷狀態開啟機制
阿新 • • 發佈:2018-11-03
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;
}
從紅色程式碼處可以看出,當節點已關聯回撥函式,再次設定新的回撥函式時,不會影響父節點的遍歷控制計數。
void Node::setUpdateCallback(Callback* nc)
bool Group::insertChild( unsigned int index, Node *child )
從以上兩個函式中可以看出,父節點遍歷控制計數的改變是在加入到場景圖中時自動處理的, 給節點設定回撥函式或更改節點遍歷控制計數都能將遍歷下放到子節點,這兩種方式有各自的用途,比如,我們需要在節點的virtual void traverse(osg::NodeVisitor& nv) 方法中處理所有的遍歷情況,但該節點沒有設定相關的回撥函式或是子節點的訪問是在過程中完成的,而不是串在場景圖中,這裡我們只需要將父節點遍歷控制計數設定成大於0的數,節點遍歷就會由根節點往下傳遞。