从源头看Dust3d | (七)meshcombiner:CGAL网格聚合

2021SC@SDUSC 

目录

预备知识:CGAL库

(一)Kernel内核

(二)CgalMesh

(三)半边网格数据结构

一、类MeshCombiner

二、具体函数

主要通过combine函数实现网格的半边结构黏合

(一)Mesh类

(二)combine函数:实现网格聚合


预备知识:CGAL库

(一)Kernel内核

kernel代表代表程序如何去对待精度问题

在计算几何时,精度是一个重要的问题,如果内核选择不正确,往往会造成意料之外的结果。

Exact_predicates_inexact_constructions_kernel:精确谓词,且精确构造

(二)CgalMesh

在之前的博客中曾有过介绍:从源头看Dust3d | (三)Booleanmesh&Boundingboxmesh_苏打不是糖的博客-CSDN博客icon-default.png?t=LA92https://blog.csdn.net/weixin_46273149/article/details/120817558?spm=1001.2014.3001.5501

(三)半边网格数据结构

每个边分为两个半边,每个半边都是一个有向边,方向相反。

如果一个边被两个面片公用,则每个面片都能各自拥有一个半边。如果一个边仅被一个面片占用(边界边),则这个面片仅拥有该边的其中一个半边,另一个半边为闲置状态。

每一条半边仅存储它的起点指针。

从源头看Dust3d | (七)meshcombiner:CGAL网格聚合_第1张图片

 更多有关半边结构的部分参考:半边数据结构&网格细分 - 简书

一、类MeshCombiner

内含枚举类型Method、Source;Mesh类;函数combine

class MeshCombiner
{
public:
    enum class Method
    {
        Union,
        Diff
    };
    
    enum class Source
    {
        None,
        First,
        Second
    };

    class Mesh
    {
    public:
        Mesh() = default;
        Mesh(const std::vector &vertices, const std::vector> &faces, bool disableSelfIntersects=false);
        Mesh(const Mesh &other);
        ~Mesh();
        void fetch(std::vector &vertices, std::vector> &faces) const;
        bool isNull() const;
        bool isCombinable() const;
        
        friend MeshCombiner;
        
    private:
        void *m_privateData = nullptr;
        bool m_isCombinable = false;
        
        void validate();
    };
    
    static Mesh *combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method,
        std::vector> *combinedVerticesComeFrom=nullptr);
};

二、具体函数

主要通过combine函数实现网格的半边结构黏合

(一)Mesh类

1.构造函数

    (1)

使用到的函数 来源 函数功能

buildCgalMesh

booleanmesh.cpp

建立CGAL网格结构

is_valid_polygon_mesh

CGAL库 判断是否为合格的多边形网络

triangulate_faces

CGAL库 判断是否为三角形网格

does_self_intersect

CGAL库 判断网格中是否存在自相交

fetchFromCgalMesh

booleanmesh.cpp 从网格结构中抓取顶点和面信息

isManifold

util.cpp

判断是否为流形网络

MeshCombiner::Mesh::Mesh(const std::vector &vertices, const std::vector> &faces, bool disableSelfIntersects)
{
    CgalMesh *cgalMesh = nullptr;
    if (!faces.empty()) {
        cgalMesh = buildCgalMesh(vertices, faces);
        //booleanmesh.cpp中的buildCgalMesh函数
        if (!disableSelfIntersects) {
            //是否是合格的多边形网格
            if (!CGAL::is_valid_polygon_mesh(*cgalMesh)) {
                qDebug() << "Mesh is not valid polygon";
                delete cgalMesh;
                cgalMesh = nullptr;
            } else {
                if (CGAL::Polygon_mesh_processing::triangulate_faces(*cgalMesh)) {
                    if (CGAL::Polygon_mesh_processing::does_self_intersect(*cgalMesh)) {
                        qDebug() << "Mesh does_self_intersect";//网格中存在自相交,模型有自相交则无法生成体网格
                        delete cgalMesh;
                        cgalMesh = nullptr;
                    } else {
                        //没有自相交时
						std::vector fetchedVertices;
						std::vector> fetchedFaces;
						fetchFromCgalMesh(cgalMesh, fetchedVertices, fetchedFaces);
                        //booleanmesh中的函数
                        //判断是否为流形网络,半边数据结构仅支持流形网络
						if (!isManifold(fetchedFaces)) {
							qDebug() << "Mesh does not self intersect but is not manifold";
							delete cgalMesh;
							cgalMesh = nullptr;
						} else {
                            //是流行网络
							m_isCombinable = true;
						}
                    }
                } else {
                    //如果不是三角形网格,就删除
                    qDebug() << "Mesh triangulate failed";
                    delete cgalMesh;
                    cgalMesh = nullptr;
                }
            }
        }
    }
    m_privateData = cgalMesh;
    validate();
}

    (2)

MeshCombiner::Mesh::Mesh(const Mesh &other)
{
    if (other.m_privateData) {
		m_isCombinable = other.m_isCombinable;
        m_privateData = new CgalMesh(*(CgalMesh *)other.m_privateData);
        validate();
    }
}

 2.析构函数

MeshCombiner::Mesh::~Mesh()
{
    CgalMesh *cgalMesh = (CgalMesh *)m_privateData;
    delete cgalMesh;
}

