OcTree 八叉树

本文出自: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();
}



 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值