2021SC@SDUSC
目录
MeshGenerator类预备知识
一、generate函数
1.涉及的函数
(1)preprocessMirror函数
3D物体的两种展现形式:点云形式(Point)与网格形式(Mesh)
点云包含顶点信息、颜色信息、法向量;网格形式包含顶点、颜色、法向量信息以及面片索引、贴图坐标。网格模型可以通过一半的网格来镜像生成另一半的网格实现快速高效生成网格结构的效果。
void MeshGenerator::preprocessMirror()
{
std::vector<std::map<QString, QString>> newParts;
std::map<QString, QString> partOldToNewMap;
for (auto &partIt: m_snapshot->parts) {
bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partIt.second, "xMirrored"));
if (!xMirrored)
continue;
std::map<QString, QString> mirroredPart = partIt.second;
//实现倒转:
QString newPartIdString = reverseUuid(mirroredPart["id"]);
partOldToNewMap.insert({mirroredPart["id"], newPartIdString});
//qDebug() << "Added part:" << newPartIdString << "by mirror from:" << mirroredPart["id"];
mirroredPart["__mirrorFromPartId"] = mirroredPart["id"];
mirroredPart["id"] = newPartIdString;
mirroredPart["__dirty"] = "true";
newParts.push_back(mirroredPart);
}
for (const auto &it: partOldToNewMap)
m_snapshot->parts[it.second]["__mirroredByPartId"] = it.first;
std::map<QString, QString> parentMap;
for (auto &componentIt: m_snapshot->components) {
for (const auto &childId: valueOfKeyInMapOrEmpty(componentIt.second, "children").split(",")) {
if (childId.isEmpty())
continue;
parentMap[childId] = componentIt.first;
//qDebug() << "Update component:" << childId << "parent to:" << componentIt.first;
}
}
for (const auto &childId: valueOfKeyInMapOrEmpty(m_snapshot->rootComponent, "children").split(",")) {
if (childId.isEmpty())
continue;
parentMap[childId] = QString();
//qDebug() << "Update component:" << childId << "parent to root";
}
std::vector<std::map<QString, QString>> newComponents;
for (auto &componentIt: m_snapshot->components) {
QString linkDataType = valueOfKeyInMapOrEmpty(componentIt.second, "linkDataType");
if ("partId" != linkDataType)
continue;
QString partIdString = valueOfKeyInMapOrEmpty(componentIt.second, "linkData");
auto findPart = partOldToNewMap.find(partIdString);
if (findPart == partOldToNewMap.end())
continue;
//完善倒转部分的对应信息:
std::map<QString, QString> mirroredComponent = componentIt.second;
QString newComponentIdString = reverseUuid(mirroredComponent["id"]);
//qDebug() << "Added component:" << newComponentIdString << "by mirror from:" << valueOfKeyInMapOrEmpty(componentIt.second, "id");
mirroredComponent["linkData"] = findPart->second;
mirroredComponent["id"] = newComponentIdString;
mirroredComponent["__dirty"] = "true";
parentMap[newComponentIdString] = parentMap[valueOfKeyInMapOrEmpty(componentIt.second, "id")];
//qDebug() << "Update component:" << newComponentIdString << "parent to:" << parentMap[valueOfKeyInMapOrEmpty(componentIt.second, "id")];
newComponents.push_back(mirroredComponent);
}
for (const auto &it: newParts) {
m_snapshot->parts[valueOfKeyInMapOrEmpty(it, "id")] = it;
}
for (const auto &it: newComponents) {
QString idString = valueOfKeyInMapOrEmpty(it, "id");
QString parentIdString = parentMap[idString];
m_snapshot->components[idString] = it;
//将镜像部分与非镜像部分进行链接
if (parentIdString.isEmpty()) {
m_snapshot->rootComponent["children"] += "," + idString;
//qDebug() << "Update rootComponent children:" << m_snapshot->rootComponent["children"];
} else {
m_snapshot->components[parentIdString]["children"] += "," + idString;
//qDebug() << "Update component:" << parentIdString << "children:" << m_snapshot->components[parentIdString]["children"];
}
}
}
采用镜像模式生成网格的另一半的优点:
a.Dust3d中的网格都是对称的,因此通过镜像生成完美符合对称的性质
b.通过镜像来生成另一半的网格速度非常快,适合于Dust3d这种轻量级的建模软件,可以更好地应用在游戏的模型建立中。
(2)类GeneratedCacheContext
类中存储的是将会生成镜像网格部分的附近节点,能使计算和查询更加快速
有关MeshCombiner类的相关内容可以浏览从源头看Dust3d | (七)meshcombiner:CGAL网格聚合_苏打不是糖的博客-CSDN博客Dust3d源码分析——meshcombinerhttps://blog.csdn.net/weixin_46273149/article/details/121462519?spm=1001.2014.3001.5501
class GeneratedCacheContext
{
public:
~GeneratedCacheContext()
{
for (auto &it: cachedCombination)
delete it.second;
for (auto &it: parts)
it.second.releaseMeshes();
for (auto &it: components)
it.second.releaseMeshes();
}
std::map<QString, GeneratedComponent> components;
std::map<QString, GeneratedPart> parts;
std::map<QString, QString> partMirrorIdMap;
std::map<QString, MeshCombiner::Mesh *> cachedCombination;
};
(3)collectParts
collectParts用于将snapshot中点和边的信息分别放入m_partNodeIds和m_partEdgeIds中
void MeshGenerator::collectParts()
{
//收集边和点的信息
for (const auto &node: m_snapshot->nodes) {
QString partId = valueOfKeyInMapOrEmpty(node.second, "partId");
if (partId.isEmpty())
continue;
m_partNodeIds[partId].insert(node.first);
}
for (const auto &edge: m_snapshot->edges) {
QString partId = valueOfKeyInMapOrEmpty(edge.second, "partId");
if (partId.isEmpty())
continue;
m_partEdgeIds[partId].insert(edge.first);
}
}
(4)checkDirtyFlags
检查脏标记,对计算进行加速
void MeshGenerator::checkDirtyFlags()
{
checkIsComponentDirty(QUuid().toString());
}
(5)fetch
抓取网格中的顶点信息与面信息
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);
}
(6) util库中的函数
recoverQuads:对焊接的部分进行处理
weldSeam:将两个面之间进行焊接
(7)postprocessObject函数
对物体进行后处理的函数,在此函数中实现了物体表面色彩更加流畅、不同面之间的法向量的交替更加顺滑的操作。此后处理操作在之后渲染阶段会对模型的光泽感等有重要的作用。
void MeshGenerator::postprocessObject(Object *object)
{
std::vector<QVector3D> combinedFacesNormals;
for (const auto &face: object->triangles) {
combinedFacesNormals.push_back(QVector3D::normal(
object->vertices[face[0]],
object->vertices[face[1]],
object->vertices[face[2]]
));
}
object->triangleNormals = combinedFacesNormals;
std::vector<std::pair<QUuid, QUuid>> sourceNodes;
triangleSourceNodeResolve(*object, m_nodeVertices, sourceNodes, &object->vertexSourceNodes);
object->setTriangleSourceNodes(sourceNodes);
//存储颜色信息
std::map<std::pair<QUuid, QUuid>, QColor> sourceNodeToColorMap;
for (const auto &node: object->nodes)
sourceNodeToColorMap.insert({{node.partId, node.nodeId}, node.color});
//生成颜色信息
object->triangleColors.resize(object->triangles.size(), Qt::white);
const std::vector<std::pair<QUuid, QUuid>> *triangleSourceNodes = object->triangleSourceNodes();
if (nullptr != triangleSourceNodes) {
for (size_t triangleIndex = 0; triangleIndex < object->triangles.size(); triangleIndex++) {
const auto &source = (*triangleSourceNodes)[triangleIndex];
object->triangleColors[triangleIndex] = sourceNodeToColorMap[source];
}
}
//生成法向量
std::vector<std::vector<QVector3D>> triangleVertexNormals;
generateSmoothTriangleVertexNormals(object->vertices,
object->triangles,
object->triangleNormals,
&triangleVertexNormals);
object->setTriangleVertexNormals(triangleVertexNormals);
}
二、 优化设计
使用脏标记模式对数据计算进行加速
脏标记模式:将工作推迟到必要时进行以避免不必要的工作。就是用一个标志位来标记内容是否发生变化,如果没有发生变化就直接使用缓存数据,不需要重新计算。