那该如何将点云ID写入文件呢?这样有利于拾取,获取相应属性。
设置一个结构体

struct ElementInfo
{
	//坐标
	osg::Vec3 pos;
	//全局变量
	uint32_t globalID;
};

在创建点云geometry时写入

// ID存到UserDataContainer(不影响渲染!)
osg::ref_ptr<osg::UserDataContainer> udc = new osg::DefaultUserDataContainer();
udc->addUserObject(ids);  // 不加名字也行
// 或者加个名字,方便查找
// udc->setUserObject(ids, "GlobalIDs");
geometry->setUserDataContainer(udc);
	//给当前geom设置名字
	std::string strBlockID = "block" + std::to_string(_currentBlockID);
	theGeom->setName(strBlockID);

拾取
bool bFind = false;
const osgUtil::PolytopeIntersector::Intersections& intersections = intersector->getIntersections();

            for (osgUtil::PolytopeIntersector::Intersections::iterator it =
                intersections.begin(); it != intersections.end(); ++it) 
            {
                osg::Geometry* theGeom = dynamic_cast<osg::Geometry*> (it->drawable.get());
                if (theGeom == nullptr)
                {
                    continue;
                }
                std::string theName = theGeom->getName();
                size_t pos = theName.find("block");
                if (pos == std::string::npos) 
                {
                    continue;
                }
                int localIndex = it->primitiveIndex;
                int globalID = -1;
                // 3. 获取全局ID(从UserDataContainer)
                osg::UserDataContainer* udc = theGeom->getUserDataContainer();
                if (udc)
                {
                    // 方式1:遍历查找
                    for (unsigned int i = 0; i < udc->getNumUserObjects(); ++i)
                    {
                        osg::UIntArray* ids = dynamic_cast<osg::UIntArray*>(udc->getUserObject(i));
                        if (ids && localIndex < ids->size())
                        {
                            globalID = (*ids)[localIndex];
                            bFind = true;
                            break;
                        }
                    }
                }
                if (bFind)
                {
                    std::cout << "拾取点在点云" << theName << "中,局部索引:" << localIndex <<",全局索引" << globalID << std::endl;
                    break;

                }

运行结果
在这里插入图片描述

代码如下:

STLReader.h
#pragma once

#include <osg/Notify>
#include <osg/Endian>
#include <osg/Types>

#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>

#include <osg/Geode>
#include <osg/Geometry>

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <string.h>
#include
#include <osgViewer/Viewer>

class ReaderObject
{
public:
ReaderObject();
virtual ~ReaderObject();

enum ReadResult
{
    ReadSuccess,
    ReadError,
    ReadEOF
};

virtual ReadResult read(FILE* fp) = 0;
osg::ref_ptr<osg::Vec3Array> getVertexArray();

protected:

osg::ref_ptr<osg::Vec3Array> _vertex;
osg::ref_ptr<osg::Vec3Array> _normal;
osg::ref_ptr<osg::Vec4Array> _color;

void clear()
{
    _vertex = osg::ref_ptr<osg::Vec3Array>();
    _normal = osg::ref_ptr<osg::Vec3Array>();
    _color = osg::ref_ptr<osg::Vec4Array>();
}

};

class AsciiReaderObject : public ReaderObject
{
public:
AsciiReaderObject()
: ReaderObject()
{
}

ReadResult read(FILE* fp);

};

STLReader.cpp
#include “STLReader.h”

osg::ref_ptrosg::Vec3Array ReaderObject::getVertexArray()
{
return _vertex;
}
ReaderObject::ReaderObject()
{
this->clear();

}
ReaderObject::~ReaderObject()
{
this->clear();
}

ReaderObject::ReadResult AsciiReaderObject::read(FILE* fp)
{
unsigned int vertexCount = 0;
unsigned int facetIndex[] = { 0, 0, 0 };
unsigned int vertexIndex = 0;
unsigned int normalIndex = 0;

const int MaxLineSize = 256;
char buf[MaxLineSize];
char sx[MaxLineSize], sy[MaxLineSize], sz[MaxLineSize];

// clear();

while (fgets(buf, sizeof(buf), fp))
{
    // strip '\n' or '\r\n' and trailing whitespace
    unsigned int len = strlen(buf) - 1;

    while (len && (buf[len] == '\n' || buf[len] == '\r' || isspace(buf[len])))
    {
        buf[len--] = '\0';
    }

    if (len == 0 || buf[0] == '\0')
    {
        continue;
    }

    // strip leading whitespace
    char* bp = buf;
    while (isspace(*bp))
    {
        ++bp;
    }

    if (strncmp(bp, "vertex", 6) == 0)
    {
        if (sscanf(bp + 6, "%s %s %s", sx, sy, sz) == 3)
        {
            if (!_vertex.valid())
                _vertex = new osg::Vec3Array;

            float vx = osg::asciiToFloat(sx);
            float vy = osg::asciiToFloat(sy);
            float vz = osg::asciiToFloat(sz);

            vertexIndex = _vertex->size();
            if (vertexCount < 3)
            {
                _vertex->push_back(osg::Vec3(vx, vy, vz));
                facetIndex[vertexCount++] = vertexIndex;
            }
        }
    }
    else if (strncmp(bp, "facet", 5) == 0)
    {
        if (sscanf(bp + 5, "%*s %s %s %s", sx, sy, sz) == 3)
        {
            float nx = osg::asciiToFloat(sx);
            float ny = osg::asciiToFloat(sy);
            float nz = osg::asciiToFloat(sz);

            if (!_normal.valid())
                _normal = new osg::Vec3Array;

            osg::Vec3 normal(nx, ny, nz);
            normal.normalize();

            normalIndex = _normal->size();
            _normal->push_back(normal);

            vertexCount = 0;
        }
    }
    else if (strncmp(bp, "solid", 5) == 0)
    {
        OSG_INFO << "STL loader parsing '" << bp + 6 << "'" << std::endl;
    }
    else if (strncmp(bp, "endsolid", 8) == 0)
    {
        return ReadSuccess;
    }
}

return ReadEOF;

}

OctreeBuilder.h
#pragma once
#include <osg/PagedLod>
#include <osg/Geometry>

class OctreeBuilder
{
struct ElementInfo
{
//坐标
osg::Vec3 pos;
//全局变量
uint32_t globalID;
};
protected:
osg::PagedLOD* createNewLevel(int level, const osg::Vec3& center, float radius);
osg::ref_ptrosg::Geometry createElement(std::vectorOctreeBuilder::ElementInfo childData);
osg::Geode* createBoxForDebug(const osg::Vec3& maxValue, const osg::Vec3& minValue);

int _maxChildNumber;
int _maxTreeDepth;
int _maxLevel;
std::string _strRootDir;
osg::ref_ptr<osg::Vec3Array> _posArray = nullptr;
//简模
osg::ref_ptr<osg::Group> _simpleModel = NULL;
//当前块ID
int _currentBlockID = 0;

public:
OctreeBuilder(std::string strRootDir,osg::ref_ptrosg::Vec3Array posArray)
{
_maxChildNumber = 50000;
_maxTreeDepth = 32;
_maxLevel = 0;
_strRootDir = strRootDir;
_posArray = posArray;
}
void run();
int getMaxLevel() const
{
return _maxLevel;
}
void setMaxChildNumber(int maxValue)
{
_maxChildNumber = maxValue;
}
int getMaxChildNumber()
{
return _maxChildNumber;
}
void setMaxTreeDepth(int maxValue)
{
_maxTreeDepth = maxValue;
}
int getMaxTreeDepth() const
{
return _maxTreeDepth;
}
osg::Group* build( int depth, const osg::BoundingBox& total, std::vectorOctreeBuilder::ElementInfo elementInfoVector);
//建立简模
osg::ref_ptrosg::Group buildSimpleModel();
};

OctreeBuilder.cpp

#include “OctreeBuilder.h”
#include
#include <sys/stat.h>
#include <io.h>
#include <direct.h>
#include <osg/Point>
#include <osg/Geode>
#include <osg/ShapeDrawable>
#include <osgDB/WriteFile>
osg::PagedLOD* OctreeBuilder::createNewLevel(int level, const osg::Vec3& center, float radius)
{
osg::ref_ptrosg::PagedLOD lod = new osg::PagedLOD;
lod->setCenter(center);
lod->setRadius(radius);
if (level == 0)
{
lod->setRange(0, 0.0f, 3000);
lod->setRange(1, 3000, FLT_MAX);

}
else
{
	lod->setRange(0, 0.0f, radius * 50.0);

}
//lod->setRange(0, radius * 50.0f, FLT_MAX);
//lod->setRange(1, 0.0f, radius * 50.0f);
//lod->setRange(0, 0.0f, radius * 1000.0f);
//lod->setRange(0, 0.0f, radius * 500.0f);
//lod->setRange(0, 0.0f, radius * 200.0f);
// lod->setRange(0, 0.0f, radius * 50.0f);
//lod->setRange(0, 0.0f, FLT_MAX);
if (_maxLevel < level)
{
	_maxLevel = level;
}
return lod.release();

}

osg::ref_ptrosg::Geometry OctreeBuilder::createElement(std::vectorOctreeBuilder::ElementInfo childData )
{
osg::ref_ptrosg::Geometry geometry = new osg::Geometry();

// 1. 设置顶点数组(点的位置)
osg::ref_ptr<osg::Vec3Array> posArray = new osg::Vec3Array;

// ID - 存到UserDataContainer(完全不影响渲染)
osg::ref_ptr<osg::UIntArray> ids = new osg::UIntArray();
for (int i = 0; i < childData.size(); i++)
{
	ElementInfo info = childData[i];
	posArray->push_back(info.pos);
	ids->push_back(info.globalID);
}
geometry->setVertexArray(posArray);

// 3. 设置点的绘制方式
geometry->addPrimitiveSet(new osg::DrawArrays(
	osg::PrimitiveSet::POINTS,  // 绘制类型为点
	0,                          // 起始索引
	childData.size()							// 点的数量
));
// ID存到UserDataContainer(不影响渲染!)
osg::ref_ptr<osg::UserDataContainer> udc = new osg::DefaultUserDataContainer();
udc->addUserObject(ids);  // 不加名字也行
// 或者加个名字,方便查找
// udc->setUserObject(ids, "GlobalIDs");
geometry->setUserDataContainer(udc);

// 4. 设置点的大小
//osg::ref_ptr<osg::Point> point = new osg::Point();
//point->setSize(100.0f); // 设置点大小
//geometry->getOrCreateStateSet()->setAttribute(point.get());


// 启用光照(可选)
//geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
return geometry;

}

osg::Geode* OctreeBuilder::createBoxForDebug(const osg::Vec3& maxValue, const osg::Vec3& minValue)
{
osg::ref_ptrosg::Geode geode = new osg::Geode;
osg::Vec3 center = (maxValue + minValue) / 2.0;
float width = (maxValue - minValue).length() * 0.5;
//float width = 100;
geode->addDrawable(new osg::ShapeDrawable(new osg::Box(center, width)));
return geode.release();
}

void OctreeBuilder::run()
{
_currentBlockID = 0;
osg::BoundingBox globalBound;
std::vectorOctreeBuilder::ElementInfo globalElements;
for (int i = 0; i < _posArray->size(); i++)
{
osg::Vec3 pos = _posArray->at(i);
globalBound.expandBy(pos);
ElementInfo info;
info.pos = pos;
info.globalID = i;
globalElements.push_back(info);
}

osg::ref_ptr<osg::Group> rootPlot = this->build(0, globalBound, globalElements);
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(rootPlot);
std::string strRootOutputFileName = _strRootDir + "root.osgb";
osgDB::writeNodeFile(*root, strRootOutputFileName);

}

osg::Group* OctreeBuilder::build(int depth, const osg::BoundingBox& total, std::vectorOctreeBuilder::ElementInfo elementInfoVector)
{
int s[3];
osg::Vec3 extentSet[3] =
{
total._min,
(total._max + total._min) * 0.5f,
total._max
};
std::vectorOctreeBuilder::ElementInfo childData;
childData.clear();
for (unsigned int i = 0; i < elementInfoVector.size(); i++)
{
ElementInfo info = elementInfoVector[i];
if (total.contains(info.pos) )
{
childData.push_back(info);
}
}
//如果数据为空,则返回一个空的
if (childData.size() == 0)
{
return NULL;
}

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];
	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]++)
				{
					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] )
		{
			group->addChild(childNodes[i]);
		}
	}
}
else
{
	osg::ref_ptr<osg::Geometry> theGeom = createElement(childData);
	group->addChild(theGeom);
	//给当前geom设置名字
	std::string strBlockID = "block" + std::to_string(_currentBlockID);
	theGeom->setName(strBlockID);
	//更新当前块ID和全局开始索引ID,供下一个块处理
	_currentBlockID++;
}
//如果group为空,则返回
if (group->getNumChildren() == 0)
{
	return NULL;
}
//如果没有文件夹,则建立
std::string strDir = _strRootDir + std::to_string(depth);
if (access(strDir.c_str(),0) == -1)
{
	mkdir(strDir.c_str());
}
osg::Vec3 center = (total._max + total._min) * 0.5;
float radius = (total._max - total._min).length() * 0.5f;
std::string strName = std::to_string(depth) + "/"
	"x_" + std::to_string(center.x()) +
	"_y_" + std::to_string(center.y()) +
	"_z_" + std::to_string(center.z()) + ".osgb";
