本文出自:https://github.com/xarray/osgRecipes 另加一些个人理解
#ifndef H_COOKBOOK_CH8_OCTREEBUILDER
#define H_COOKBOOK_CH8_OCTREEBUILDER
#include
#include
class OctreeBuilder
{
public:
OctreeBuilder() : _maxChildNumber(16), _maxTreeDepth(32), _maxLevel(0) {}
int getMaxLevel() const { return _maxLevel; }
void setMaxChildNumber( int max ) { _maxChildNumber = max; }
int getMaxChildNumber() const { return _maxChildNumber; }
void setMaxTreeDepth( int max ) { _maxTreeDepth = max; }
int getMaxTreeDepth() const { return _maxTreeDepth; }
typedef std::pair ElementInfo;
osg::Group* build( int depth, const osg::BoundingBox& total,std::vector& elements );
protected:
osg::LOD* createNewLevel( int level, const osg::Vec3& center, float radius );
osg::Node* createElement( const std::string& id, const osg::Vec3& center, float radius );
osg::Geode* createBoxForDebug( const osg::Vec3& max, const osg::Vec3& min );
int _maxChildNumber;
int _maxTreeDepth;
int _maxLevel;
};
#endif
#include
#include
#include
#include "OctreeBuilder.h"
osg::Group* OctreeBuilder::build( int depth, const osg::BoundingBox& total,std::vector& elements )
{
//基本思想就是如果当前立方体的可绘制体数目小于_maxChildNumber,则停止划分,
//否则继续递归划分
//最根本的思想就是 :借助递归算法,求出每个符合条件的“叶立方体”(叶立方体的条件自己定义,如本例中
//满足立方体中的节点数目<_maxChildNumber),结合osg::LOD,设置它的 lod->setCenter( center );
//和lod->setRadius( radius ); ,这样只有满足LOD条件的节点才会被现实出来,达到动态调度问题。
int s[3]; // axis sides (0 or 1)//划分当前立方体的辅助数组
osg::Vec3 extentSet[3] = {//当前立方体的包围体
total._min,
(total._max + total._min) * 0.5f,
total._max
};
osg::LOD* root;
std::vector childData;
for ( unsigned int i=0; i_maxTreeDepth )
isLeafNode = true;
osg::ref_ptr group = new osg::Group;
if ( !isLeafNode )
{
osg::ref_ptr childNodes[8];
//划分立方体为八个小方格2^3,
for ( s[0]=0; s[0]<2; ++s[0] )
{
for ( s[1]=0; s[1]<2; ++s[1] )
{
for ( s[2]=0; s[2]<2; ++s[2] )
{
// Calculate the child extent
osg::Vec3 min, max;//找到一个小方格的包围盒
for ( int a=0; a<3; ++a )
{
min[a] = (extentSet[s[a] + 0])[a];
max[a] = (extentSet[s[a] + 1])[a];
}
int id = s[0] + (2 * s[1]) + (4 * s[2]);//八个小立方体的编号
childNodes[id] = build( depth+1, osg::BoundingBox(min, max), childData );
}
}
}
for ( unsigned int i=0; i<8; ++i )
{
if ( childNodes[i] && childNodes[i]->getNumChildren() )
group->addChild( childNodes[i] );
}
}
else
{
for ( unsigned int i=0; iaddChild( createElement(obj.first, center, radius) );
}
}
osg::Vec3 center = (total._max + total._min) * 0.5;
float radius = (total._max - total._min).length() * 0.5f;
osg::LOD* level = createNewLevel( depth, center, radius );
level->insertChild( 0, createBoxForDebug(total._max, total._min) ); // For debug use
level->insertChild( 1, group.get() );
return level;
}
osg::LOD* OctreeBuilder::createNewLevel( int level, const osg::Vec3& center, float radius )
{
osg::ref_ptr lod = new osg::LOD;
lod->setCenterMode( osg::LOD::USER_DEFINED_CENTER );
lod->setCenter( center );
lod->setRadius( radius );
lod->setRange( 0, radius * 5.0f, FLT_MAX );
lod->setRange( 1, 0.0f, radius * 5.0f );
if ( _maxLevel geode = new osg::Geode;
geode->addDrawable( new osg::ShapeDrawable(new osg::Sphere(center, radius)) );
geode->setName( id );
return geode.release();
}
osg::Geode* OctreeBuilder::createBoxForDebug( const osg::Vec3& max, const osg::Vec3& min )
{
osg::Vec3 dir = max - min;
osg::ref_ptr va = new osg::Vec3Array(10);
(*va)[0] = min + osg::Vec3(0.0f, 0.0f, 0.0f);
(*va)[1] = min + osg::Vec3(0.0f, 0.0f, dir[2]);
(*va)[2] = min + osg::Vec3(dir[0], 0.0f, 0.0f);
(*va)[3] = min + osg::Vec3(dir[0], 0.0f, dir[2]);
(*va)[4] = min + osg::Vec3(dir[0], dir[1], 0.0f);
(*va)[5] = min + osg::Vec3(dir[0], dir[1], dir[2]);
(*va)[6] = min + osg::Vec3(0.0f, dir[1], 0.0f);
(*va)[7] = min + osg::Vec3(0.0f, dir[1], dir[2]);
(*va)[8] = min + osg::Vec3(0.0f, 0.0f, 0.0f);
(*va)[9] = min + osg::Vec3(0.0f, 0.0f, dir[2]);
osg::ref_ptr geom = new osg::Geometry;
geom->setVertexArray( va.get() );
geom->addPrimitiveSet( new osg::DrawArrays(GL_QUAD_STRIP, 0, 10) );
osg::ref_ptr geode = new osg::Geode;
geode->addDrawable( geom.get() );
geode->getOrCreateStateSet()->setAttribute(
new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE) );
geode->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
return geode.release();
}
#include
#include
#include
#include
#include
#include
#include
#include
#include "OctreeBuilder.h"
class PrintNameVisitor : public osgUtil::PrintVisitor
{
public:
PrintNameVisitor( std::ostream& out ) : osgUtil::PrintVisitor(out) {}
void apply( osg::Node& node )
{
if ( !node.getName().empty() )
{
output() << node.getName() << std::endl;
enter();
traverse( node );
leave();
}
else osgUtil::PrintVisitor::apply(node);
}
};
float randomValue( float min, float max )
{
return (min + (float)rand()/(RAND_MAX+1.0f) * (max - min));
}
osg::Vec3 randomVector( float min, float max )
{
return osg::Vec3( randomValue(min, max),
randomValue(min, max),
randomValue(min, max) );
}
int main( int argc, char** argv )
{
osg::BoundingBox globalBound;
std::vector globalElements;
for ( unsigned int i=0; i<5000; ++i )
{
osg::Vec3 pos = randomVector( -500.0f, 500.0f );
float radius = randomValue( 0.5f, 2.0f );
std::stringstream ss; ss << "Ball-" << i+1;
osg::Vec3 min = pos - osg::Vec3(radius, radius, radius);
osg::Vec3 max = pos + osg::Vec3(radius, radius, radius);
osg::BoundingBox region(min, max);
globalBound.expandBy( region );
globalElements.push_back( OctreeBuilder::ElementInfo(ss.str(), region) );
}
OctreeBuilder octree;
osg::ref_ptr root = octree.build( 0, globalBound, globalElements );
std::ofstream out("octree_output.txt");
PrintNameVisitor printer( out );
root->accept( printer );
osgViewer::Viewer viewer;
viewer.setSceneData( root.get() );
viewer.addEventHandler( new osgViewer::StatsHandler );
return viewer.run();
}