转:Ogre源码剖析 - 场景管理之Octree
由于本人的引擎ProjectGaia服务于08年创新杯的游戏项目 – 3D太空游戏,所以理所应当加入Octree(八叉树 – 已经周宁学长发帖介绍过)场景管理器.参考了无数Octree的代码,发现还是我们可爱的Ogre写的最好,于是狂看n千行代码,把精髓提取出来给大家共享.
鉴于我们游戏版教程又n久没有更新了,今天发一篇我对Ogre场景管理器之Octree源代码分析的笔记.
所有代码采用伪代码.
首先回顾一下Ogre场景管理的架构
Ogre以插件形式提供了多种场景管理器
1. BSP管理用于支持Quake系列
2. Terrain管理,优化大型的地形场景
3. Octree管理,用处多多,很适合太空游戏
4. 等等… (我只了解这几个:P)
以上所有的管理器都继承与SceneManager类,实现其提供的接口
并把与SceneManager挂接的SceneNode根据自身的特性进行管理.
进入正题: OctreeSceneManager – 名字好长啊,打字都麻烦
对八叉树的简介在附件中,如果不了解的请先看完附件,我就不废话了.
注意: 实际代码中的成员,方法数量是我下面提供的10倍以上..我只讲关键过程用到的成员和方法.
OctreeSceneManager : SceneManager
主要的方法:
FindVisiableObjs
WalkTree
UpdateOctreeNode
FindNodeIn(BoundingXXX)
主要的成员:
Root : Octree
WorldBox : BoundingBox
Octree
主要的方法:
GetChildIndex(BoundingBox)
GetCullBox()
主要的成员:
nodeList : List<OctreeNode>
box : BoundingBox
child : Octree[2][2][2]
parent : Octree
OctreeNode : SceneNode
主要的方法:
GetOctree
AddToRenderQueue
UpdateBound
主要的成员
LocalBoundingBox
这三者之间的关系如下:
OctreeSceneMgr包含一棵Octree的root, 一个Octree对象则包含了八个方向的子树(见上面的成员介绍), 一个Octree用链表管理了n多个OctreeNode. (补充: 由于OctreeNode继承与SceneNode, 看过我前面介绍的兄弟们都知道,SceneNode也是以树状结构组成的.但是此树非我们Octree中的树,因此在接下来的讲解中无视, 但要注意一点:OctreeNode的LocalBoundingBox则是由该SceneNode下面挂接的所有SceneNode的AttachedEntity (想像为一个或者多个3D模型)的boundingBox合并而成的…有点复杂了,多看几遍)
从程序执行的步奏来讲解:
首先,场景管理器为何物?答曰:cull,即剔除不可见的东东.
场景管理器的调用步奏基本如下: --- 参考燕良大牛blog…
1. SceneManager::_renderScene()
2. SceneManager:: _updateSceneGraph从root node开始递归的调用了所有scene node的update,主要是计算了transform;
3.SceneManager::prepareRenderQueue 这里有一个Ogre场景管理的概念RenderQueue。粗略的看,这个类主要是为了把Objects按照材质分组,它还将管理对象的渲染优先权;
4.OctreeSceneManager::_findVisibleObjects
4.1 OctreeSceneManager::walkOctree
4.2 OctreeNode::_addToRenderQueue
可见这个操作利用SceneManager的空间管理算法来对所有的SceneNode进行了可见性判断,如果可能可见,则加入到RenderQueue中;
步奏也看了,那么开始分析具体的函数吧.从调用的顺序开始…
前三步是用于状态的update,和cull无关,从第4部开始才是关键…
_findVisibleObjects{
//没什么内容,初始化
//调用walkOctree
walkOctree(camera,renderQueue,root,visibilityInfo,false )
//搞定
}
walkOctree(camera,renderQueue,octree,visibilityInfo,foundVisible )
{
//顾名思义,遍历树
If(octree->NumOFNodes == 0)
Return //没有OtreeNode,也就是没有模型关节,根本谈不上可见判断
OctreeCamera::Visibility v = OctreeCamera::NONE; //不是伪代码
If(foundVisible){
//当为true的时候,说明这个节点的父节点已经判断为完全可见,所以不用费神了
v = OctreeCamera::FULL;
}
Else if(octree == root){
//根结点暴大,不可能全部visible
v = OctreeCamera::PARTIAL;
}
Else{
Box = octree->getCullBox() //Cull,剔除盒,大小比octree的boundingbox大125%
v = camera->getVisibilty() //通过摄像机的Frustum平截头体(形状和透视体一样,
//定义一个farClip面,一个nearClip,然后四点分别相连)大小判定
}
if ( v != OctreeCamera::NONE ) //如果不是完全不可见{
//处理当前Octree节点下面挂接的OctreeNodeList的可见性
//此处我会砍掉一些用于显示八叉树盒子线条的代码 – 无用
Foreach(OctreeNode n in octree){
//原文代码用的是迭代器
//对于每一个OctreeNode n,判断是否可见,AABB是轴对称包围盒的意思
Bool vis = camera -> isVisible( n -> _getWorldAABB() );
if(vis){
//Ogre很大很复杂,一下所有代码表示把该节点放到渲染队列
sn->_addToRenderQueue(camera,queue ,visibleBounds );
mVisible.push_back( sn );
if ( mDisplayNodes )
queue -> addRenderable( sn );
}
}
}
Foreach(Octree child in octree){
//对于当前octree的八个子结点
bool childfoundvisible = (v == OctreeCamera::FULL);
if(child!=null)
walkOctree … 递归调用
}
}
OctreeSceneMgr:UpdateOctreeNode ( OctreeNode * onode ) {
//由于挂接在八叉树上的OctreeNode:SceneNode的模型随时可能会移动,跑出当前的包
//围盒,所以我们需要重新计算传入的节点所在的Octree位置
If(该场景节点所挂接的Octree == null){
//没有被挂接在任何Octree上
//如果已经在root的范围以外,强行挂接的root上
if ( ! onode -> _isIn( mOctree -> mBox ) )
mOctree->_addNode( onode );
else
调用_addOctreeNode
}
If(该场景节点已经跑出所挂接的Octree){
从原来的Octree上remove掉
//没有被挂接在任何Octree上
//如果已经在root的范围以外,强行挂接的root上
if ( ! onode -> _isIn( mOctree -> mBox ) )
mOctree->_addNode( onode );
else
调用_addOctreeNode
}
}
//怎么添加一个新的场景节点 – 即挂接一个OctreeNode用于挂接模型
OctreeSceneManager::_addOctreeNode( OctreeNode * n, Octree *octant, int depth ){
首先获得要被挂接的节点n的包围盒
if ( ( 没有超过八叉树的最大深度) &&
被挂接的octree的包围盒大小是被挂接的节点包围盒的两倍 ){
Child = 根据节点n的包围盒大小计算其属于octree的哪个子结点
If(child == 0){
建立该节点
}
Else{
递归调用_addOctreeNode(n,child,++depth)
//作用在于找到改好能包围该场景节点的子树
}
}
}
好了,基本的流程就是这样,那么我要动工开始移植代码了.把Ogre的C++移植到基于XNA的C#中去…ZZZ…
转:Ogre源码剖析 - 场景管理之Octree的更多相关文章
- 转:Ogre源码剖析1
初学Ogre 貌似看到一些套路(ajohn) 1 Ogre的编译 获得最新的Ogre 1.71 和之前的Ogre比起来,除了sampler集成之外,最大的改变就是编译过程加入了Cmake,这个东西其 ...
- Flask核心机制--上下文源码剖析
一.前言 了解过flask的python开发者想必都知道flask中核心机制莫过于上下文管理,当然学习flask如果不了解其中的处理流程,可能在很多问题上不能得到解决,当然我在写本篇文章之前也看到了很 ...
- (原创滴~)STL源码剖析读书总结1——GP和内存管理
读完侯捷先生的<STL源码剖析>,感觉真如他本人所说的"庖丁解牛,恢恢乎游刃有余",STL底层的实现一览无余,给人一种自己的C++水平又提升了一个level的幻觉,呵呵 ...
- Hadoop源码学习笔记之NameNode启动场景流程二:http server启动源码剖析
NameNodeHttpServer启动源码剖析,这一部分主要按以下步骤进行: 一.源码调用分析 二.伪代码调用流程梳理 三.http server服务流程图解 第一步,源码调用分析 前一篇文章已经锁 ...
- linux0.11内核源码剖析:第一篇 内存管理、memory.c【转】
转自:http://www.cnblogs.com/v-July-v/archive/2011/01/06/1983695.html linux0.11内核源码剖析第一篇:memory.c July ...
- C++动态内存管理与源码剖析
引言 在本篇文章中,我们主要剖析c++中的动态内存管理,包括malloc.new expression.operator new.array new和allocator内存分配方法以及对应的内存释放方 ...
- Apache Spark源码剖析
Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著 ISBN 978-7-121-25420- ...
- 《Apache Spark源码剖析》
Spark Contributor,Databricks工程师连城,华为大数据平台开发部部长陈亮,网易杭州研究院副院长汪源,TalkingData首席数据科学家张夏天联袂力荐1.本书全面.系统地介绍了 ...
- (升级版)Spark从入门到精通(Scala编程、案例实战、高级特性、Spark内核源码剖析、Hadoop高端)
本课程主要讲解目前大数据领域最热门.最火爆.最有前景的技术——Spark.在本课程中,会从浅入深,基于大量案例实战,深度剖析和讲解Spark,并且会包含完全从企业真实复杂业务需求中抽取出的案例实战.课 ...
随机推荐
- sql server 复制,镜像,日志传输及故障转移集群区别
一, 数据库复制 SQL Server 2008数据库复制是通过发布/订阅的机制进行多台服务器之间的数据同步,我们把它用于数据库的同步备份.这里的同步备份指的是备份服务器与主服务器进行 实时数据同步, ...
- android 很多应用中用到的 listView + viewPager
比如网易啊啥的, 有些界面的 listview 第一行是可以 左右滑动的 viewpager,当滑动下面listView的时候 会一起滑动上去 工程目录: 效果图: ...
- Neo4j 2.0 生产环境集群搭建
一.在windows上搭建Neo4j ha cluster的配置方法: 例如:建立集群的三台机器的ip分别为:10.230.9.91,10.230.9.92,10.230.9.93. 10.230.9 ...
- Android -- ViewPager切换动画,PageTransformer
transformPage(View view, float position) view就是滑动中的那个view,position这里是float类型,是当前滑动状态的一个表示,比如当滑动到正全屏时 ...
- 【大数据】大数据处理-Lambda架构-Kappa架构
大数据处理-Lambda架构-Kappa架构 elasticsearch-head Elasticsearch-sql client NLPchina/elasticsearch-sql: Use S ...
- POJ 3525 Most Distant Point from the Sea 二分+半平面交
题目就是求多变形内部一点. 使得到任意边距离中的最小值最大. 那么我们想一下,可以发现其实求是看一个圆是否能放进这个多边形中. 那么我们就二分这个半径r,然后将多边形的每条边都往内退r距离. 求半平面 ...
- JAVA-SpringMVC开发第一个应用
找到eclipse工具路径 打开eclipse.exe 选择workspace的存放位置,点击ok 点击file-new 选择web-dynamic web project(动态web项目)-next ...
- mysql设置允许外网访问
1.设置mysql服务允许外网访问 修改mysql的配置文件,有的是my.ini,有的是my.cnf[linux],找到bind-address变量,这个值默认是127.0.0.1,设置为0.0.0. ...
- Android 多屏适配解决方式
1.主流手机必要測量的參数(通过详细的方法.測量出,须要測试手机的 以下的这些參数,我们主要使用的仅仅是 screenwidth 这个參数,其它參数仅仅是帮助我们更好的理解 屏幕适配) Displa ...
- C#.NET常见问题(FAQ)-如何让文本框textbox内容限制为数字
//限制文本框的输入 private void txtQuestionScore_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyCha ...