#松散八叉树设计方案
##1.松散八叉树
八叉树就是用在3D空间中的场景管理,可以很快地知道物体在3D场景中的位置,或侦测与其它物体是否有碰撞以及是否在可视范围内。
松散八叉树不会出现物体跨界的情况,也不会出现很小的物体需要放到很大的节点之中的问题。
缺点:由于节点与节点之间相互重叠在一起,相对于经典八叉树来说,做视锥裁剪时,程序逻辑可能需要检查更多的节点。
##2.[写时复制](http://hackerboss.com/copy-on-write-101-part-1-what-is-it/)
写入时复制(Copy-on-write)是一个被使用在程式设计领域的最佳化策略。其基础的观念是,如果有多个呼叫者(callers)同时要求相同资源,他们会共同取得相同的指标指向相同的资源,直到某个呼叫者(caller)尝试修改资源时,系统才会真正复制一个副本(private copy)给该呼叫者,以避免被修改的资源被直接察觉到,这过程对其他的呼叫只都是通透的(transparently)。此作法主要的优点是如果呼叫者并没有修改该资源,就不会有副本(private copy)被建立。
## 3.设计方案
八叉树主要管理两种数据:八叉树据本身节点;物件数据。
###3.1.数据结构
pub struct S3dOctree{
pub root : AtomicPtr<*mut _S3dOTNode>, // 八叉树根节点,支持原子操作。
}
pub struct _S3dOTNode{
pub aabb: PiAABBBox, // 本节点的世界坐标系下空间范围
pub children : [*mut _S3dOTNode;octree::_CHD_NUM], //子节点
pub objects:*const _S3dOTObj, // 节点列表,注意:第一个obj为引用占位
pub obj_num:i32, //本节点直接持有的物体数量
pub all_obj_num:i32, //包括自己和其所有子节点所持有的物体数量
pub splitted:bool, //此节点是否执行过分裂
pub ref_count : AtomicUsize; //引用计数,根节点有用,原子操作
}
###3.2. 应用场景
本方案适用于:一个写线程,一个或多个读线程的场景。一个场景中只存在一棵树。
###3.3.设计思路
写操作时,复制(深拷贝)老树(真实存在的)上变化的节点生成新的节点,这些新生成的节点 和 老树上的不变节点,组成一棵新的树,
称新树(逻辑树)。然后用新树的根节点指针替换(原子操作)老树的根节点指针,释放老树上变化节点所占用的内存空间。整个过程就
完成了八叉树的写时复制。
方案中两处使用原子操作:根节点内部引用计数;树的根节点指针。根节点使用引用计数用于树的节点内存管理,当根节点为引用计数为0时,
释放老树上变化的节点所占内存(释放工作由写线程完成)。读线程访问树时,根节点引用加1,访问完成时,根节点引用计数减1。使用原子操作,不需要加额外的锁,开销小。
八叉树的变化由物件的增、删、改驱动的。物件的插入,可能导致的几种结果: 插入已有节点,表现为节点更新;分化出新节点,物件插入新节点,
表现为节点增加;分化出新节点,并导致父节点的物件下放,表现为节点新增和节点更新同时发生。物件删除,可能导致: 从节点上删除,表现为
节点数据更新;节点收缩,表现为节点删除;收缩节点的同时,上放物件,表现为节点新增和节点更新同时发生。物件的修改,可能导致:节点收缩;节点新增;节点删除;或同时发生。
## 4.八叉树的性能
### 4.1 读的性能
语言 :c , rust(cow),rust
树的规模(物件个数): 1000,5000,10000,100000
测试方法: 遍历树10000次
结果: 单位秒,保留小数后两位有效数字
<table>
<tr>
<td> </td>
<td>1000</td>
<td>5000</td>
<td>10000</td>
<td>100000</td>
</tr>
<tr>
<td>c</td>
<td>0.31</td>
<td>1.35</td>
<td>2.68</td>
<td>26.73</td>
</tr>
<tr>
<td>rust(cow)</td>
<td>0.71</td>
<td>3.15</td>
<td>5.52</td>
<td>57.28</td>
</tr>
<tr>
<td>rust</td>
<td>1.28</td>
<td>5.77</td>
<td>11.09</td>
<td>100.15</td>
</tr>
</table>
### 4.2 写的性能
语言 :c , rust(cow),rust
测试方法: 向树插入1000,5000,10000,100000规模的物件
结果: 单位秒,保留小数后两位有效数字
<table>
<tr>
<td> </td>
<td>1000</td>
<td>5000</td>
<td>10000</td>
<td>100000</td>
</tr>
<tr>
<td>c</td>
<td>0.0014</td>
<td>0.0073</td>
<td>0.014</td>
<td>0.14</td>
</tr>
<tr>
<td>rust(cow)</td>
<td>0.026</td>
<td>0.15</td>
<td>0.32</td>
<td>41.66</td>
</tr>
<tr>
<td>rust</td>
<td>0.00026</td>
<td>0.0012</td>
<td>0.0023</td>
<td>0.020</td>
</tr>
</table>
#s3d 整体设计方案
##1.概述
s3d 向外提供添加、删除、修改、查询物件的接口;对内管理八叉树数据。
##2.数据结构
//场景数据
pub struct S3dScene{
pub octree : Box<Octree>, //八叉树指针,一个场景中只存在一个棵树
pub obj_index : AtomicUsize, //物件索引器
pub obj_map: VecMap<_S3dObj>, //物件存储器
pub write_read: RwLock<u32>, //读写锁
}
//物件数据
pub struct S3dObj {
pub point : PiVector3, //
pub aabb : PiAABBBox, //aabb
pub obb : PiOBBBox, //obb
pub internal_mask : u32,//掩码
}
##3.设计思路
### 3.1 物件存储
物件的数据的信息存在S3d层,方便直接获取单个物件的数据,不需要每次都查询八叉树,加快了数据的查询;八叉树只存储物件的索引和aabb数据,加快写时复制的效率。物件存储器负责物件的存储,数据以<索引,S3dObj对象>的形式存储;物件索引器负责物件的索引的分配,支持原子操作,保证了数据的唯一性;读写锁控制物件存储器的读写权限。
物件创建和插入过程: 获取从物件索引器获取索引,索引器自加1(原子操作);创建S3dObj对象;将<索引,物件对象>插入物件对象管理器,将物件插入八叉树(只保存索引和AABB数据)。