从源头看Dust3d | (八)meshgenerator:网格生成预备知识

2021SC@SDUSC 

目录

MeshGenerator类预备知识

一、generate函数

1.涉及的函数

(1)preprocessMirror函数

(2)类GeneratedCacheContext

 (3)collectParts

(4)checkDirtyFlags

 (5)fetch

(6) util库中的函数

(7)postprocessObject函数

二、 优化设计

使用脏标记模式对数据计算进行加速


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);
}

二、 优化设计

使用脏标记模式对数据计算进行加速

脏标记模式:将工作推迟到必要时进行以避免不必要的工作。就是用一个标志位来标记内容是否发生变化,如果没有发生变化就直接使用缓存数据,不需要重新计算。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值