std::string strOutputFile = _strRootDir + strName;
osgDB::writeNodeFile(*group, strOutputFile);


osg::PagedLOD* level = createNewLevel(depth, center, radius);
//level->insertChild(0, createBoxForDebug(total._max, total._min)); //在145行对应	if (childNodes[i] && childNodes[i]->getNumChildren())
//level->insertChild(1, group.get());
//level->insertChild(0, group.get());

std::string strLevelGroupFileName = "../" + std::to_string(depth) + "/"
	"x_" + std::to_string(center.x()) +
	"_y_" + std::to_string(center.y()) +
	"_z_" + std::to_string(center.z()) + ".osgb";
if (depth==0)
{
	strLevelGroupFileName =  std::to_string(depth) + "/"
		"x_" + std::to_string(center.x()) +
		"_y_" + std::to_string(center.y()) +
		"_z_" + std::to_string(center.z()) + ".osgb";
}
//level->setFileName(1, strLevelGroupFileName);
level->setFileName(0, strLevelGroupFileName);

if (depth == 0)
{
	//简模
	osg::ref_ptr<osg::Group> simpleGrp = this->buildSimpleModel();
	std::string strSimpleNameWithDir = _strRootDir + std::to_string(depth) + "/simpleModel.osgb";
	osgDB::writeNodeFile(*simpleGrp, strSimpleNameWithDir);
	std::string strSimpleLevelName = std::to_string(depth) + "/simpleModel.osgb";
	level->setFileName(1, strSimpleLevelName);
	
}

