八叉树(Octree)Typescript 实现

export class Octree {
// 父&子树
private parent_node: any;
private children_nodes: Octree[];
// 原点
private oringePosition: THREE.Vector3;
private halfX: number;
private halfY: number;
private halfZ: number;
// 树深度
public depth: number;
// 内部实体
private entities: any[];
private _all_entities = new Array();
private _to_update: THREE.Mesh[];
// 叶子?叶节点
private _leaves: any;
private _need_leaves_update: boolean;
private _need_all_entities_update: boolean;
private BoxGeo: THREE.Geometry;
public BoxMesh: THREE.Mesh;
entities_per_node = 1;
max_depth = 5;
constructor(parent: Octree, origin, halfwidth, halfheight, halfdepth) {
this.oringePosition = origin;
this.halfX = halfwidth;
this.halfY = halfheight;
this.halfZ = halfdepth;
this.depth = parent === null ? 0 : parent.depth + 1;
// 设置当前树内无实体
this.entities = new Array();
// 父子节点
this.parent_node = parent;
this.children_nodes = new Array();
this._to_update = parent === null ? new Array() : parent._to_update;
this._leaves = new Array();
this._leaves.push(this);
this._need_leaves_update = false;
this._need_all_entities_update = false;
// 视觉感受
this.BoxGeo = new THREE.CubeGeometry(this.halfX * 2, this.halfY * 2, this.halfZ * 2);
this.BoxMesh = new THREE.Mesh(this.BoxGeo, new THREE.MeshBasicMaterial({color: 0x0, opacity: 1, wireframe: true}));
this.BoxMesh.position.set(this.oringePosition.clone().x, this.oringePosition.clone().y, this.oringePosition.clone().z);
if (parent !== null) {
this.BoxMesh.position.sub(parent.oringePosition);
parent.BoxMesh.add(this.BoxMesh);
}
}
// 当实体位置改变
onEntityPoseChanged(entity) {
if (this._to_update.indexOf(entity) === -1) {
this._to_update.push(entity);
}
}
// 判断交叉
intersects(entity) {
return this.contains(entity.position);
};
// 是否包含
contains(point) {
let diff = new THREE.Vector3();
// subVectors方法用来将三维向量的(x,y,z)坐标值分别于参数(a,b)的(x,y,z)相减.并返回新的坐标值的三维向量.
diff.subVectors(point, this.oringePosition);
return Math.abs(diff.x) <= this.halfX
&& Math.abs(diff.y) <= this.halfY
&& Math.abs(diff.z) <= this.halfZ;
};
// 子节点更新
needLeavesUpdate() {
let iter = this;
while (iter !== null) {
iter._need_leaves_update = true;
iter = iter.parent_node;
}
};
// 将实体从当前节点中删除,并将当前this指向根节点
remove(entity) {
for (let i = 0; i < this.entities.length; i++) {
if (this.entities[i] === entity) {
this.entities.splice(i, 1);
break;
}
}
// 删除过后将当前this指向根结点
let iter = this;
while (iter !== null) {
iter._need_all_entities_update = true;
iter = iter.parent_node;
}
};
// 细分
subdivide() {
/* _____________
/ 4 / 5 / | y
/_____ /______/ | | |
/ / / | | |___ x
/_____ / _____/ |/ | /
| 0 | 1 | |/7 / /
|_____ |_____ |/ | / z
| 2 | 3 | |/
|_____ |_____ |/ (lol)
*/
if (this.depth >= this.max_depth) {
return;
}
this.needLeavesUpdate();
let qwidth = this.halfX / 2;
let qheight = this.halfY / 2;
let qdepth = this.halfZ / 2;
this.children_nodes[0] = new Octree(this, new THREE.Vector3(this.oringePosition.x - qwidth,
this.oringePosition.y + qheight,
this.oringePosition.z + qdepth),
qwidth, qheight, qdepth);
this.children_nodes[1] = new Octree(this, new THREE.Vector3(this.oringePosition.x + qwidth,
this.oringePosition.y + qheight,
this.oringePosition.z + qdepth),
qwidth, qheight, qdepth);
this.children_nodes[2] = new Octree(this, new THREE.Vector3(this.oringePosition.x - qwidth,
this.oringePosition.y - qheight,
this.oringePosition.z + qdepth),
qwidth, qheight, qdepth);
this.children_nodes[3] = new Octree(this, new THREE.Vector3(this.oringePosition.x + qwidth,
this.oringePosition.y - qheight,
this.oringePosition.z + qdepth),
qwidth, qheight, qdepth);
this.children_nodes[4] = new Octree(this, new THREE.Vector3(this.oringePosition.x - qwidth,
this.oringePosition.y + qheight,
this.oringePosition.z - qdepth),
qwidth, qheight, qdepth);
this.children_nodes[5] = new Octree(this, new THREE.Vector3(this.oringePosition.x + qwidth,
this.oringePosition.y + qheight,
this.oringePosition.z - qdepth),
qwidth, qheight, qdepth);
this.children_nodes[6] = new Octree(this, new THREE.Vector3(this.oringePosition.x - qwidth,
this.oringePosition.y - qheight,
this.oringePosition.z - qdepth),
qwidth, qheight, qdepth);
this.children_nodes[7] = new Octree(this, new THREE.Vector3(this.oringePosition.x + qwidth,
this.oringePosition.y - qheight,
this.oringePosition.z - qdepth),
qwidth, qheight, qdepth);
};
add(entity) {
let _this = this;
function addToThis() {
let iter = _this;
while (iter !== null) {
iter._need_all_entities_update = true;
iter = iter.parent_node;
}
_this.entities.push(entity);
_this.BoxMesh.visible = true;
}
// 如果不包含=>返回
// 也就是说如果新增的Mesh 不在大Mesh中,不进行查找
if (!this.intersects(entity)) {
return;
}
if (this.depth >= this.max_depth) {
addToThis();
}
else if (this.children_nodes.length === 0) {
// ↑小于最大深度&没有子节点并且它里面没有实体的时候
// ↓每个节点中的数量小于规定要求
if (this.entities.length < this.entities_per_node) {
addToThis();
}
else {
// 如果它里面有实体,则拆分
this.subdivide();
// 拆分过后,如果内部有实体,则从这个节点中删除,并重新对所有实体做add动作(通过this值的变化)
if (this.entities.length !== 0) {
let entities_tmp = this.entities.slice();
this.entities.length = 0;
while (entities_tmp.length > 0) {
let ent = entities_tmp.pop();
this.remove(ent);
this.add(ent);
}
}
// 然后再将这个节点添加到指定位置
this.add(entity);
}
}
else {
// ↑如果它当前有节点,已经分成八份
// check if the obb intersects multiple children
let child_id = -1;
let multiple_intersect = false;
for (let i = 0; i < this.children_nodes.length; i++) {
if (this.children_nodes[i].intersects(entity)) {
if (child_id !== -1) {
multiple_intersect = true;
break;
}
child_id = i;
}
}
// 把当前结点放入制定的位置中
if (multiple_intersect) {
addToThis();
}
else {
// 放入0节点中
this.children_nodes[child_id].add(entity);
}
}
}
empty() {
if (this.entities.length > 0) {
return false;
}
for (let i = 0; i < this.children_nodes.length; i++) {
if (!this.children_nodes[i].empty()) {
return false;
}
}
return true;
};
countChildrenIntersections(max, entity) {
let children_idx = new Array();
for (let j = 0; j < this.children_nodes.length; j++) {
if (this.children_nodes[j].intersects(entity)) {
children_idx.push(j);
}
if (children_idx.length === max) {
break;
}
}
return children_idx;
}
// updates children entities reference
updateChildrenEntities() {
if (this._need_all_entities_update) {
this._all_entities.length = 0;
for (let i = 0; i < this.children_nodes.length; i++) {
this.children_nodes[i].updateChildrenEntities();
this._all_entities = this._all_entities.concat(this.children_nodes[i]._all_entities);
}
for (let i = 0; i < this.entities.length; i++) {
this._all_entities.push([this.entities[i], this]);
}
}
}
// updates leaves reference
updateLeaves() {
if (this._need_leaves_update) {
this._leaves.length = 0;
for (let i = 0; i < this.children_nodes.length; i++) {
this.children_nodes[i].updateLeaves();
this._leaves = this._leaves.concat(this.children_nodes[i]._leaves);
}
if (this.children_nodes.length === 0) {
this._leaves.push(this);
}
this._need_leaves_update = false;
}
}
update() {
let _this = this;
_this.updateChildrenEntities();
let entities_tmp = this._all_entities.slice();
entities_tmp.forEach(function (element) {
let entity = element[0];
for (let i = 0; i < _this._to_update.length; i++) {
if (entity === _this._to_update[i]) {
let octree;
let intersections;
// check if multiple intersection with children
// if yes do same recursively with parents till we can fit it entirely
// in one node, and add it to this node
octree = element[1];
while (octree !== null) {
intersections = octree.countChildrenIntersections(2, entity);
if (intersections.length === 1) {
// don't perform any operation if no update is required
if (element[1] === octree.children_nodes[intersections[0]]) {
break;
}
element[1].remove(entity);
octree.children_nodes[intersections[0]].add(entity);
break;
}
else if (octree.parent_node === null && intersections.length > 0) {
element[1].remove(entity);
octree.add(entity);
break;
}
else {
octree = octree.parent_node;
}
}
_this._to_update.splice(i, 1);
break;
}
}
});
// update _all_entities arrays
_this.updateChildrenEntities();
// get rid of dead leaves
_this.updateLeaves();
function pruneUp(node) {
if (node._all_entities.length <= 1) {
// remove the children from the leaves array and detach their mesh from parents
let removeChildrenNodes = function (nodes) {
for (let i = 0; i < nodes.children_nodes.length; i++) {
removeChildrenNodes(nodes.children_nodes[i]);
let idx = _this._leaves.indexOf(nodes.children_nodes[i]);
if (idx !== -1) {
_this._leaves.splice(idx, 1);
}
nodes.BoxMesh.remove(nodes.children_nodes[i].BoxMesh);
}
};
removeChildrenNodes(node);
node.needLeavesUpdate();
node.children_nodes.length = 0;
if (node._all_entities.length === 1 && (node._all_entities[0])[1] !== node) {
// if the entity was in a one of the child, put it in current node
node._all_entities[0][1] = node; // will update this ref for parents node too
node.add(node._all_entities[0][0]);
}
if (node.parent_node !== null) {
pruneUp(node.parent_node);
}
}
}
this._leaves.forEach(function (node) {
pruneUp(node);
});
};
}
八叉树(Octree)Typescript 实现的更多相关文章
- 基于octree的空间划分及搜索操作
(1) octree是一种用于管理稀疏3D数据的树形数据结构,每个内部节点都正好有八个子节点,介绍如何用octree在点云数据中进行空间划分及近邻搜索,实现“体素内近邻搜索(Neighbors wi ...
- PCL库简要说明
PCL(PointCloudLibrary)是在吸收了前人点云相关研究基础上建立起来的大型跨平台开源C++编程库,它实现了大量点云相关的通用算法和高效数据结构,涉及到点云获取.滤波.分割.配准.检索. ...
- 游戏引擎架构 (Jason Gregory 著)
第一部分 基础 第1章 导论 (已看) 第2章 专业工具 (已看) 第3章 游戏软件工程基础 (已看) 第4章 游戏所需的三维数学 (已看) 第二部分 低阶引擎系统 第5章 游戏支持系统 (已看) 第 ...
- PCL
PCL(PointCloudLibrary)——是一个的模块化的现代C++模板库. 其基于以下第三方库:Boost.Eigen.FLANN.VTK.CUDA.OpenNI.Qhull,实现点云相关的获 ...
- CloudCompare 的简单的使用说明
来自:https://blog.csdn.net/datase/article/details/79797795 File open:打开 save:保存 Global Shift settings: ...
- 图形学3D渲染管线学习
图形学3D渲染管线 DX和OpenGL左右手坐标系不同,会有一些差距,得出的矩阵会不一样; OpenGL的投影平面不是视景体的近截面: 顶点(vertexs) 顶点坐标,颜色,法线,纹理坐标(UV), ...
- 八叉树(Octree)
八叉树(Octree)是一种用于描述三维空间的树状数据结构.想象一个立方体,我们最少可以切成多少个相同等分的小立方体?答案就是8个.再想象我们有一个房间,房间里某个角落藏着一枚金币,我们想很快的把金币 ...
- 转:Ogre的八叉树场景管理器OctreeSceneManager
上面是我绘制的一张图. 关于八叉树场景管理器主要需要关注两个类,其一是松散八叉树的数据结构Ogre::Octree,其二是八叉树场景管理器Ogre::OctreeSceneManager. 下面摘录图 ...
- 转:Ogre源码剖析 - 场景管理之Octree
由于本人的引擎ProjectGaia服务于08年创新杯的游戏项目 – 3D太空游戏,所以理所应当加入Octree(八叉树 – 已经周宁学长发帖介绍过)场景管理器.参考了无数Octree的代码,发现还是 ...
随机推荐
- Python 集合 day3
集合(set)是一个无序的不重复元素序列. 可以使用大括号 { } 或者 set() 函数创建集合,集合用{},里面是一个一个元素,不同于key-value形式的字典: 注意:创建一个空集合必须用 s ...
- jmeter3.1 压测
压测目标:error 为0,线程起到250,服务器配置达到最大 一.Jmeter3.1 压测 JMeter3.1提供一个用于生成HTML页面格式图形化报告的扩展模块.该模块支持通过两种方式生成多维度图 ...
- 11.【Linq】
11.2.4 Cast . OfType 和显式类型的范围变量 这两个操作符很相似:都可以处理任意非类型化的序列(它们是非泛型 IEnumerable 类的扩展方法),并返回强类型的序列. Cast ...
- js中获取数组中的最大值最小值
var a=[1,2,3,5]; alert(Math.max.apply(null, a));//最大值 alert(Math.min.apply(null, a));//最小值 多维数组可以这么修 ...
- vue_music:排行榜rank中top-list.vue中样式的实现:class
排行榜的歌曲列表,根据排名渲染不同的样式,同时需要考虑分辨率的2x 3x图 不同的样式--:class 考虑分辨率的2x 3x图--需要在stylu中的mixin中bgImage根据屏幕分辨率选择图片 ...
- 【hihocoder 1499】A Box of Coins
[题目链接]:http://hihocoder.com/problemset/problem/1499 [题意] [题解] 贪心,模拟; 从左往右对于每一列; 如果上下两个格子; ① 有一个格子超过了 ...
- (12)Spring Boot改变JDK编译版本【从零开始学Spring Boot】
Spring Boot在编译的时候,是有默认JDK版本的,如果我们期望使用我们要的JDK版本的话,那么要怎么配置呢? 这个只需要修改pom.xml文件的<build> -- <plu ...
- LightOJ - 1232 - Coin Change (II)
先上题目: 1232 - Coin Change (II) PDF (English) Statistics Forum Time Limit: 1 second(s) Memory Limit: ...
- 对Django框架中Cookie的简单理解
概念的理解:首先Cookie和Session一样,是django中用于视图保持状态的方案之一.为什么要进行视图保留呢,这是因为浏览器在向服务器发出请求时,服务器不会像人一样,有记忆,服务器像鱼一样,在 ...
- 数学之路-python计算实战(4)-Lempel-Ziv压缩(2)
Format characters have the following meaning; the conversion between C and Python values should be o ...