转: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…

时间: 2024-10-12 18:08:50

转:Ogre源码剖析 - 场景管理之Octree的相关文章

转:Ogre源码剖析1

初学Ogre 貌似看到一些套路(ajohn) 1 Ogre的编译  获得最新的Ogre 1.71 和之前的Ogre比起来,除了sampler集成之外,最大的改变就是编译过程加入了Cmake,这个东西其实就是检测你电脑上装了些什么?  比如说是否安装DX_SDK 如果没有装,就不会有RenderSystem_DXXXX的工程,如果有装,检测DXSDK是什么版本的,相应产生  RenderSystem_DX9 RenderSystem_DX10 RenderSystem_DX11工程到你的解决方案中

《python源码剖析》笔记 pythonm内存管理机制

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.内存管理架构 Python的内存管理机制都有两套实现:debug模式和release模式 Python内存管理机制的层次结构: 图16-1 第0层是操作系统提供的内存管理接口,如malloc.free 第1层是Python基于第0层操作系统的内存管理接口包装而成的,主要是为了处理与平台相关的内存分配行为. 实现是一组以PyMem_为前缀的函数族 两套接口:函数和宏. 宏,可以避免函数调

图解STL内存管理的两种边界情况(STL源码剖析补充)

图解STL内存管理的两种边界情况(STL源码剖析补充) 第一种情况就是内存池剩余的小字节空间怎么处理,会不会有内存泄露,答案肯定是不会,但是这个过程是怎么处理的,以下的代码已经简化处理,直接放到VS2010里就可以运行 #include<stdio.h> #include<stdlib.h> static const size_t __ALIGN=8; static const size_t __MAX_BYTES=128; static const size_t __NFREEL

Phaser实现源码剖析

在这里首先说明一下,由于Phaser在4.3代码里是存在,但并没有被开放出来供使用,但已经被本人大致研究了,因此也一并进行剖析. Phaser是一个可以重复利用的同步栅栏,功能上与CyclicBarrier和CountDownLatch相似,不过提供更加灵活的用法.也就是说,Phaser的同步模型与它们差不多.一般运用的场景是一组线程希望同时到达某个执行点后(先到达的会被阻塞),执行一个指定任务,然后这些线程才被唤醒继续执行其它任务. Phaser一般是定义一个parties数(parties一

(升级版)Spark从入门到精通(Scala编程、案例实战、高级特性、Spark内核源码剖析、Hadoop高端)

本课程主要讲解目前大数据领域最热门.最火爆.最有前景的技术——Spark.在本课程中,会从浅入深,基于大量案例实战,深度剖析和讲解Spark,并且会包含完全从企业真实复杂业务需求中抽取出的案例实战.课程会涵盖Scala编程详解.Spark核心编程.Spark SQL和Spark Streaming.Spark内核以及源码剖析.性能调优.企业级案例实战等部分.完全从零起步,让学员可以一站式精通Spark企业级大数据开发,提升自己的职场竞争力,实现更好的升职或者跳槽,或者从j2ee等传统软件开发工程

《Apache Spark源码剖析》

Spark Contributor,Databricks工程师连城,华为大数据平台开发部部长陈亮,网易杭州研究院副院长汪源,TalkingData首席数据科学家张夏天联袂力荐1.本书全面.系统地介绍了Spark源码,深入浅出,细致入微2.提供给读者一系列分析源码的实用技巧,并给出一个合理的阅读顺序3.始终抓住资源分配.消息传递.容错处理等基本问题,抽丝拨茧4.一步步寻找答案,所有问题迎刃而解,使读者知其然更知其所以然 内容简介 书籍计算机书籍 <Apache Spark源码剖析>以Spark

转:Ogre源码分析之Root类、Facade模式

Ogre源码分析(一)Root类,Facade模式 Ogre中的Root对象是一个Ogre应用程序的主入口点.因为它是整个Ogre引擎的外观(Façade)类.通过Root对象来开启和停止Ogre是最简单的一种方式:当你构造构造一个Root实例的时候你就启动了整个Ogre,当析构的时候(让它停止活动或者执行delete删除它)Ogre也就关闭了. API手册中这样介绍到:Ogre::Root 类代表了客户应用程序的入口点.在这里,应用程序可以获得系统的重要的访问权,也就是获取渲染系统 ,管理配置

【译】Doom3源码剖析(1/6)——引论

原文地址:doom3 source code review 转载请注明出处:[译]Doom3源码剖析(1/6)——引论 在2011年11月23号,id Software继续维持他们开放源码的作风,开放了他们先前游戏引擎的源代码.这次公布的源码是idTech4,这款游戏引擎曾用来制作猎魂,雷神之锤4,当然还有毁灭战士3.公布源代码之后数小时之内,Github上就已经fork了400多次.同时人们开始探索这款游戏的内部实现机制,并试着将该游戏转移到其他平台上.我(下文均指本文作者)也立即实现了Mac

豆瓣Redis解决方案Codis源码剖析:Dashboard

豆瓣Redis解决方案Codis源码剖析:Dashboard 1.不只是Dashboard 虽然名字叫Dashboard,但它在Codis中的作用却不可小觑.它不仅仅是Dashboard管理页面,更重要的是,它负责监控和指挥各个Proxy的负载均衡(数据分布和迁移).并且,所有API都以RESTFul接口的形式对外提供,供Proxy和codis-config(Codis的命令行工具)调用.下面就来看一下数据分布和迁移的代码执行流程. Dashboard涉及到的知识点比较多,包括Martini框架