Ogre PCZSceneManger学习笔记:
新加类:
(MovableObject)->PortalBase 视口(基类)PortalBase->Portal 可见性判断
PortalBase->AntiPortal 遮挡判断
(SceneCtlAllocatedObject)->PCZone 分割区域(纯虚类)
PCZone->DefaultZone 默认实现
(SceneNode)->PCZSceneNode PCZSceneManger的场景节点
场景创建方式:
Ogre::Root::createSceneManager("PCZSceneManager", "PCZSceneManager");
PCZSceneManager::createCamera(); //创建摄像机,创建完必须要attach到一个node上!创建灯光也要attach到一个node上
PCZSceneManager::createZone(const String& zoneType, const String& instanceName); //创建Zone,最好带mesh做enclousre AABB,不然判断scenenode的归属时会有问题
zoneType = "ZoneType_Default" 指向 DefaultZone
PCZSceneManager::createZoneFromFile(m_ZoneType, index, node, meshName); //带mesh创建,不推荐,资源组不可选
DefaultZone::setEnclosureNode(node); //直接设置包围节点,node里面要求东西,可以是个包围盒mesh,渲染时不会显示
PCZSceneManager::createPortal(const String& name, PortalBase::PORTAL_TYPE type = PortalBase::PORTAL_TYPE_QUAD); 创建portal
PCZSceneManager::createAntiPortal(const String& name, PortalBase::PORTAL_TYPE type = PortalBase::PORTAL_TYPE_QUAD); 创建AntiPortal
Portal::setNode(SceneNode* sn) //设置节点绑定,这样portal可以跟着节点一起移动
PCZSceneManager::createSceneNode ( const String &name );
//把相应的sceneNode加入到Zone中,一个sceneNode只能有一个homeZone,但可以被多个PCZZone遍历(见mVisitorNodeList)
PCZSceneManager::addPCZSceneNode(PCZSceneNode * sn, PCZone * homeZone);
{
// set the home zone
sn->setHomeZone(homeZone); //如果sn原来的homeZone是另一个,则原来的homeZone会把sn从homeNodeList里删除
// add the node
homeZone->_addNode(sn);
}
或者 添加附加节点
PCZSceneNode::addZoneToVisitingZonesMap(connectedZone);
void DefaultZone::_addNode( PCZSceneNode * n )
{
if (n->getHomeZone() == this)
{
// add a reference to this node in the "nodes at home in this zone" list
mHomeNodeList.insert( n );
}
else
{
// add a reference to this node in the "nodes visiting this zone" list
mVisitorNodeList.insert( n );
}
}
//PCZZone添加portal,一个portal只能有一个zone
PCZone::_addPortal(Portal * newPortal); / _addAntiPortal(Portal * newPortal);
void Portal::setTargetZone(PCZone* zone); //设置对面的区域
void Portal::setTargetPortal(Portal* portal); //设置对面区域相应的portal
渲染:
renderScene()
{
void _updateSceneGraph( Camera * cam )
{
// First do the standard scene graph update
SceneManager::_updateSceneGraph( cam );
// check for portal zone-related changes (portals intersecting other portals)
_updatePortalZoneData(); //检测portal是否有更新,判断portal::needUpdate(),portal在重新setCorner(int index, const Vector3& point)会更新
// mark nodes dirty base on portals that changed.
_dirtyNodeByMovingPortals(); //如果有portal变动,则标记里面所有的scenNode.setMoved(true)
// update all scene nodes
_updatePCZSceneNodes(); //更新里面所有的scenNode,由上面引起
{
for each sceneNode:
void PCZSceneManager::_updatePCZSceneNode( PCZSceneNode * pczsn )
{
// Skip if root Zone has been destroyed (shutdown conditions)
if (!mDefaultZone)
return;
// Skip if the node is the sceneroot node
if (pczsn == getRootSceneNode())
return;
// clear all references to visiting zones
pczsn->clearNodeFromVisitedZones(); //清除其他Zone里面visited标记
// Find the current home zone of the node associated with the pczsn entry.
//判断sceneNode是否还在原 homeZone里面,如果不在的话就重新寻找合适的homeZone
//注意:节点不要掉进DefaultZone,因为DefaultZone没有portal,所以永远出不去
_updateHomeZone( pczsn, false );
{
//807行
if (!pczsn->isAnchored()) //对于一般的节点是false,enclouse的节点初始化的是true
}
/* The following function does the following:
* 1) Check all portals in the home zone - if the node is touching the portal
* then add the node to the connected zone as a visitor
* 2) Recurse into visited zones in case the node spans several zones
*/
// (recursively) check each portal of home zone to see if the node is touching
if (pczsn->getHomeZone() &&
pczsn->allowedToVisit() == true)
{
pczsn->getHomeZone()->_checkNodeAgainstPortals(pczsn, 0); //向邻近的zone添加VisitedNode标记(一般在sceneNode横跨portal的时候发生)
}
// update zone-specific data for the node for any zones that require it
pczsn->updateZoneData();
}
}
// calculate zones affected by each light
_calcZonesAffectedByLights(cam); //更新光源
// clear update flags at end so user triggered updated are
// not cleared prematurely
_clearAllZonesPortalUpdateFlag();
}
.....
void _findVisibleObjects(Camera* cam, VisibleObjectsBoundsInfo* visibleBounds, bool onlyShadowCasters)
{
//为compositor优化的步骤参考1190行
//摄像机剔除(核心)
PCZone* cameraHomeZone = ((PCZSceneNode*)(cam->getParentSceneNode()))->getHomeZone();
cameraHomeZone->findVisibleNodes((PCZCamera*)cam,
mVisible,
getRenderQueue(),
visibleBounds,
onlyShadowCasters,
mDisplayNodes,
mShowBoundingBoxes);
}
}
//判断当前节点是否在本地Zone里面
PCZSceneManager::_updateHomeZone( PCZSceneNode * pczsn, bool allowBackTouches )
{
if (!mDefaultZone)
return;
PCZone * startzone;
PCZone * newHomeZone;
// start with current home zone of the node
startzone = pczsn->getHomeZone();
if (startzone) //如果当前sceneNode有 homeZone
{
if (!pczsn->isAnchored()) //一般节点
{
newHomeZone = startzone->updateNodeHomeZone(pczsn, false); //此函数基于portal计算,如果没有portal的话,始终是自己
}
else //包围盒
{
newHomeZone = startzone;
}
if (newHomeZone != startzone)
{
// add the node to the home zone
newHomeZone->_addNode(pczsn);
}
}
//当前没有homeZone,造成这个原因一般为 人为设置sceneNode的homeZone
else
{
// the node hasn't had it's home zone set yet, so do our best to
// find the home zone using volume testing.
Vector3 nodeCenter = pczsn->_getDerivedPosition();
//查找最合适的zone,如果没有找到,则默认为defaultZone(此zone默认是不带pportal的),
//就是说一旦设为defaultZone则再也出不去了,也不会显示(除非摄像机也在defaultZone)
PCZone * bestZone = findZoneForPoint(nodeCenter);
// set the best zone as the node's home zone
pczsn->setHomeZone(bestZone);
// add the node to the zone
bestZone->_addNode(pczsn);
}
return;
}
结论:
在加载场景后分配一般sceneNode时,一定要避免放在defaultZone里面,scenNode在移动是不用担心位置跑到外面去;
摄像机节点初始也要设置在有效Zone里面,位置要对,且创建好后就setMoved(true)或者setHomeZone(a valid zone);
PCZSceneNode::createChildSceneNode(const Vector3& translate, const Quaternion& rotate) 创建的子节点会和父节点放在同一个Zone里面
startzone->updateNodeHomeZone(pczsn, false)机制
{
// default to newHomeZone being the current home zone
PCZone * newHomeZone = pczsn->getHomeZone();
// Check all portals of the start zone for crossings!
Portal* portal;
PortalList::iterator pi, piend;
piend = mPortals.end();
for (pi = mPortals.begin(); pi != piend; pi++)
{
portal = *pi;
Portal::PortalIntersectResult pir = portal->intersects(pczsn);
switch (pir)
{
default:
case Portal::NO_INTERSECT: // node does not intersect portal - do nothing
case Portal::INTERSECT_NO_CROSS:// node intersects but does not cross portal - do nothing //中心点没有穿过portal,即使中心点在portal外面也判定不相交
break;
case Portal::INTERSECT_BACK_NO_CROSS:// node intersects but on the back of the portal
if (allowBackTouches) //默认是false,即中心点轨迹没有穿过portal,但是包围盒有跟portal相交
{
// node is on wrong side of the portal - fix if we're allowing backside touches
if (portal->getTargetZone() != this &&
portal->getTargetZone() != pczsn->getHomeZone())
{
// set the home zone of the node to the target zone of the portal
pczsn->setHomeZone(portal->getTargetZone());
// continue checking for portal crossings in the new zone
newHomeZone = portal->getTargetZone()->updateNodeHomeZone(pczsn, false);
}
}
break;
case Portal::INTERSECT_CROSS: //中心点轨迹穿过portal
// node intersects and crosses the portal - recurse into that zone as new home zone
if (portal->getTargetZone() != this &&
portal->getTargetZone() != pczsn->getHomeZone())
{
// set the home zone of the node to the target zone of the portal
pczsn->setHomeZone(portal->getTargetZone());
// continue checking for portal crossings in the new zone
newHomeZone = portal->getTargetZone()->updateNodeHomeZone(pczsn, true);
}
break;
}
}
// return the new home zone
return newHomeZone;
}
PortalBase::intersects(PCZSceneNode* pczsn) //判断sceneNode是否与portal相交,用的是前一帧和当前帧的轨迹检测的方法 再综合包围盒判断,见上面的注释
自动判断会引发一些问题,对于比较大的静态物体区域可能会跨过若干个相邻或者不相邻的zone的包围盒,但是我们更倾向其属于一个特定的zone,其他的zone虽然
包围盒有交叉,但是实际上在里面看不到,此时的sceneNode在创建时就需要用sceneManger::createSceneNode(),不要挂到root下面,手动_updateBounds()和PCZSceneManager::addPCZSceneNode
其他静态物体,放入场景中要么挂到root下面自动归位zone(小物体推荐),要么用上述方法手动放置到一个合适的zone里面(大物体推荐),切忌不要放到不合适的zone里面
动态物体的移动,需要在portal和zone规定的范围内移动,如果不从portal里面走的话,即使已经走出zone的范围,系统仍然判断在其zone内
疑点:
1)Portal::setNode() 作者说必须的,但本人认为不一定要,直接设置corner也能用
2)更新sceneNode,怎么判断PCZSceneNode.isMoved() 代码里面没有对怎么判断, Node::setPosition和setOrientaion()等变换函数不可派生,
只有_getDerivedScale,
_getDerivedPosition,
_getDerivedOrientation,
convertWorldToLocalPosition,
convertLocalToWorldPosition,
convertWorldToLocalOrientation
convertLocalToWorldOrientation
里面调用了 _updateFromParent 里面有setMoved(true)
其他变换后 需要手东设置并且要savePrePosition
====解决,rootSceneNode->_update()会每帧调用(scenemanger::updateScenGraphic()),里面会自动更新移动的子节点的_update() 里面会调用_updateFromParent和_updateBounds();
_updateBounds()也会触发_updateFromParent() 并setMoved(true);
所以节点都必须有rootNode->createChild产生
rootNode->createChild也会触发childNode的needUpdate(),会在后面scenemanger::updateScenGraphic()自动_update()
用sceneManger::createSceneNode()并且没有挂到root下面就不会自动更新
3)sceneManager::getRootSceneNode()默认属于NULL的Zone? ====解决,Yes