return level;

}

osg::ref_ptrosg::Group OctreeBuilder::buildSimpleModel()
{
osg::ref_ptrosg::Group simpleModelGrp = new osg::Group;
int theSize = _posArray->size();
int simpleSize = _maxChildNumber;
int intervalNumber = theSize / simpleSize;
std::vectorOctreeBuilder::ElementInfo simplePosArray ;
for ( int i = 0; i < theSize; i++)
{
if (i % intervalNumber != 0)
{
continue;
}
ElementInfo info;
info.pos = _posArray->at(i);
info.globalID = i;
simplePosArray.push_back(info);
}
simpleModelGrp->addChild(createElement(simplePosArray));
return simpleModelGrp;
}

PointPickerHandler.h

#pragma once
#include <osgUtil/PolytopeIntersector>
#include <osgUtil/IntersectionVisitor>
#include <osgViewer/Viewer>
#include <osgGA/GUIEventHandler>

class PointPickHandler : public osgGA::GUIEventHandler
{
public:
PointPickHandler(osgViewer::Viewer* viewer) : _viewer(viewer)
{
}

virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&) 
{
    if (ea.getEventType() == osgGA::GUIEventAdapter::PUSH) 
    {
        int x = ea.getX();
        int y = ea.getY();

        // 1. 获取窗口尺寸,并转换Y轴坐标(OSG窗口原点在左下角)
        const osg::GraphicsContext::Traits* traits =
            _viewer->getCamera()->getGraphicsContext()->getTraits();
        if (!traits) return false;
        y = traits->height - y;

        // 2. 定义拾取区域的大小(例如,一个5x5像素的窗口)
        float pickSize = 5.0f;
        double xMin = x - pickSize;
        double yMin = y - pickSize;
        double xMax = x + pickSize;
        double yMax = y + pickSize;

        // 3. 创建PolytopeIntersector,指定坐标系为WINDOW,并定义矩形区域
        osg::ref_ptr<osgUtil::PolytopeIntersector> intersector =
            new osgUtil::PolytopeIntersector(osgUtil::Intersector::WINDOW, xMin, yMin, xMax, yMax);

        // 4. 关键步骤:设置维度掩码,使其只检测点(DimZero)
        //    如果还想检测线和面,可以使用位或操作,例如:DimZero | DimOne | DimTwo
        intersector->setDimensionMask(osgUtil::PolytopeIntersector::DimZero);

        // 5. 启动相交测试
        osgUtil::IntersectionVisitor iv(intersector);
        _viewer->getCamera()->accept(iv);

        // 6. 处理结果:检查是否拾取到点
        if (!intersector->containsIntersections())
        {
            return false;
        }
        {
            bool bFind = false;
            const osgUtil::PolytopeIntersector::Intersections& intersections = intersector->getIntersections();
          
            for (osgUtil::PolytopeIntersector::Intersections::iterator it =
                intersections.begin(); it != intersections.end(); ++it) 
            {
                osg::Geometry* theGeom = dynamic_cast<osg::Geometry*> (it->drawable.get());
                if (theGeom == nullptr)
                {
                    continue;
                }
                std::string theName = theGeom->getName();
                size_t pos = theName.find("block");
                if (pos == std::string::npos) 
                {
                    continue;
                }
                int localIndex = it->primitiveIndex;
                int globalID = -1;
                // 3. 获取全局ID(从UserDataContainer)
                osg::UserDataContainer* udc = theGeom->getUserDataContainer();
                if (udc)
                {
                    // 方式1:遍历查找
                    for (unsigned int i = 0; i < udc->getNumUserObjects(); ++i)
                    {
                        osg::UIntArray* ids = dynamic_cast<osg::UIntArray*>(udc->getUserObject(i));
                        if (ids && localIndex < ids->size())
                        {
                            globalID = (*ids)[localIndex];
                            bFind = true;
                            break;
                        }
                    }
                }
                if (bFind)
                {
                    std::cout << "拾取点在点云" << theName << "中,局部索引:" << localIndex <<",全局索引" << globalID << std::endl;
                    break;

                }

                // 对于点图元,localIntersectionPoint即为被拾取点的坐标
                // 这个坐标是局部坐标,如果需要世界坐标,可以结合it->matrix进行变换
                //osg::Vec3 localPoint = it->localIntersectionPoint;
                //OSG_NOTICE << "Picked a point at: " << localPoint.x() << ", "
                 //   << localPoint.y() << ", " << localPoint.z() << std::endl;

                // 可以进一步获取更多信息,如所在的Drawable、Primitive索引等
                // unsigned int primitiveIndex = it->primitiveIndex;
                // osg::Drawable* drawable = it->drawable.get();
            }
        }
        return true;
    }
    return false;
}

