简单实现了场景管理八叉树算法
代码结构:
#pragma once /* //被管理的对象类 */ class Object { public: Object(float _x,float _y,float _z,float _xSize,float _ySize,float _zSize); ~Object(); public: //对象的属性,例如坐标和长宽高,以左上角为锚点 float x; float y; float z; float xSize; float ySize; float zSize; };
#include "object.h" Object::Object(float _x,float _y,float _z,float _xSize,float _ySize,float _zSize): x(_x), y(_y), z(_z), xSize(_xSize), ySize(_ySize), zSize(_zSize) { } Object::~Object() { }
/* //八叉树节点类,用头节点代表八叉树 //采用opengl右手坐标系,靠近原点的那个角为锚点,方便计算 //本八叉树的策略是:1,一次划分所有节点,是满树;2,当立方体空间完全包含某物体才剔除,当立方体空间与某物体相交或者完全包含时才查询;3,对象放在完全包含它的区域叶子节点内,非根节点不存储对象,默认为物体不可能跨多个叶子节点,都在一个叶子节点的空间范围内部,未考虑交叉的情况 */ #pragma once #include <list> //八叉树节点类型 enum OctreeType { ROOT, //根 BOTTOM_LEFT_FRONT, // 1 BOTTOM_RIGHT_FRONT, // 2 BOTTOM_LEFT_BACK, // 3 BOTTOM_RIGHT_BACK, // 4 TOP_LEFT_FRONT, // 5 TOP_RIGHT_FRONT, // 6 TOP_LEFT_BACK, // 7 TOP_RIGHT_BACK // 8 }; template <class T> class OctreeNode { public: OctreeNode(float _x,float _y,float _z,float _xSize,float _ySize,float _zSize,OctreeType _octreeNodeType,int _level,int _maxLevel); ~OctreeNode(); public: void BuildTree(int level); //建立八叉树,划分到所有子节点 void InsertObject(T *object); //插入对象 std::list<T *> GetObjectsAt(float px,float py,float pz,float x_size,float y_size,float z_size); //查询对象,获得一片区域里的对象链表,考虑包含或相交,由于 void RemoveObjectsAt(float px,float py,float pz,float x_size,float y_size,float z_size); //删除对象,删除一片区域里的对象,此处只考虑完全包含的 private: bool IsContain(float px,float py,float pz,float x_size,float y_size,float z_size,T *object) const; //判断某个区域是否包含某对象 bool IsContain(float px,float py,float pz,float x_size,float y_size,float z_size,OctreeNode<T> *octreeNode) const; //重载,判断某个区域是否包含某个节点 bool IsInterSect(float px,float py,float pz,float x_size,float y_size,float z_size,OctreeNode<T> *octreeNode) const; //判断某个区域是否与节点相交,如果相交,则查询时要递归到其子节点 public: std::list<T *> objectList; //节点存储的对象列表 private: //节点属性 OctreeType octreeNodeType; float x; float y; float z; float xSize; float ySize; float zSize; int level; int maxLevel; //子节点,根据opengl坐标系,依次坐标增大 OctreeNode *bottom_left_front_node; OctreeNode *bottom_right_front_node; OctreeNode *bottom_left_back_node; OctreeNode *bottom_right_back_node; OctreeNode *top_left_front_node; OctreeNode *top_right_front_node; OctreeNode *top_left_back_node; OctreeNode *top_right_back_node; };
#include "octree_node.h" template <class T> OctreeNode<T>::OctreeNode(float _x,float _y,float _z,float _xSize,float _ySize,float _zSize,OctreeType _octreeNodeType,int _level,int _maxLevel): x(_x), y(_y), z(_z), xSize(_xSize), ySize(_ySize), zSize(_zSize), octreeNodeType(_octreeNodeType), level(_level), maxLevel(_maxLevel) { //初始子节点都赋空值 bottom_left_front_node=nullptr; bottom_right_front_node=nullptr; bottom_left_back_node=nullptr; bottom_right_back_node=nullptr; top_left_front_node=nullptr; top_right_front_node=nullptr; top_left_back_node=nullptr; top_right_back_node=nullptr; } template <class T> OctreeNode<T>::~OctreeNode() { } template <class T> bool OctreeNode<T>::IsContain(float px,float py,float pz,float x_size,float y_size,float z_size,T *object) const { if(object->x>=px &&object->x+object->xSize<=px+x_size &&object->y>=py &&object->y+object->ySize<=py+y_size &&object->z>=pz &&object->z+object->zSize<=pz+z_size) return true; return false; } template <class T> bool OctreeNode<T>::IsContain(float px,float py,float pz,float x_size,float y_size,float z_size,OctreeNode<T> *octreeNode) const { if(octreeNode->x>=px &&octreeNode->x+octreeNode->xSize<=px+x_size &&octreeNode->y>=py &&octreeNode->y+octreeNode->ySize<=py+y_size &&octreeNode->z>=pz &&octreeNode->z+octreeNode->zSize<=pz+z_size) return true; return false; } template <class T> bool OctreeNode<T>::IsInterSect(float px,float py,float pz,float x_size,float y_size,float z_size,OctreeNode<T> *octreeNode) const { if(octreeNode->x>px+x_size ||octreeNode->x+xSize<px ||octreeNode->y>py+y_size ||octreeNode->y+ySize<py ||octreeNode->z+zSize<pz ||octreeNode->z>pz+z_size) return false; return true; } template <class T> void OctreeNode<T>::BuildTree(int level) { //递归地进行八叉树空间划分,直到最大深度 if(level==maxLevel) return; //创建子节点 bottom_left_front_node=new OctreeNode(x,y,z,xSize/2,ySize/2,zSize/2,BOTTOM_LEFT_FRONT,level+1,maxLevel); bottom_right_front_node=new OctreeNode(x+xSize/2,y,z,xSize/2,ySize/2,zSize/2,BOTTOM_RIGHT_FRONT,level+1,maxLevel); bottom_left_back_node=new OctreeNode(x,y+ySize/2,z,xSize/2,ySize/2,zSize/2,BOTTOM_LEFT_BACK,level+1,maxLevel); bottom_right_back_node=new OctreeNode(x+xSize/2,y+ySize/2,z,xSize/2,ySize/2,zSize/2,BOTTOM_RIGHT_BACK,level+1,maxLevel); top_left_front_node=new OctreeNode(x,y,z+zSize/2,xSize/2,ySize/2,zSize/2,TOP_LEFT_FRONT,level+1,maxLevel); top_right_front_node=new OctreeNode(x+xSize/2,y,z+zSize/2,xSize/2,ySize/2,zSize/2,TOP_RIGHT_FRONT,level+1,maxLevel); top_left_back_node=new OctreeNode(x,y+ySize/2,z+zSize/2,xSize/2,ySize/2,zSize/2,TOP_LEFT_BACK,level+1,maxLevel); top_right_back_node=new OctreeNode(x+xSize/2,y+ySize/2,z+zSize/2,xSize/2,ySize/2,zSize/2,TOP_RIGHT_BACK,level+1,maxLevel); //递归构造 bottom_left_front_node->BuildTree(level+1); bottom_right_front_node->BuildTree(level+1); bottom_left_back_node->BuildTree(level+1); bottom_right_back_node->BuildTree(level+1); top_left_front_node->BuildTree(level+1); top_right_front_node->BuildTree(level+1); top_left_back_node->BuildTree(level+1); top_right_back_node->BuildTree(level+1); } template <class T> void OctreeNode<T>::InsertObject(T *object) { if(level==maxLevel) { objectList.push_back(object); return; } //递归地插入,直到叶子节点 //1 if(bottom_left_front_node&&IsContain(x,y,z,xSize/2,ySize/2,zSize/2,object)) { bottom_left_front_node->InsertObject(object); return; } //2 if(bottom_right_front_node&&IsContain(x+xSize/2,y,z,xSize/2,ySize/2,zSize/2,object)) { bottom_right_front_node->InsertObject(object); return; } //3 if(bottom_left_back_node&&IsContain(x,y+ySize/2,z,xSize/2,ySize/2,zSize/2,object)) { bottom_left_back_node->InsertObject(object); return; } //4 if(bottom_right_back_node&&IsContain(x+xSize/2,y+ySize/2,z,xSize/2,ySize/2,zSize/2,object)) { bottom_right_back_node->InsertObject(object); return; } //5 if(top_left_front_node&&IsContain(x,y,z+zSize/2,xSize/2,ySize/2,zSize/2,object)) { top_left_front_node->InsertObject(object); return; } //6 if(top_right_front_node&&IsContain(x+xSize/2,y,z+zSize/2,xSize/2,ySize/2,zSize/2,object)) { top_right_front_node->InsertObject(object); return; } //7 if(top_left_back_node&&IsContain(x,y+ySize/2,z+zSize/2,xSize/2,ySize/2,zSize/2,object)) { top_left_back_node->InsertObject(object); return; } //8 if(top_right_back_node&&IsContain(x+xSize/2,y+ySize/2,z+zSize/2,xSize/2,ySize/2,zSize/2,object)) { top_right_back_node->InsertObject(object); return; } } template <class T> std::list<T *> OctreeNode<T>::GetObjectsAt(float px,float py,float pz,float x_size,float y_size,float z_size) { if(level==maxLevel) return objectList; std::list<T *> resObjects; //递归地判断选定区域是否与某个节点相交(包含或被包含都算) //1 if(bottom_left_front_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,bottom_left_front_node)) { std::list<T *> childObjects1=bottom_left_front_node->GetObjectsAt(px,py,pz,x_size,y_size,z_size); resObjects.insert(resObjects.end(),childObjects1.begin(),childObjects1.end()); } //2 if(bottom_right_front_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,bottom_right_front_node)) { std::list<T *> childObjects2=bottom_right_front_node->GetObjectsAt(px,py,pz,x_size,y_size,z_size); resObjects.insert(resObjects.end(),childObjects2.begin(),childObjects2.end()); } //3 if(bottom_left_back_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,bottom_left_back_node)) { std::list<T *> childObjects3=bottom_left_back_node->GetObjectsAt(px,py,pz,x_size,y_size,z_size); resObjects.insert(resObjects.end(),childObjects3.begin(),childObjects3.end()); } //4 if(bottom_right_back_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,bottom_right_back_node)) { std::list<T *> childObjects4=bottom_right_back_node->GetObjectsAt(px,py,pz,x_size,y_size,z_size); resObjects.insert(resObjects.end(),childObjects4.begin(),childObjects4.end()); } //5 if(top_left_front_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,top_left_front_node)) { std::list<T *> childObjects5=top_left_front_node->GetObjectsAt(px,py,pz,x_size,y_size,z_size); resObjects.insert(resObjects.end(),childObjects5.begin(),childObjects5.end()); } //6 if(top_right_front_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,top_right_front_node)) { std::list<T *> childObjects6=top_right_front_node->GetObjectsAt(px,py,pz,x_size,y_size,z_size); resObjects.insert(resObjects.end(),childObjects6.begin(),childObjects6.end()); } //7 if(top_left_back_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,top_left_back_node)) { std::list<T *> childObjects7=top_left_back_node->GetObjectsAt(px,py,pz,x_size,y_size,z_size); resObjects.insert(resObjects.end(),childObjects7.begin(),childObjects7.end()); } //8 if(top_right_back_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,top_right_back_node)) { std::list<T *> childObjects8=top_right_back_node->GetObjectsAt(px,py,pz,x_size,y_size,z_size); resObjects.insert(resObjects.end(),childObjects8.begin(),childObjects8.end()); } return resObjects; } template <class T> void OctreeNode<T>::RemoveObjectsAt(float px,float py,float pz,float x_size,float y_size,float z_size) { if(level==maxLevel) { if(IsContain(px,py,pz,x_size,y_size,z_size,this)) objectList.clear(); //到了叶子节点且完全被包含就把该节点存储的对象清空 return; } //递归地判断选定区域是否与某个节点相交(包含或被包含都算),没有相交就不用再递归了 //1 if(bottom_left_front_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,bottom_left_front_node)) bottom_left_front_node->RemoveObjectsAt(px,py,pz,x_size,y_size,z_size); //2 if(bottom_right_front_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,bottom_right_front_node)) bottom_right_front_node->RemoveObjectsAt(px,py,pz,x_size,y_size,z_size); //3 if(bottom_left_back_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,bottom_left_back_node)) bottom_left_back_node->RemoveObjectsAt(px,py,pz,x_size,y_size,z_size); //4 if(bottom_right_back_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,bottom_right_back_node)) bottom_right_back_node->RemoveObjectsAt(px,py,pz,x_size,y_size,z_size); //5 if(top_left_front_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,top_left_front_node)) top_left_front_node->RemoveObjectsAt(px,py,pz,x_size,y_size,z_size); //6 if(top_right_front_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,top_right_front_node)) top_right_front_node->RemoveObjectsAt(px,py,pz,x_size,y_size,z_size); //7 if(top_left_back_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,top_left_back_node)) top_left_back_node->RemoveObjectsAt(px,py,pz,x_size,y_size,z_size); //8 if(top_right_back_node&&IsInterSect(px,py,pz,x_size,y_size,z_size,top_right_back_node)) top_right_back_node->RemoveObjectsAt(px,py,pz,x_size,y_size,z_size); }
#include <iostream> #include "object.h" #include "octree_node.h" #include "octree_node.cpp" //模板分开写要包含h和cpp using namespace std; int main() { OctreeNode<Object> *octree=new OctreeNode<Object>(0,0,0,200,200,200,ROOT,1,3); octree->BuildTree(1); octree->InsertObject(new Object(10,10,10,30,30,30)); octree->InsertObject(new Object(11,11,11,32,32,32)); octree->InsertObject(new Object(110,60,60,30,30,30)); octree->InsertObject(new Object(110,110,110,30,30,30)); octree->RemoveObjectsAt(0,0,0,110,70,70); list<Object *> resObjects=octree->GetObjectsAt(0,0,0,130,130,130); cout<<resObjects.size()<<endl; for(auto &t:resObjects) cout<<t->x<<' '<<t->y<<' '<<t->z<<' '<<t->xSize<<' '<<t->ySize<<' '<<t->zSize<<endl; delete octree; system("pause"); return 0; }