场景管理:BSP算法C++实现

简单的实现了下BSP树

代码结构:

 

  • face.hpp 被管理的模型面片类
  • bsp_tree.hpp BSP树类
  • main.cpp程序入口

 

face.hpp

#pragma once
/*
//被管理的对象类
*/

//点结构
template <typename T>
struct Point
{
	T x;
	T y;
	T z;
	Point(){} //要声明这个空构造函数,否则下面的类编译出错
	Point(T _x,T _y,T _z):x(_x),y(_y),z(_z){}
	Point(const Point<T> &point)
	{
		x=point.x;
		y=point.y;
		z=point.z;
	}
	Point &operator+=(const T &n)
	{
		x+=n;
		y+=n;
		z+=n;
		return *this;
	}
};

//面结构,带三个顶点
template <typename T>
struct Face
{
	Point<T> point[3];
	void operator+=(const T &offset)
	{
		for(int i=0;i<3;++i)
			point[i]+=offset;
	}
};


bsp_tree.hpp

#pragma once
/*
//BSP树
//按照AABB包围盒分隔,按照最长边切分轴,然后把场景面片递归地划分到两个子节点,一开始就划分完毕,主要是作为静态场景管理
*/
#include <vector>
#include "face.hpp"

enum Axis
{
	AXIS_X,
	AXIS_Y,
	AXIS_Z
};


template <typename T>
struct BspTreeNode
{
	std::vector<Face<T>> faces;
	Axis axis;
	int depth;
	T splitter; //某个方向的分隔值
	Point<T> minPoint; //节点的包围盒顶点
	Point<T> maxPoint;
	BspTreeNode *lChild;
	BspTreeNode *rChild;
	BspTreeNode():lChild(nullptr),rChild(nullptr){}
};


template <typename T>
class BspTree
{
public:
	BspTree();
	~BspTree();
public:
	//构造树根以及整体AABB包围盒,由min和max两个点来设置
	void InitBspTree(std::vector<Face<T>> &_faces,Point<T> min_point,Point<T> max_point,int max_depth);
	void DeleteBspTree();
	void TraverseBspTree() const;
private:
	void cutFace(const Face<T> &face,Axis axis,const T &splitter,int &leftCount,int &rightCount,int &bothCount);
	void splitSpace(BspTreeNode<T> *node,Axis axis,int depth);
	void traverse(BspTreeNode<T> *node) const;
	void deleteTree(BspTreeNode<T> *root);
private:
	BspTreeNode<T> *root; //根节点
	int maxDepth; //最大递归深度,防止堆栈溢出

};

template <typename T>
BspTree<T>::BspTree()
{

}

template <typename T>
BspTree<T>::~BspTree()
{
}

template <typename T>
void BspTree<T>::InitBspTree(std::vector<Face<T>> &_faces,Point<T> min_point,Point<T> max_point,int max_depth)
{
	maxDepth=max_depth;
	root=new BspTreeNode<T>;
	root->depth=1;
	root->faces=_faces;
	root->lChild=nullptr;
	root->rChild=nullptr;

	root->maxPoint=max_point;
	root->minPoint=min_point;

	//初始化后就开始分割所有场景
	splitSpace(root,AXIS_X,1);
}

template <typename T>
void BspTree<T>::deleteTree(BspTreeNode<T> *root)
{
	if(root)
	{
		deleteTree(root->lChild);
		deleteTree(root->rChild);
		delete root;
		root=nullptr;
	}
}

template <typename T>
void BspTree<T>::DeleteBspTree()
{
	deleteTree(root);
}

template <typename T>
void BspTree<T>::cutFace(const Face<T> &face,Axis axis,const T &splitter,int &leftCount,int &rightCount,int &bothCount)
{
	T p[3]; //记录某个方向的点分量
	switch(axis)
	{
	case AXIS_X:
		p[0]=face.point[0].x;
		p[1]=face.point[1].x;
		p[2]=face.point[2].x;
		break;
	case AXIS_Y:
		p[0]=face.point[0].y;
		p[1]=face.point[1].y;
		p[2]=face.point[2].y;
		break;
	case AXIS_Z:
		p[0]=face.point[0].z;
		p[1]=face.point[1].z;
		p[2]=face.point[2].z;
		break;
	}
	//对分隔到两边的顶点计数
	for(int i=0;i<3;++i)
	{
		if(p[i]<splitter)
			leftCount++;
		else if(p[i]>splitter)
			rightCount++;
		else if(fabs(p[i]-splitter)<1e-6)
			bothCount++;
	}
}