private:
osgViewer::Viewer* _viewer;
//<点云key,全局开始索引ID>集合
std::map<std::string, int> _data;
};

调用Main.cpp

#include “STLReader.h”
#include “OctreeBuilder.h”
#include <osgGA/TrackballManipulator>
#include “PointPickerHandler.h”

int main()
{
std::string strRootDir = “d:/test/stllod/50000/”;
#if 0
std::string fileName = “d:/test/dianyun.stl”;

FILE* fp = osgDB::fopen(fileName.c_str(), "r");
osg::ref_ptr<osg::Group> group = new osg::Group;

// read
rewind(fp);

ReaderObject* readerObject = new AsciiReaderObject();

std::auto_ptr<ReaderObject> readerPtr(readerObject);

while (1)
{
    ReaderObject::ReadResult result;

    if ((result = readerPtr->read(fp)) == ReaderObject::ReadError)
    {
        fclose(fp);
        return -1;
    }

    if (result == ReaderObject::ReadEOF)
        break;
}

fclose(fp);
osg::ref_ptr<osg::Vec3Array> posArray = readerPtr->getVertexArray();
OctreeBuilder octree(strRootDir, posArray);
octree.run();
return 0;

#else
std::string strRootOutputFileName = strRootDir + “root.osgb”;
//std::string strRootOutputFileName = strRootDir + “0/simpleModel.osg”;
osg::ref_ptrosgViewer::Viewer viewer = new osgViewer::Viewer;
//unsigned int currentDefault = viewer->getDatabasePager()->getTargetMaximumNumberOfPageLOD();
//bool bcompiled =viewer->getDatabasePager()->getDoPreCompile();
osg::ref_ptrosg::Node root = osgDB::readNodeFile(strRootOutputFileName);

root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
osg::ref_ptr<PointPickHandler> pickerHandler = new PointPickHandler(viewer);
viewer->addEventHandler(pickerHandler);
viewer->setSceneData(root);
return viewer->run();

#endif

}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