松散八叉树

八叉树简述

定义

八叉树是一种对三维世界进行场景管理的理想的空间数据结构。八叉树中根节点包含一个立方体包围盒。每个非叶子节点都拥有八个子节点,它们将双亲节点细分为八分体。也就是说而且每个子节点表示一个立方体的体积元素,而且所有子节点的体积加起来是父节点的体积。
当满足用户所定义的标准被满足时,停止细分。常见的停止标准有:

  • 节点包围盒达到一个特定大小
  • 节点内包含的多边形数目达到最小数目

 

 

数据

八叉树的每个节点至少要包含以下数据:

  • 包围盒 — 空间中的八叉树节点所包含的封闭立方体
  • 几何体列表 — 包含在该节点内的所包含的几何体
  • 子节点 — 指向每个子节点的指针
  • 相邻节点 — 每个节点最多有六个相邻节点(立方体拥有六个平面)。在碰撞检测的过程中,对八叉树的遍历要求指向相邻节点的指针。

树的建立

包含多边形数目大于最低阈值POLY_THRESHOLD的节点,BuildOctree()会产生8个子节点。BuildNode()函数创建了节点数据,主要包含两个步骤:

  • 为节点创建包围盒(通过父节点的包围盒确定宽度、高度以及深度,并通过索引i确定包围盒的中心位置是8个子节点中的哪一个)
  • 确定哪些多边形位于节点的包围盒内部

 

BuildOctree(Node N) { if(NumPolys(N)>POLY_THRESHOLD) for(int i = 0; i < 8; i++) { BuildNode(N->Child[i], i, N); BuildOctree(N->Child[i]); } }

1

2

3

4

5

6

7

8

9

10

11

BuildOctree(Node N)

{

  if(NumPolys(N)>POLY_THRESHOLD)

     for(int i = 0; i < 8; i++)

     {

       BuildNode(N->Child[i], i, N);

       BuildOctree(N->Child[i]);

     }

 

}

 

 

计算包围体的大小与中心点

某层节点立方体包围盒的边长计算公式:

L(depth)=W/(2depth)

 

某层节点与其相邻节点包围盒中心间距的计算公式:

S(depth)=W/(2depth)

 

W:worldsize

 

depth:depth(root)=0

 

判断物体所属的包围盒

伪代码如下:

struct node{ vector3 CubeCenter; node* Child[2][2][2]; ObjectList Objects; }; int Classify(plane p,volume v){ if(v is completely behind p){ return 0; }else if(v is completely in front of p){ return 1; }else{ // v straddles p. return 2; } } void InsertObjectIntoTree(node* n,Object* o){ int xc = Classify(plane(1,0,0,CubeCenter.x)),o.BoundingVolume); int yc = Classify(plane(0,1,0,CubeCenter.y)),o.BoundingVolume); int zc = Classify(plane(0,0,1,CubeCenter.z)),o.BoundingVolume); if(xc ==2 || yc == 2 || zc == 2) { //Object straddles one pr more of the child partition planes, //and so won't fit in any child node,so store it in this node. Objects.Insert(o) }else{ //Object fits in one of the child nodes.Recurse to find the //correct descendant. InsertObjectIntoTree(Child[zc][yc][xc],o) } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

struct node{

   vector3 CubeCenter;

   node*   Child[2][2][2];

   ObjectList Objects;

};

 

int Classify(plane p,volume v){

  if(v is completely behind p){

     return 0;

  }else if(v is completely in front of p){

     return 1;

  }else{

     // v straddles p.

     return 2;

  }

}

 

void InsertObjectIntoTree(node* n,Object* o){

  int xc = Classify(plane(1,0,0,CubeCenter.x)),o.BoundingVolume);

  int yc = Classify(plane(0,1,0,CubeCenter.y)),o.BoundingVolume);

  int zc = Classify(plane(0,0,1,CubeCenter.z)),o.BoundingVolume);

  if(xc ==2 || yc == 2 || zc == 2)

  {

    //Object straddles one pr more of the child partition planes,

    //and so won't fit in any child node,so store it in this node.

    Objects.Insert(o)

  }else{

    //Object fits in one of the child nodes.Recurse to find the

    //correct descendant.

    InsertObjectIntoTree(Child[zc][yc][xc],o)

  }

}

 

 

