2021SC@SDUSC
目录
预备知识:CGAL库
(一)Kernel内核
kernel代表代表程序如何去对待精度问题
在计算几何时,精度是一个重要的问题,如果内核选择不正确,往往会造成意料之外的结果。
Exact_predicates_inexact_constructions_kernel:精确谓词,且精确构造
(二)CgalMesh
(三)半边网格数据结构
每个边分为两个半边,每个半边都是一个有向边,方向相反。
如果一个边被两个面片公用,则每个面片都能各自拥有一个半边。如果一个边仅被一个面片占用(边界边),则这个面片仅拥有该边的其中一个半边,另一个半边为闲置状态。
每一条半边仅存储它的起点指针。
更多有关半边结构的部分参考:半边数据结构&网格细分 - 简书
一、类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<QVector3D> &vertices, const std::vector<std::vector<size_t>> &faces, bool disableSelfIntersects=false);
Mesh(const Mesh &other);
~Mesh();
void fetch(std::vector<QVector3D> &vertices, std::vector<std::vector<size_t>> &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<std::pair<Source, size_t>> *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<QVector3D> &vertices, const std::vector<std::vector<size_t>> &faces, bool disableSelfIntersects)
{
CgalMesh *cgalMesh = nullptr;
if (!faces.empty()) {
cgalMesh = buildCgalMesh<CgalKernel>(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<QVector3D> fetchedVertices;
std::vector<std::vector<size_t>> fetchedFaces;
fetchFromCgalMesh<CgalKernel>(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<QVector3D> &vertices, std::vector<std::vector<size_t>> &faces) const
{
CgalMesh *exactMesh = (CgalMesh *)m_privateData;
if (nullptr == exactMesh)
return;
fetchFromCgalMesh<CgalKernel>(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<CgalKernel>(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<std::pair<Source, size_t>> *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<PositionKey, std::pair<Source, size_t>> 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<QVector3D> vertices;
std::vector<std::vector<size_t>> faces;
fetchFromCgalMesh<CgalKernel>(resultCgalMesh, vertices, faces);
mesh->m_isCombinable = isManifold(faces);
}
mesh->validate();
return mesh;
}