Demo

GitHub

 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 实现的更多相关文章

  1. 基于octree的空间划分及搜索操作

    (1)  octree是一种用于管理稀疏3D数据的树形数据结构,每个内部节点都正好有八个子节点,介绍如何用octree在点云数据中进行空间划分及近邻搜索,实现“体素内近邻搜索(Neighbors wi ...

  2. PCL库简要说明

    PCL(PointCloudLibrary)是在吸收了前人点云相关研究基础上建立起来的大型跨平台开源C++编程库,它实现了大量点云相关的通用算法和高效数据结构,涉及到点云获取.滤波.分割.配准.检索. ...

  3. 游戏引擎架构 (Jason Gregory 著)

    第一部分 基础 第1章 导论 (已看) 第2章 专业工具 (已看) 第3章 游戏软件工程基础 (已看) 第4章 游戏所需的三维数学 (已看) 第二部分 低阶引擎系统 第5章 游戏支持系统 (已看) 第 ...

  4. PCL

    PCL(PointCloudLibrary)——是一个的模块化的现代C++模板库. 其基于以下第三方库:Boost.Eigen.FLANN.VTK.CUDA.OpenNI.Qhull,实现点云相关的获 ...

  5. CloudCompare 的简单的使用说明

    来自:https://blog.csdn.net/datase/article/details/79797795 File open:打开 save:保存 Global Shift settings: ...

  6. 图形学3D渲染管线学习

    图形学3D渲染管线 DX和OpenGL左右手坐标系不同,会有一些差距,得出的矩阵会不一样; OpenGL的投影平面不是视景体的近截面: 顶点(vertexs) 顶点坐标,颜色,法线,纹理坐标(UV), ...

  7. 八叉树(Octree)

    八叉树(Octree)是一种用于描述三维空间的树状数据结构.想象一个立方体,我们最少可以切成多少个相同等分的小立方体?答案就是8个.再想象我们有一个房间,房间里某个角落藏着一枚金币,我们想很快的把金币 ...

  8. 转:Ogre的八叉树场景管理器OctreeSceneManager

    上面是我绘制的一张图. 关于八叉树场景管理器主要需要关注两个类,其一是松散八叉树的数据结构Ogre::Octree,其二是八叉树场景管理器Ogre::OctreeSceneManager. 下面摘录图 ...

  9. 转:Ogre源码剖析 - 场景管理之Octree

    由于本人的引擎ProjectGaia服务于08年创新杯的游戏项目 – 3D太空游戏,所以理所应当加入Octree(八叉树 – 已经周宁学长发帖介绍过)场景管理器.参考了无数Octree的代码,发现还是 ...

随机推荐

  1. Js正则匹配处理时间

    <html> <body> <script type="text/javascript"> //将long 型 转换为 日期格式 年-月-日 h ...

  2. 初探CORBA组件化编程

    1.掌握组件化开发的概念,了解CORBA模型及ORB机制:2.掌握CORBA组件编程方法.二.实验内容(一).步骤1.配制环境JDK环境.2.编写编译IDL接口.3.编写编译服务端程序.4.编写编译客 ...

  3. Linux下文件查找命令find笔记

    在Linux命令下如果需要快速自己系统所需要处理的文件,可以通过find命令快速进行检索. 如果想在某个路径下查找相应的文件可以执行如下命令: find path -name filename # p ...

  4. Docker拉取images时报错Error response from daemon

    docker拉取redis时,抛出以下错误: [master@localhost ~]$ docker pull redis Using default tag: latest Error respo ...

  5. S-HR二开基础

    检测是否某个类部署上去了:http://10.3.0.115:6888/easportal/tools/getclassurl.jsp?className=com.kingdee.eas.hr.ser ...

  6. 【剑指Offer】41、和为S的连续正数序列

      题目描述:   小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数). ...

  7. Linux—Ubuntu14.0.5安装mongo

    1.安装mongo sudo apt-get install mongo 2.如果遇到找不到安装包运行,那就更新资源列表 sudo apt-get update 3.安装成功会自动运行mongo pg ...

  8. 重装系统后导入raid

    参考 https://ubuntuforums.org/showthread.php?t=2002217 https://www.funkypenguin.co.nz/note/importing-e ...

  9. 3.在eclipse中创建Web项目,并部署到Tomcat上

    1.找到创建web项目的菜单 2.创建web项目并选择web环境 3.查看创建好的web项目结构 4.在web项目的webContent文件夹下创建jsp页面 5.查看是否创建jsp页面成功,并编辑j ...

  10. D2007从win7升级到win10下的莫名其妙问题。

    在win7下听说win10被推荐,于是升级到win10.结果使用d2007不能打开,出现莫名其妙的错误.把bin\bds.exe改名bds1.exe后居然可以启动了.一番折腾后,这把bds1.exe改 ...