2021SC@SDUSC
目录
一、网格切割
网格切割是建模软件中重要的一部分,模型中网格的数量和细分程度往往能直接影响到建模的质量,在Dust3d中可以通过将两个节点之间的连接段删除来进行网格的切割,如:
目前的网格切割技术主要有迭代聚类法、区域生长算法、层次分割算法等。其中,我认为Dust3d所使用的分割是基于区域生长算法实现的。
1.迭代聚类法
迭代聚类法使用的是先将每个三角网格当成一个聚类,利用网格面片之间的拓扑关系以及几何属性,计算不同距离的聚类之间合并的成本,然后迭代地合并相邻的聚类,合并后重新计算聚类间的合并成本,不断迭代直到分成两类
2.区域生长算法
区域生长算法是取一个三角网格作为一个种子,然后将所有和这个三角网格满足一定关系的邻域点都加入到种子中,不断迭代以上过程直到稳定
3.层次分割算法
层次分割算法首先利用聚类进行一次粗分割,在粗分割的基础上,用区域增长的思想,按带状推进读取模型,在高斯曲率比较小的地方对模型再进行一次分割。
二、MeshSplitterTriangle类
(一)概述
用来进行分割的三角网格片类,indices是网格片三个顶点对应的索引
class MeshSplitterTriangle
{
public:
int indices[3] = {0, 0, 0};
bool operator<(const MeshSplitterTriangle &other) const
{
return std::make_tuple(indices[0], indices[1], indices[2]) <
std::make_tuple(other.indices[0], other.indices[1], other.indices[2]);
}
};
(二)具体函数
1.重载'<'操作符函数
bool operator<(const MeshSplitterTriangle &other) const
{
return std::make_tuple(indices[0], indices[1], indices[2]) <
std::make_tuple(other.indices[0], other.indices[1], other.indices[2]);
}
三、MeshSplitter类
(一)概述
类中仅含有一个公有函数split,用于分割input(需要进行分割的三角面片)到firstGroup与secondGroup两组中
class MeshSplitter
{
public:
static bool split(const std::set<MeshSplitterTriangle> &input,
const std::vector<std::pair<std::pair<size_t, size_t>, std::pair<size_t, size_t>>> &triangleLinks,
std::set<MeshSplitterTriangle> &splitter,
std::set<MeshSplitterTriangle> &firstGroup,
std::set<MeshSplitterTriangle> &secondGroup,
bool expandSplitter=false);
};
(二)split函数
1.制作边到三角形的贴图,该贴图将用于查找相邻的三角形
firstGroup.clear();
secondGroup.clear();
// 制作边到三角形的贴图,该贴图将用于查找相邻的三角形
std::map<std::pair<int, int>, MeshSplitterTriangle> edgeToTriangleMap;
for (const auto &triangle: input) {
for (int i = 0; i < 3; i++) {
int next = (i + 1) % 3;
edgeToTriangleMap[std::make_pair(triangle.indices[i], triangle.indices[next])] = triangle;
}
}
std::map<std::pair<int, int>, MeshSplitterTriangle> edgeToLinkedTriangleMap;
for (const auto &it: triangleLinks) {
auto firstEdge = std::make_pair((int)it.first.first, (int)it.first.second);
auto secondEdge = std::make_pair((int)it.second.first, (int)it.second.second);
//根据边找到对应的三角面贴图
auto findFirstTriangle = edgeToTriangleMap.find(firstEdge);
auto findSecondTriangle = edgeToTriangleMap.find(secondEdge);
if (findFirstTriangle == edgeToTriangleMap.end())
continue;
if (findSecondTriangle == edgeToTriangleMap.end())
continue;
//制作边对应三角形的贴图
edgeToLinkedTriangleMap[firstEdge] = findSecondTriangle->second;
edgeToLinkedTriangleMap[std::make_pair(firstEdge.second, firstEdge.first)] = findSecondTriangle->second;
edgeToLinkedTriangleMap[secondEdge] = findFirstTriangle->second;
edgeToLinkedTriangleMap[std::make_pair(secondEdge.second, secondEdge.first)] = findFirstTriangle->second;
}
2.扩展拆分器Splitter(在需要的情况下)
布尔分量expandSplitter决定是否需要进行扩展,向与拆分器相邻的面片方向进行扩展
// 如果需要的话,扩展拆分器Splitter
if (expandSplitter) {
std::vector<MeshSplitterTriangle> expandedTriangles;
for (const auto &triangle: splitter) {
for (int i = 0; i < 3; i++) {
int next = (i + 1) % 3;
auto oppositeEdge = std::make_pair(triangle.indices[next], triangle.indices[i]);
//先next,再i,与上面建立点对的顺序相同,oppositeTriangle是与拆分器直接相邻的
auto oppositeTriangle = edgeToTriangleMap.find(oppositeEdge);
if (oppositeTriangle != edgeToTriangleMap.end()) {
//拆分器中没有oppositeTriangle就进行扩展
if (splitter.find(oppositeTriangle->second) == splitter.end()) {
expandedTriangles.push_back(oppositeTriangle->second);
}
}
}
}
size_t addTriangles = 0;
for (const auto &triangle: expandedTriangles) {
auto insertResult = splitter.insert(triangle);
if (insertResult.second)
++addTriangles;
}
if (0 == addTriangles) {
qDebug() << "Expanded without new triangles added";
} else {
qDebug() << "Expanded with new triangles added:" << addTriangles;
}
}
3.找到与拆分器直接相邻的三角形
MeshSplitterTriangle startTriangle;
bool foundStartTriangle = false;
for (const auto &triangle: splitter) {
for (int i = 0; i < 3; i++) {
int next = (i + 1) % 3;
auto oppositeEdge = std::make_pair(triangle.indices[next], triangle.indices[i]);
auto oppositeTriangle = edgeToTriangleMap.find(oppositeEdge);
if (oppositeTriangle == edgeToTriangleMap.end()) {
qDebug() << "Find opposite edge failed:" << oppositeEdge.first << oppositeEdge.second;
continue;
}
if (splitter.find(oppositeTriangle->second) == splitter.end()) {
foundStartTriangle = true;
startTriangle = oppositeTriangle->second;
break;
}
}
}
if (!foundStartTriangle) {
qDebug() << "Find start triangle for splitter failed";
return false;
}
4.进行分组
递归地将与第一个找到的三角形相邻的所有三角面片放到同一个组中,剩余的三角形面片放到第二个组中
// 递归地将第一个找到的三角形的所有邻居连接到第一个组
std::set<MeshSplitterTriangle> processedTriangles;
for (const auto &triangle: splitter) {
processedTriangles.insert(triangle);
}
std::queue<MeshSplitterTriangle> waitQueue;
waitQueue.push(startTriangle);
while (!waitQueue.empty()) {
MeshSplitterTriangle triangle = waitQueue.front();
waitQueue.pop();
firstGroup.insert(triangle);
if (!processedTriangles.insert(triangle).second)
continue;
for (int i = 0; i < 3; i++) {
int next = (i + 1) % 3;
auto oppositeEdge = std::make_pair(triangle.indices[next], triangle.indices[i]);
auto oppositeTriangle = edgeToTriangleMap.find(oppositeEdge);
if (oppositeTriangle != edgeToTriangleMap.end()) {
if (processedTriangles.find(oppositeTriangle->second) == processedTriangles.end()) {
waitQueue.push(oppositeTriangle->second);
}
}
//相邻的三角形
auto linkedTriangle = edgeToLinkedTriangleMap.find(oppositeEdge);
if (linkedTriangle != edgeToLinkedTriangleMap.end()) {
if (processedTriangles.find(linkedTriangle->second) == processedTriangles.end()) {
waitQueue.push(linkedTriangle->second);
}
}
}
}
//将剩下的都放入第二个组
for (const auto &triangle: input) {
if (processedTriangles.find(triangle) != processedTriangles.end())
continue;
secondGroup.insert(triangle);
}
5.检查分割是否失败
如果两个组中存在一个组中没有任何面片的情况,那么就证明分组失败
if (firstGroup.empty() || secondGroup.empty()) {
qDebug() << "At lease one group is empty";
return false;
}
return true;