3.fetch函数

调用booleanmesh.cpp中的fetchFromCgalMesh函数抓取网格中的顶点信息与面信息

void MeshCombiner::Mesh::fetch(std::vector &vertices, std::vector> &faces) const
{
    CgalMesh *exactMesh = (CgalMesh *)m_privateData;
    if (nullptr == exactMesh)
        return;
    
    fetchFromCgalMesh(exactMesh, vertices, faces);
}

4.返回信息的函数

判断m_privateData是否为空(是否存在CGAL网格)

判断m_isCombinable为true还是false(能否黏合)

bool MeshCombiner::Mesh::isNull() const
{
    return nullptr == m_privateData;
}

bool MeshCombiner::Mesh::isCombinable() const
{
    return m_isCombinable;
}

3.validate函数

确保空网格的信息正确

void MeshCombiner::Mesh::validate()
{
    if (nullptr == m_privateData)
        return;
    
    CgalMesh *exactMesh = (CgalMesh *)m_privateData;
    if (isNullCgalMesh(exactMesh)) {
        delete exactMesh;
        m_privateData = nullptr;
        m_isCombinable = false;
    }
}

(二)combine函数:实现网络聚合

combine函数主要使用了CGAL库中的corefine_and_compute_union与corefine_and_compute_difference函数,分别对两个输入的网格进行求和与求差操作,将结果更新到resultCgalMesh中

MeshCombiner::Mesh *MeshCombiner::combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method,
    std::vector> *combinedVerticesComeFrom)
{
	if (firstMesh.isNull() || !firstMesh.isCombinable() ||
			secondMesh.isNull() || !secondMesh.isCombinable())
		return nullptr;
	//确定firstMesh&secondMesh都可以进行黏合
    CgalMesh *resultCgalMesh = nullptr;
    CgalMesh *firstCgalMesh = (CgalMesh *)firstMesh.m_privateData;
    CgalMesh *secondCgalMesh = (CgalMesh *)secondMesh.m_privateData;
    std::map> verticesSourceMap;
    
    auto addToSourceMap = [&](CgalMesh *mesh, Source source) {
        size_t vertexIndex = 0;
        for (auto vertexIt = mesh->vertices_begin(); vertexIt != mesh->vertices_end(); vertexIt++) {
            auto point = mesh->point(*vertexIt);
            float x = (float)CGAL::to_double(point.x());
            float y = (float)CGAL::to_double(point.y());
            float z = (float)CGAL::to_double(point.z());
            auto insertResult = verticesSourceMap.insert({{x, y, z}, {source, vertexIndex}});
            //if (!insertResult.second) {
            //    qDebug() << "Position key conflict:" << QVector3D {x, y, z} << "with:" << insertResult.first->first.position();
            //}
            ++vertexIndex;
        }
    };
    if (nullptr != combinedVerticesComeFrom) {
        addToSourceMap(firstCgalMesh, Source::First);
        addToSourceMap(secondCgalMesh, Source::Second);
    }
    
    if (Method::Union == method) {
        resultCgalMesh = new CgalMesh;
        try {
            //算和
            if (!CGAL::Polygon_mesh_processing::corefine_and_compute_union(*firstCgalMesh, *secondCgalMesh, *resultCgalMesh)) {
                delete resultCgalMesh;
                resultCgalMesh = nullptr;
            }
        } catch (...) {
            delete resultCgalMesh;
            resultCgalMesh = nullptr;
        }
    } else if (Method::Diff == method) {
        resultCgalMesh = new CgalMesh;
        try {
            //算差
            if (!CGAL::Polygon_mesh_processing::corefine_and_compute_difference(*firstCgalMesh, *secondCgalMesh, *resultCgalMesh)) {
                delete resultCgalMesh;
                resultCgalMesh = nullptr;
            }
        } catch (...) {
            delete resultCgalMesh;
            resultCgalMesh = nullptr;
        }
    }

    if (nullptr != combinedVerticesComeFrom) {
        combinedVerticesComeFrom->clear();
        if (nullptr != resultCgalMesh) {
            for (auto vertexIt = resultCgalMesh->vertices_begin(); vertexIt != resultCgalMesh->vertices_end(); vertexIt++) {
                auto point = resultCgalMesh->point(*vertexIt);
                float x = (float)CGAL::to_double(point.x());
                float y = (float)CGAL::to_double(point.y());
                float z = (float)CGAL::to_double(point.z());
                auto findSource = verticesSourceMap.find(PositionKey(x, y, z));
                if (findSource == verticesSourceMap.end()) {
                    combinedVerticesComeFrom->push_back({Source::None, 0});
                    //结束点
                } else {
                    combinedVerticesComeFrom->push_back(findSource->second);
                }
            }
        }
    }
    
    if (nullptr == resultCgalMesh)
        return nullptr;
    
    Mesh *mesh = new Mesh;
    mesh->m_privateData = resultCgalMesh;
    {
        //获取信息
        std::vector vertices;
        std::vector> faces;
        fetchFromCgalMesh(resultCgalMesh, vertices, faces);
        mesh->m_isCombinable = isManifold(faces);
    }
    mesh->validate();
    return mesh;
}

你可能感兴趣的:(Dust3d学习,c++,html,1024程序员节)