感谢
感谢网友热心提出这个问题,大家有问题也可以在评论区提出。
资源下载
本文集包括本节所有资源包括模型代码都在此下载,按节的序号有文件或文件夹:
注意:务必使用浏览器打开:
链接:https://pan.baidu.com/s/13gwJLwo_LbRnN3Bl2NXXXw
提取码:xrf5
功能
1.我们先画一个盒子,长宽高是10,8,7,再随便旋转个30度,盒子的体积是560。这样我们就好看看我们自己的方式算的准不准。
2.点击w键,会采样变密,点击s键会采样变粗,命令行中打印出采样的间隔以及算出的体积和盒子本身的体积560的对比来看看误差。
具体实现
思想:我寻思填方是把一个坑填平喽,挖方是再把填的挖出来,因此应该是一个算法。我们找到要填的几何体,这里是一个盒子然后算出包围球,这样就求出来全覆盖这个包围球的xyz,然后以xy为平面,固定间隔连一条直线,然后与盒子求交,求出来的交点以采样间隔为长宽,以交点间的距离为高,然后算出来体积,这么多体积相加,就是总体积。有点微积分的味道。
实现:具体实现也没有什么好说的,直线和物体求交也就是个函数的API,没有什么好讲的。看代码吧。
结果:有一点是肯定的,当采样越密的时候,精度就越高,这个是可以显见收敛的。有些人可能说我采样往粗了变,怎么有时候误差大有时候误差小呢?这就是采样过粗是不行的。因此以采样点密为要。如果要非常精确,不考虑实时性,可以增加采样点,肯定会越来越精确。
扩展思考:如果有些几何体特别复杂,比如你溶洞似的,那么怎么办呢?这就牵扯到一条线可能有多个交点的问题,一般情况下交点必为偶数个则才成为一个封闭的体,则两两认为是在一个空间,是要求的。自己想象一下。
具体代码如下:
#include <osgViewer/viewer>
#include <osgDB/ReadFile>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/ShapeDrawable>
#include <osg/MatrixTransform>
#include <osgUtil/IntersectionVisitor>
//定义全局变量用着方便
osg::Group* _root = nullptr;
osg::Node* _box = nullptr;
osg::Group* _shiyi = nullptr;
osg::Geode* CreateBox(osg::Vec3 from, osg::Vec3 to, float stepSize)
{
osg::Geode* gnode = new osg::Geode;
gnode->addDrawable(new osg::ShapeDrawable(new osg::Box((from+to)/2, stepSize, stepSize, std::abs(to.z()-from.z()))));
return gnode;
}
//画一个盒子
osg::Node* CreateScene()
{
osg::Group* root = new osg::Group;
_root = root;
osg::MatrixTransform* mt = new osg::MatrixTransform;
_box = mt;
root->addChild(mt);
mt->setMatrix(osg::Matrix::rotate(osg::inDegrees(30.0), osg::Vec3(1.0, 1.0, 1.0)));
osg::Geode* gnode = new osg::Geode;
osg::ShapeDrawable* sd = new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0, 0.0, 0.0), 10, 8, 7));
gnode->addDrawable(sd);
mt->addChild(gnode);
return root;
}
class MyEventHandler : public osgGA::GUIEventHandler
{
public:
MyEventHandler()
{
_jiange = 0.5;
}
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
if (ea.getEventType() == ea.KEYDOWN)
{
bool _push = false;
if ((ea.getKey() == 'w') || (ea.getKey() == 'W'))
{
_push = true;
_jiange /= 1.5;
}
if ((ea.getKey() == 's') || (ea.getKey() == 'S'))
{
_push = true;
_jiange *= 1.5;
}
if (_push)
{
//清空上一次采样结果
if (_shiyi != nullptr)
{
_root->removeChild(_shiyi);
_shiyi = nullptr;
}
_shiyi = new osg::Group();
_root->addChild(_shiyi);
std::cout << "采样间隔:"<<_jiange << std::endl;
//求要求的场景的boudingbox
osg::BoundingSphere bs = _box->getBound();
//求出xyz的最小值和最大值,然后根据间隔来求
osg::Vec3 center = bs.center();
float r = bs.radius();
float areas = 0.0;
for (float fromx = center.x() - r; fromx <= center.x() + r; fromx += _jiange)
{
for (float fromy = center.y() - r; fromy <= center.y() + r; fromy += _jiange)
{
osg::Vec3 from = osg::Vec3(fromx, fromy, center.z() - r);
osg::Vec3 to = osg::Vec3(fromx, fromy, center.z() + r);
osgUtil::LineSegmentIntersector::Intersections intersections;
osg::ref_ptr<osgUtil::LineSegmentIntersector> ls = new osgUtil::LineSegmentIntersector(from, to);
osg::ref_ptr<osgUtil::IntersectionVisitor> iv = new osgUtil::IntersectionVisitor(ls);
_box->accept(*iv.get());
if (ls->containsIntersections())
{
intersections = ls->getIntersections();
//判断当前交点数
if (intersections.size() == 2)
{
osgUtil::LineSegmentIntersector::Intersections::iterator iter = intersections.begin();
osg::Vec3 firstInter = iter->getWorldIntersectPoint();
iter++;
osg::Vec3 secondInter = iter->getWorldIntersectPoint();
_shiyi->addChild(CreateBox(firstInter, secondInter, _jiange));
areas += (_jiange*_jiange*std::abs(firstInter.z() - secondInter.z()));
}
}
}
}
std::cout << "实际面积:560 计算面积:" << areas << std::endl;
}
}
return false;
}
float _jiange;
};
int main()
{
osgViewer::Viewer viewer;
viewer.addEventHandler(new MyEventHandler());
viewer.setSceneData(CreateScene());
return viewer.run();
}