本文出自:https://github.com/xarray/osgRecipes 另加一些个人理解
#ifndef H_COOKBOOK_CH8_OCTREEBUILDER #define H_COOKBOOK_CH8_OCTREEBUILDER #include <osg/Geode> #include <osg/LOD> 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<std::string, osg::BoundingBox> ElementInfo; osg::Group* build( int depth, const osg::BoundingBox& total,std::vector<ElementInfo>& 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 <osg/ShapeDrawable> #include <osg/Geometry> #include <osg/PolygonMode> #include "OctreeBuilder.h" osg::Group* OctreeBuilder::build( int depth, const osg::BoundingBox& total,std::vector<ElementInfo>& 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<ElementInfo> childData; for ( unsigned int i=0; i<elements.size(); ++i ) { const ElementInfo& obj = elements[i]; if ( total.contains(obj.second._min) && total.contains(obj.second._max) ) childData.push_back( obj ); else if ( total.intersects(obj.second) ) { osg::Vec3 center = (obj.second._max + obj.second._min) * 0.5f; if ( total.contains(center) ) childData.push_back( obj ); } } bool isLeafNode = false; if ( (int)childData.size()<=_maxChildNumber || depth>_maxTreeDepth ) isLeafNode = true; osg::ref_ptr<osg::Group> group = new osg::Group; if ( !isLeafNode ) { osg::ref_ptr<osg::Group> 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; i<childData.size(); ++i ) { const ElementInfo& obj = childData[i]; osg::Vec3 center = (obj.second._max + obj.second._min) * 0.5; float radius = (obj.second._max - obj.second._min).length() * 0.5f; group->addChild( 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<osg::LOD> 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<level ) _maxLevel = level; return lod.release(); } osg::Node* OctreeBuilder::createElement( const std::string& id, const osg::Vec3& center, float radius ) { osg::ref_ptr<osg::Geode> 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<osg::Vec3Array> 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<osg::Geometry> geom = new osg::Geometry; geom->setVertexArray( va.get() ); geom->addPrimitiveSet( new osg::DrawArrays(GL_QUAD_STRIP, 0, 10) ); osg::ref_ptr<osg::Geode> 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 <osg/Group> #include <osgDB/ReadFile> #include <osgUtil/PrintVisitor> #include <osgViewer/ViewerEventHandlers> #include <osgViewer/Viewer> #include <iostream> #include <fstream> #include <sstream> #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<OctreeBuilder::ElementInfo> 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<osg::Group> 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(); }