松散八叉树

八叉树是一种典型有效的空间数据结构。但是它有一些缺点:较小的物体可能因为其特殊位置被存储到一个具有非常大的包围盒体积的八叉树节点中。在《Game Programming Gems》中该问题被描述为层次划分过程中产生的“粘性(Sticky)”区域,较小的物体却保持在树的较高层次,降低了划分的效率。

松散八叉树的建立

松散八叉树的“松散”是指调整节点的包围体大小,“放松”包围立方体,但是同时节点的层次和节点的中心不变。在松散八叉树中,包围立方体边长的计算公式修改为:

L(depth)=k∗W/(2depth)

 

节点的间距依旧与传统八叉树保持一致。这意味着同层节点的包围立方体会相互重叠,如图c)所示。

松散八叉树

而k值的选择就是一个比较重要的问题,k值过小则无法体现松散八叉树减少粘性区域的优势,k值过大则会导致包围体过于松散。各类文献中基本都建议选择k=2,能够获得最好的效果。

假设一棵松散八叉树的k=2,我们可以计算出节点所在的深度,而物体在那一层深度中的哪个位置则取决于物体中心位置的坐标。

层次选择公式的推导过程:


已知深度的情况下,只需要选择距离物体中心最近的节点就可以确定物体应该被哪个节点的包围盒所包含。

index[x,y,z]=floor(object.[x,y,z]+W/2)/S(depth))

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的Rust松散4叉树的实现: 首先,定义一个节点的结构体,包含四个子节点(NW,NE,SW,SE),一个矩形边界框,以及一个点列表: ```rust use std::cmp::Ordering; #[derive(Debug)] struct Node<T> { nw: Option<Box<Node<T>>>, ne: Option<Box<Node<T>>>, sw: Option<Box<Node<T>>>, se: Option<Box<Node<T>>>, bbox: BBox, points: Vec<T>, } #[derive(Debug)] struct BBox { x1: f64, y1: f64, x2: f64, y2: f64, } ``` 然后,定义一个松散4叉树结构体,包含一个根节点和一个矩形边界框: ```rust #[derive(Debug)] struct LooseQuadtree<T> { root: Option<Box<Node<T>>>, bbox: BBox, } ``` 接下来,实现松散4叉树的插入和搜索函数: ```rust impl<T> LooseQuadtree<T> { fn insert(&mut self, point: T) { if !self.bbox.contains(point) { return; } if let Some(ref mut node) = self.root { if node.points.len() < 4 { node.points.push(point); } else { let mut nw_bbox = node.bbox.clone(); nw_bbox.x2 = (nw_bbox.x1 + nw_bbox.x2) / 2.0; nw_bbox.y2 = (nw_bbox.y1 + nw_bbox.y2) / 2.0; if nw_bbox.contains(point) { if node.nw.is_none() { node.nw = Some(Box::new(Node { nw: None, ne: None, sw: None, se: None, bbox: nw_bbox, points: vec![], })); } node.nw.as_mut().unwrap().insert(point); } else { // and so on for the other quadrants } } } else { self.root = Some(Box::new(Node { nw: None, ne: None, sw: None, se: None, bbox: self.bbox.clone(), points: vec![point], })); } } fn search(&self, bbox: &BBox) -> Vec<&T> { let mut result = vec![]; if let Some(ref node) = self.root { if bbox.intersects(&node.bbox) { for point in &node.points { if bbox.contains(point) { result.push(point); } } if let Some(ref nw) = node.nw { result.append(&mut nw.search(bbox)); } // and so on for the other quadrants } } result } } ``` 这个实现定义了一个节点结构体,一个矩形边界框结构体,一个松散4叉树结构体,以及`insert`和`search`方法。`insert`方法用于将一个点插入到松散4叉树中,`search`方法用于搜索一个矩形内的所有点。`insert`方法使用递归来遍历树,如果节点已满,则将点插入到相应的象限中。`search`方法也使用递归,如果节点与矩形相交,则遍历节点的所有点和子节点。 这个实现是一个简单的版本,还可以添加更多功能,例如删除点,计算节点的平衡因子等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值