template <typename T>
void BspTree<T>::splitSpace(BspTreeNode<T> *node,Axis axis,int depth)
{
	//在当前层节点做分割处理,为下层子节点数据做填充

	if(!node) 
		return;
	node->axis=axis;
	node->depth=depth;
	if(depth==maxDepth)
		return;
	if(node->faces.size()<2) //上层节点拥有的面片数太少就不再划分
		return;

	node->lChild=new BspTreeNode<T>;
	node->rChild=new BspTreeNode<T>;
	//先给子节点包围盒赋值
	node->lChild->maxPoint=node->maxPoint;
	node->lChild->minPoint=node->minPoint;
	node->rChild->maxPoint=node->maxPoint;
	node->rChild->minPoint=node->minPoint;

	T xLen=node->maxPoint.x-node->minPoint.x;
	T yLen=node->maxPoint.y-node->minPoint.y;
	T zLen=node->maxPoint.z-node->minPoint.z;
	//设置该节点的划分点
	Axis mAxis=AXIS_X;
	if(yLen>xLen&&yLen>zLen)
		mAxis=AXIS_Y;
	if(zLen>xLen&&zLen>yLen)
		mAxis=AXIS_Z;
	switch(mAxis)
	{
	case AXIS_X:
		node->splitter=(node->maxPoint.x+node->minPoint.x)/2; //取中间值
		//修改子节点的包围盒值
		node->lChild->maxPoint.x=node->splitter;
		node->rChild->minPoint.x=node->splitter;
		break;
	case AXIS_Y:
		node->splitter=(node->maxPoint.y+node->minPoint.y)/2;
		node->lChild->maxPoint.y=node->splitter;
		node->rChild->minPoint.y=node->splitter;
		break;
	case AXIS_Z:
		node->splitter=(node->maxPoint.z+node->minPoint.z)/2;
		node->lChild->maxPoint.z=node->splitter;
		node->rChild->minPoint.z=node->splitter;
		break;
	}

	
	for(int i=0;i<node->faces.size();++i)
	{
            // 重置计数
            int leftCount=0;
	    int rightCount=0;
	    int bothCount=0;

	    //对每个面做切分到子节点
	    cutFace(node->faces[i],mAxis,node->splitter,leftCount,rightCount,bothCount);
	    if(leftCount||bothCount)
		node->lChild->faces.push_back(node->faces[i]);
	    if(rightCount||bothCount)
		node->rChild->faces.push_back(node->faces[i]);
	}

	//关键的一步,分割完了之后,把当前层节点的面片清空
	node->faces.clear(); 

	//递归
	splitSpace(node->lChild,mAxis,depth+1);
	splitSpace(node->rChild,mAxis,depth+1);
	
}

template <typename T>
void BspTree<T>::traverse(BspTreeNode<T> *node) const
{
	if(!node)
		return;
	traverse(node->lChild);
	//中序遍历
	std::cout<<"节点深度 "<<node->depth<<" 面片数 "<<node->faces.size()<<std::endl;
	switch(node->axis)
	{
	case AXIS_X:
		std::cout<<"沿X轴分割 "<<"分割点x "<<node->splitter<<std::endl;
		break;
	case AXIS_Y:
		std::cout<<"沿Y轴分割 "<<"分割点y "<<node->splitter<<std::endl;
		break;
	case AXIS_Z:
		std::cout<<"沿Z轴分割 "<<"分割点z "<<node->splitter<<std::endl;
		break;
	}
	traverse(node->rChild);
}

template <typename T>
void BspTree<T>::TraverseBspTree() const
{
	//封装一层再遍历
	traverse(root);
}


main.cpp

#include <iostream>
#include <vector>
#include <time.h>
#include "bsp_tree.hpp"
using namespace std;

int main()
{
	BspTree<float> bspTree;

	//创造一些面片
	vector<Face<float>> face_vec;
	srand(time(0));
	for(int i=0;i<16;++i)
	{
		Face<float> face;
		for(int j=0;j<3;++j)
			face.point[j]=Point<float>(-200+rand()%400,-200+rand()%400,-200+rand()%400);
		face_vec.push_back(face);
	}	
	//初始化bsp树并遍历
	bspTree.InitBspTree(face_vec,Point<float>(-200,-200,-200),Point<float>(200,200,200),5);
	bspTree.TraverseBspTree();
	bspTree.DeleteBspTree();

	system("pause");
	return 0;
}

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值