八叉树 ( 1 )

 

1、对Octree的描述

Octree的定义是:若不为空树的话,树中任一节点的子节点恰好只会有八个,或零个,也就是子节点不会有08以外的数目。那么,这要用来做什么?想象一个立方体,我们最少可以切成多少个相同等分的小立方体?答案就是8个。再想象我们有一个房间,房间里某个角落藏着一枚金币,我们想很快的把金币找出来,聪明的你会怎么做?我们可以把房间当成一个立方体,先切成八个小立方体,然后排除掉没有放任何东西的小立方体,再把有可能藏金币的小立方体继续切八等份….如此下去,平均在Log8(房间内的所有物品数)的时间内就可找到金币。因此,Octree就是用在3D空间中的场景管理,可以很快地知道物体在3D场景中的位置,或侦测与其它物体是否有碰撞以及是否在可视范围内。

2、实现Octree的原理

(1). 设定最大递归深度

(2). 找出场景的最大尺寸,并以此尺寸建立第一个立方体

(3). 依序将单位元元素丢入能被包含且没有子节点的立方体

(4). 若没有达到最大递归深度,就进行细分八等份,再将该立方体所装的单位元元素全部分担给八个子立方体

(5). 若发现子立方体所分配到的单位元元素数量不为零且跟父立方体是一样的,则该子立方体停止细分,因为跟据空间分割理论,细分的空间所得到的分配必定较少,若是一样数目,则再怎么切数目还是一样,会造成无穷切割的情形。

(6). 重复3,直到达到最大递归深度。

3Octree的存贮结构

本例中Octree的存贮结构用一个有(若干+八)个字段的记录来表示树中的每个结点。其中若干字段用来描述该结点的特性(本例中的特性为:节点的值和节点坐标),其余的八个字段用来作为存放指向其八个子结点的指针。此外,还有线性存储和18式存储。

4BSP TreeOctree对比

a) BSP Tree将场景分割为1个面,而Octree分割为3个面。

b) BSP Tree每个节点最多有2个子结点,而Octree最多有8个子结点

因此BSP Tree可以用在不论几唯的场景中,而Octree则常用于三维场景

5、本例实现Octree的若干关键技术及截图

A、主界面:

Main函数中使用while(true)产生类似Trinigy引擎中的渲染循环效果,等待用户输入操作。通过IF判断用户的输入并进行相应的操作。

[Copy to clipboard] [ -]
View CodeCPP
1234567891011
cin>>choiced; while(true){        if(choiced == 0)   return 0;        else if(choiced == 1)        {…………        } …………}

B、创建八叉树界面,需要用户输入递归次数和外包盒在三维场景中的坐标

本例使用3层深度,外包盒坐标为(1,100,1,100,1,100

[Copy to clipboard] [ -]
View CodeCPP
12345678910111213
template<class T>struct OctreeNode{T data; //节点数据 T xmin,xmax; //节点坐标,即六面体个顶点的坐标 T ymin,ymax; T zmin,zmax;    OctreeNode <T> *top_left_front,*top_left_back; //该节点的8个子结点,即个子六面体    OctreeNode <T> *top_right_front,*top_right_back;    OctreeNode <T> *bottom_left_front,*bottom_left_back;OctreeNode <T> *bottom_right_front,*bottom_right_back;…………};

树的结构图:

C、创建成功后先序遍历此八叉树。

[Copy to clipboard] [ -]
View CodeCPP
12345678910111213141516171819
void createOctree(OctreeNode<T> * &root,int maxdepth,double xmin,double xmax,double ymin,double ymax,double zmin,double zmax){ maxdepth=maxdepth-1; //每递归一次就将最大递归深度-1 if(maxdepth>=0) {  root=new OctreeNode<T>();  root->xmin=xmin; //为节点坐标赋值  root->xmax=xmax;………………  double xm=(xmax-xmin)/2;  double ym=(ymax-ymin)/2;  double zm=(ymax-ymin)/2; //计算节点3个维度上的半边长   //递归创建子树,根据每一个节点所处(是几号节点)的位置决定其子结点的坐标。 createOctree(root->top_left_front,maxdepth,xmin,xmax-xm,ymax-ym,ymax,zmax-zm,zmax); createOctree(root->top_left_back,maxdepth,xmin,xmax-xm,ymin,ymax-ym,zmax-zm,zmax); ……………… //8个子结点 }}

共产生了73个节点,由于比较多,没有截出全部结果。

D、查看此树的深度

即查找1号节点的个数。

[Copy to clipboard] [ -]
View CodeCPP
1234567891011121314
int i=1;template <class T>void preOrder( OctreeNode<T> * & p){    if(p)    { cout<<i<<".当前节点的值为:"<<p->data<<"/n坐标为:";  cout<<" xmin: "<<p->xmin<<" xmax: "<<p->xmax;…………  i+=1;        preOrder(p->top_left_front);        preOrder(p->top_left_back);………………    }}

E、查找点,需要用户输入坐标,本例使用坐标(808080

如果用户输入的点在场景外,则提示没有找到。如(2002200

根据用户定义的最大递归深度和场景的坐标,计算一下该场景被分割后的最小6面体各边的边长,然后判断用户输入的坐标是在当前节点的哪一个子结点中并循环搜索,直到达到叶子节点。(代码较多,详情请见下面的源代码)

6、源代码

八叉树节点类的定义:

[Copy to clipboard] [ -]
View CodeCPP
12345678
template<class T>int depth(OctreeNode<T> *& p){    if(p == NULL)        return -1;    int h = depth(p->top_left_front);    return h+1;}

创建节点的函数:

/

你可能感兴趣的:(八叉树 ( 1 ))