四叉树是一种常用的空间分割技术,主要用于地形的瓦片,创建三维模型的lod等。
废话少说,直接上代码:
被访问者:这里我们把访问的总体空间进行抽象。
用户在构造函数中需要输入范围和分层的级数
这里的osg::bouidngbox 是osg里面的类,用户也可以自定义自己的范围类,如下例子
class CBoudingBox
{
public:
double xMin;
double yMin;
double zMin;
double xMax;
double yMax;
double zMax;
}
//被访问者定义如下: accept 定义需要接受一个访问者来对被访问者的访问
CLodArea.h
class CLodArea
{
public:
CLodArea(int level, osg::BoundingBox bb);
virtual void accept(CLodAreaVisitor& visitor);
//getter
osg::BoundingBox getBoundingBox();
int getLevel();
private:
//lod的级数
int level;
osg::BoundingBox bb;
};
CLodArea.cpp
CLodArea::CLodArea(int level, osg::BoundingBox bb)
{
this->level = level;
this->bb = bb;
}
void CLodArea::accept(CLodAreaVisitor& visitor) //虚函数
{
visitor.visit(*this);
}
osg::BoundingBox CLodArea::getBoundingBox()
{
return bb;
}
int CLodArea::getLevel()
{
return level;
}
//访问者定义如下: 核心是apply,这里使用一个虚函数,允许用户继承后进行使用apply自定义自己的对于范围的处理方法,访问者把访问到的范围提供给apply,这里的遍历方式采用前序遍历,在遍历过程中,有个类似于栈的东西对遍历的父节点序号进行保存。
CLodAreaVisitor.h
class CLodAreaVisitor
{
public:
void visit(CLodArea& s);
protected:
virtual bool apply(std::vector<osg::BoundingBox> vecBoudingBox, int curLevel)
{
return true;
}
//进行遍历 四叉树
virtual void visitQuatree(osg::BoundingBox bb, int currentLevel = 1);
protected:
//计算四叉树区域
virtual std::vector<osg::BoundingBox> calQuatreeArea(osg::BoundingBox bb);
virtual void pushOntoIndexPath(int i);
virtual void popFromIndexPath();
virtual IndexPath obtainIndexPath();
protected:
IndexPath indexPath;
private:
int level;
};
ClodAreaVisitor.cpp
void CLodAreaVisitor::visit(CLodArea& s)
{
this->level = s.getLevel();
osg::BoundingBox bb = s.getBoundingBox();
//采用四叉树
//visitQuatree(bb);
}
void CLodAreaVisitor::visitQuatree(osg::BoundingBox bb, int currentLevel)
{
if (currentLevel > level) //递归超过最后一级
{
return;
}
std::vector<osg::BoundingBox> vecBoudingBox = calQuatreeArea(bb);
//进行处理
bool isSuccess = apply(vecBoudingBox, currentLevel);
if (!isSuccess)
{
return;
}
for (int i = 0; i < vecBoudingBox.size(); i++)
{
pushOntoIndexPath(i);
//递归下一级
visitQuatree(vecBoudingBox[i], currentLevel + 1);
popFromIndexPath();
}
}
std::vector<osg::BoundingBox> CLodAreaVisitor::calQuatreeArea(osg::BoundingBox bb)
{
//获取范围
double xMin = bb.xMin();
double yMin = bb.yMin();
double xMax = bb.xMax();
double yMax = bb.yMax();
double xLength = xMax - xMin;
double yLength = yMax - yMin;
//计算分割间隔
double xInterval = 0.5 * xLength;
double yInterval = 0.5 * yLength;
//计算分割范围
std::vector<osg::BoundingBox> vecBoudingBox;
for (int yIndex = 0; yIndex < 2; yIndex++)
{
for (int xIndex = 0; xIndex < 2; xIndex++)
{
double xBlockMin = xMin + xIndex * xInterval;
double xBlockMax = xBlockMin + xInterval;
double yBlockMin = yMin + yIndex * yInterval;
double yBlockMax = yBlockMin + yInterval;
osg::Vec3d xyzBlockMin(xBlockMin, yBlockMin, 0);
osg::Vec3d xyzBlockMax(xBlockMax, yBlockMax, 0);
osg::BoundingBox blockBb;
blockBb.set(xyzBlockMin, xyzBlockMax);
vecBoudingBox.push_back(blockBb);
}
}
return vecBoudingBox;
}
void CLodAreaVisitor::pushOntoIndexPath(int i)
{
indexPath.push_back(i);
}
void CLodAreaVisitor::popFromIndexPath()
{
indexPath.pop_back();
}
IndexPath CLodAreaVisitor::obtainIndexPath()
{
return indexPath;
}