八叉树(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的代码,发现还是 ...
随机推荐
- Js正则匹配处理时间
<html> <body> <script type="text/javascript"> //将long 型 转换为 日期格式 年-月-日 h ...
- 初探CORBA组件化编程
1.掌握组件化开发的概念,了解CORBA模型及ORB机制:2.掌握CORBA组件编程方法.二.实验内容(一).步骤1.配制环境JDK环境.2.编写编译IDL接口.3.编写编译服务端程序.4.编写编译客 ...
- Linux下文件查找命令find笔记
在Linux命令下如果需要快速自己系统所需要处理的文件,可以通过find命令快速进行检索. 如果想在某个路径下查找相应的文件可以执行如下命令: find path -name filename # p ...
- Docker拉取images时报错Error response from daemon
docker拉取redis时,抛出以下错误: [master@localhost ~]$ docker pull redis Using default tag: latest Error respo ...
- S-HR二开基础
检测是否某个类部署上去了:http://10.3.0.115:6888/easportal/tools/getclassurl.jsp?className=com.kingdee.eas.hr.ser ...
- 【剑指Offer】41、和为S的连续正数序列
题目描述: 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数). ...
- Linux—Ubuntu14.0.5安装mongo
1.安装mongo sudo apt-get install mongo 2.如果遇到找不到安装包运行,那就更新资源列表 sudo apt-get update 3.安装成功会自动运行mongo pg ...
- 重装系统后导入raid
参考 https://ubuntuforums.org/showthread.php?t=2002217 https://www.funkypenguin.co.nz/note/importing-e ...
- 3.在eclipse中创建Web项目,并部署到Tomcat上
1.找到创建web项目的菜单 2.创建web项目并选择web环境 3.查看创建好的web项目结构 4.在web项目的webContent文件夹下创建jsp页面 5.查看是否创建jsp页面成功,并编辑j ...
- D2007从win7升级到win10下的莫名其妙问题。
在win7下听说win10被推荐,于是升级到win10.结果使用d2007不能打开,出现莫名其妙的错误.把bin\bds.exe改名bds1.exe后居然可以启动了.一番折腾后,这把bds1.exe改 ...