From 8053d3c3fb6e0d75507adfd1dc07a151e57624ab Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Thu, 21 Feb 2019 08:27:15 +0930 Subject: [PATCH] Improve the quality of mesh parts bridging First, split parts by combine mode, then sub group by color name, those parts which following in the same sub group will be bridged together. --- src/combinemode.h | 7 -- src/meshgenerator.cpp | 245 +++++++++++++++++++++++++++++++--------- src/meshgenerator.h | 9 +- src/meshloader.cpp | 38 ++++++- src/modelmeshbinder.cpp | 2 +- src/outcome.h | 1 + src/partwidget.cpp | 4 +- 7 files changed, 240 insertions(+), 66 deletions(-) diff --git a/src/combinemode.h b/src/combinemode.h index 9ec4a328..95ff2a52 100644 --- a/src/combinemode.h +++ b/src/combinemode.h @@ -6,7 +6,6 @@ enum class CombineMode { Normal = 0, Inversion, - Inflation, Count }; CombineMode CombineModeFromString(const char *modeString); @@ -18,8 +17,6 @@ CombineMode CombineModeFromString(const char *modeString) \ return CombineMode::Normal; \ if (mode == "Inversion") \ return CombineMode::Inversion; \ - if (mode == "Inflation") \ - return CombineMode::Inflation; \ return CombineMode::Normal; \ } const char *CombineModeToString(CombineMode mode); @@ -31,8 +28,6 @@ const char *CombineModeToString(CombineMode mode) \ return "Normal"; \ case CombineMode::Inversion: \ return "Inversion"; \ - case CombineMode::Inflation: \ - return "Inflation"; \ default: \ return "Normal"; \ } \ @@ -46,8 +41,6 @@ QString CombineModeToDispName(CombineMode mode) \ return QObject::tr("Normal"); \ case CombineMode::Inversion: \ return QObject::tr("Inversion"); \ - case CombineMode::Inflation: \ - return QObject::tr("Inflation"); \ default: \ return QObject::tr("Normal"); \ } \ diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index 8e36273e..96958bdb 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -409,6 +409,53 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt return mesh; } +const std::map *MeshGenerator::findComponent(const QString &componentIdString) +{ + const std::map *component = &m_snapshot->rootComponent; + if (componentIdString != QUuid().toString()) { + auto findComponent = m_snapshot->components.find(componentIdString); + if (findComponent == m_snapshot->components.end()) { + qDebug() << "Component not found:" << componentIdString; + return nullptr; + } + return &findComponent->second; + } + return component; +} + +CombineMode MeshGenerator::componentCombineMode(const std::map *component) +{ + if (nullptr == component) + return CombineMode::Normal; + CombineMode combineMode = CombineModeFromString(valueOfKeyInMapOrEmpty(*component, "combineMode").toUtf8().constData()); + if (combineMode == CombineMode::Normal) { + if (isTrueValueString(valueOfKeyInMapOrEmpty(*component, "inverse"))) + combineMode = CombineMode::Inversion; + } + return combineMode; +} + +QString MeshGenerator::componentColorName(const std::map *component) +{ + if (nullptr == component) + return QString(); + QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType"); + if ("partId" == linkDataType) { + QString partIdString = valueOfKeyInMapOrEmpty(*component, "linkData"); + auto findPart = m_snapshot->parts.find(partIdString); + if (findPart == m_snapshot->parts.end()) { + qDebug() << "Find part failed:" << partIdString; + return QString(); + } + auto &part = findPart->second; + QString colorName = valueOfKeyInMapOrEmpty(part, "color"); + if (colorName.isEmpty()) + return QString("-"); + return colorName; + } + return QString(); +} + nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &componentIdString, CombineMode *combineMode) { nodemesh::Combiner::Mesh *mesh = nullptr; @@ -425,11 +472,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &com component = &findComponent->second; } - *combineMode = CombineModeFromString(valueOfKeyInMapOrEmpty(*component, "combineMode").toUtf8().constData()); - if (*combineMode == CombineMode::Normal) { - if (isTrueValueString(valueOfKeyInMapOrEmpty(*component, "inverse"))) - *combineMode = CombineMode::Inversion; - } + *combineMode = componentCombineMode(component); auto &componentCache = m_cacheContext->components[componentIdString]; @@ -462,56 +505,74 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &com for (const auto &it: partCache.outcomeNodeVertices) componentCache.outcomeNodeVertices.push_back(it); } else { + std::vector>>> combineGroups; + // Firstly, group by combine mode + int currentGroupIndex = -1; + auto lastCombineMode = CombineMode::Count; for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) { if (childIdString.isEmpty()) continue; - CombineMode childCombineMode = CombineMode::Normal; - nodemesh::Combiner::Mesh *subMesh = combineComponentMesh(childIdString, &childCombineMode); - - const auto &childComponentCache = m_cacheContext->components[childIdString]; - for (const auto &vertex: childComponentCache.noneSeamVertices) - componentCache.noneSeamVertices.insert(vertex); - for (const auto &it: childComponentCache.sharedQuadEdges) - componentCache.sharedQuadEdges.insert(it); - for (const auto &it: childComponentCache.outcomeNodes) - componentCache.outcomeNodes.push_back(it); - for (const auto &it: childComponentCache.outcomeNodeVertices) - componentCache.outcomeNodeVertices.push_back(it); - - qDebug() << "Combine mode:" << CombineModeToString(childCombineMode); - if (nullptr == subMesh) { - m_isSucceed = false; - qDebug() << "Child mesh is null"; + const auto &child = findComponent(childIdString); + QString colorName = componentColorName(child); + auto combineMode = componentCombineMode(child); + if (lastCombineMode != combineMode || lastCombineMode == CombineMode::Inversion) { + qDebug() << "New group[" << currentGroupIndex << "] for combine mode[" << CombineModeToString(combineMode) << "]"; + combineGroups.push_back({combineMode, {}}); + ++currentGroupIndex; + lastCombineMode = combineMode; + } + if (-1 == currentGroupIndex) { + qDebug() << "Should not happen: -1 == currentGroupIndex"; continue; } - if (subMesh->isNull()) { - m_isSucceed = false; - qDebug() << "Child mesh is uncombinable"; - delete subMesh; - continue; - } - if (nullptr == mesh) { - if (childCombineMode == CombineMode::Inversion) { - delete subMesh; - } else { - mesh = subMesh; - } - } else { - nodemesh::Combiner::Mesh *newMesh = combineTwoMeshes(*mesh, - *subMesh, - childCombineMode == CombineMode::Inversion ? - nodemesh::Combiner::Method::Diff : nodemesh::Combiner::Method::Union); - delete subMesh; - if (newMesh && !newMesh->isNull()) { - delete mesh; - mesh = newMesh; - } else { - m_isSucceed = false; - qDebug() << "Mesh combine failed"; - delete newMesh; - } - } + combineGroups[currentGroupIndex].second.push_back({childIdString, colorName}); } + // Secondly, sub group by color + std::vector> groupMeshes; + for (const auto &group: combineGroups) { + std::set used; + std::vector> componentIdStrings; + int currentSubGroupIndex = -1; + auto lastColorName = QString(); + for (size_t i = 0; i < group.second.size(); ++i) { + if (used.find(i) != used.end()) + continue; + const auto &colorName = group.second[i].second; + if (lastColorName != colorName || lastColorName.isEmpty()) { + qDebug() << "New sub group[" << currentSubGroupIndex << "] for color[" << colorName << "]"; + componentIdStrings.push_back({}); + ++currentSubGroupIndex; + lastColorName = colorName; + } + if (-1 == currentSubGroupIndex) { + qDebug() << "Should not happen: -1 == currentSubGroupIndex"; + continue; + } + used.insert(i); + componentIdStrings[currentSubGroupIndex].push_back(group.second[i].first); + if (colorName.isEmpty()) + continue; + for (size_t j = i + 1; j < group.second.size(); ++j) { + if (used.find(j) != used.end()) + continue; + const auto &otherColorName = group.second[j].second; + if (otherColorName.isEmpty()) + continue; + if (otherColorName != colorName) + continue; + used.insert(j); + componentIdStrings[currentSubGroupIndex].push_back(group.second[j].first); + } + } + std::vector> multipleMeshes; + for (const auto &it: componentIdStrings) { + nodemesh::Combiner::Mesh *childMesh = combineComponentChildGroupMesh(it, componentCache); + multipleMeshes.push_back({childMesh, CombineMode::Normal}); + } + nodemesh::Combiner::Mesh *subGroupMesh = combineMultipleMeshes(multipleMeshes, false); + groupMeshes.push_back({subGroupMesh, group.first}); + } + mesh = combineMultipleMeshes(groupMeshes, false); } if (nullptr != mesh) @@ -525,8 +586,75 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &com return mesh; } +nodemesh::Combiner::Mesh *MeshGenerator::combineMultipleMeshes(const std::vector> &multipleMeshes, bool recombine) +{ + nodemesh::Combiner::Mesh *mesh = nullptr; + for (const auto &it: multipleMeshes) { + const auto &childCombineMode = it.second; + nodemesh::Combiner::Mesh *subMesh = it.first; + qDebug() << "Combine mode:" << CombineModeToString(childCombineMode); + if (nullptr == subMesh) { + m_isSucceed = false; + qDebug() << "Child mesh is null"; + continue; + } + if (subMesh->isNull()) { + m_isSucceed = false; + qDebug() << "Child mesh is uncombinable"; + delete subMesh; + continue; + } + if (nullptr == mesh) { + //if (childCombineMode == CombineMode::Inversion) { + // delete subMesh; + //} else { + mesh = subMesh; + //} + } else { + nodemesh::Combiner::Mesh *newMesh = combineTwoMeshes(*mesh, + *subMesh, + childCombineMode == CombineMode::Inversion ? + nodemesh::Combiner::Method::Diff : nodemesh::Combiner::Method::Union, + recombine); + delete subMesh; + if (newMesh && !newMesh->isNull()) { + delete mesh; + mesh = newMesh; + } else { + m_isSucceed = false; + qDebug() << "Mesh combine failed"; + delete newMesh; + } + } + } + return mesh; +} + +nodemesh::Combiner::Mesh *MeshGenerator::combineComponentChildGroupMesh(const std::vector &componentIdStrings, GeneratedComponent &componentCache) +{ + std::vector> multipleMeshes; + for (const auto &childIdString: componentIdStrings) { + CombineMode childCombineMode = CombineMode::Normal; + nodemesh::Combiner::Mesh *subMesh = combineComponentMesh(childIdString, &childCombineMode); + + const auto &childComponentCache = m_cacheContext->components[childIdString]; + for (const auto &vertex: childComponentCache.noneSeamVertices) + componentCache.noneSeamVertices.insert(vertex); + for (const auto &it: childComponentCache.sharedQuadEdges) + componentCache.sharedQuadEdges.insert(it); + for (const auto &it: childComponentCache.outcomeNodes) + componentCache.outcomeNodes.push_back(it); + for (const auto &it: childComponentCache.outcomeNodeVertices) + componentCache.outcomeNodeVertices.push_back(it); + + multipleMeshes.push_back({subMesh, childCombineMode}); + } + return combineMultipleMeshes(multipleMeshes); +} + nodemesh::Combiner::Mesh *MeshGenerator::combineTwoMeshes(const nodemesh::Combiner::Mesh &first, const nodemesh::Combiner::Mesh &second, - nodemesh::Combiner::Method method) + nodemesh::Combiner::Method method, + bool recombine) { if (first.isNull() || second.isNull()) return nullptr; @@ -537,7 +665,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineTwoMeshes(const nodemesh::Combin &combinedVerticesSources); if (nullptr == newMesh) return nullptr; - if (!newMesh->isNull()) { + if (!newMesh->isNull() && recombine) { nodemesh::Recombiner recombiner; std::vector combinedVertices; std::vector> combinedFaces; @@ -686,7 +814,7 @@ void MeshGenerator::generate() combinedVertices[face[2]] )); } - + recoverQuads(combinedVertices, combinedFaces, componentCache.sharedQuadEdges, m_outcome->triangleAndQuads); m_outcome->nodes = componentCache.outcomeNodes; @@ -699,6 +827,19 @@ void MeshGenerator::generate() triangleSourceNodeResolve(*m_outcome, sourceNodes); m_outcome->setTriangleSourceNodes(sourceNodes); + std::map, QColor> sourceNodeToColorMap; + for (const auto &node: m_outcome->nodes) + sourceNodeToColorMap.insert({{node.partId, node.nodeId}, node.color}); + + m_outcome->triangleColors.resize(m_outcome->triangles.size(), Qt::white); + const std::vector> *triangleSourceNodes = m_outcome->triangleSourceNodes(); + if (nullptr != triangleSourceNodes) { + for (size_t triangleIndex = 0; triangleIndex < m_outcome->triangles.size(); triangleIndex++) { + const auto &source = (*triangleSourceNodes)[triangleIndex]; + m_outcome->triangleColors[triangleIndex] = sourceNodeToColorMap[source]; + } + } + std::vector> triangleVertexNormals; generateSmoothTriangleVertexNormals(combinedVertices, combinedFaces, diff --git a/src/meshgenerator.h b/src/meshgenerator.h index d5f13581..1d6d52f6 100644 --- a/src/meshgenerator.h +++ b/src/meshgenerator.h @@ -93,10 +93,17 @@ private: void collectSharedQuadEdges(const std::vector &vertices, const std::vector> &faces, std::set> *sharedQuadEdges); nodemesh::Combiner::Mesh *combineTwoMeshes(const nodemesh::Combiner::Mesh &first, const nodemesh::Combiner::Mesh &second, - nodemesh::Combiner::Method method); + nodemesh::Combiner::Method method, + bool recombine=true); void generateSmoothTriangleVertexNormals(const std::vector &vertices, const std::vector> &triangles, const std::vector &triangleNormals, std::vector> *triangleVertexNormals); + const std::map *findComponent(const QString &componentIdString); + CombineMode componentCombineMode(const std::map *component); + nodemesh::Combiner::Mesh *combineComponentChildGroupMesh(const std::vector &componentIdStrings, + GeneratedComponent &componentCache); + nodemesh::Combiner::Mesh *combineMultipleMeshes(const std::vector> &multipleMeshes, bool recombine=true); + QString componentColorName(const std::map *component); }; #endif diff --git a/src/meshloader.cpp b/src/meshloader.cpp index 50f801ec..0ca7dd2e 100644 --- a/src/meshloader.cpp +++ b/src/meshloader.cpp @@ -112,6 +112,7 @@ MeshLoader::MeshLoader(Outcome &outcome) : const QVector2D defaultUv = QVector2D(0, 0); const QVector3D defaultTangent = QVector3D(0, 0, 0); for (size_t i = 0; i < outcome.triangles.size(); ++i) { + const auto &triangleColor = &outcome.triangleColors[i]; for (auto j = 0; j < 3; j++) { int vertexIndex = outcome.triangles[i][j]; const QVector3D *srcVert = &outcome.vertices[vertexIndex]; @@ -125,9 +126,9 @@ MeshLoader::MeshLoader(Outcome &outcome) : if (triangleTangents) srcTangent = &(*triangleTangents)[i]; Vertex *dest = &m_triangleVertices[destIndex]; - dest->colorR = 0; - dest->colorG = 0; - dest->colorB = 0; + dest->colorR = triangleColor->redF(); + dest->colorG = triangleColor->greenF(); + dest->colorB = triangleColor->blueF(); dest->posX = srcVert->x(); dest->posY = srcVert->y(); dest->posZ = srcVert->z(); @@ -144,6 +145,37 @@ MeshLoader::MeshLoader(Outcome &outcome) : destIndex++; } } + + // Uncomment out to show wireframes + /* + size_t edgeCount = 0; + for (const auto &face: outcome.triangleAndQuads) { + edgeCount += face.size(); + } + m_edgeVertexCount = edgeCount * 2; + m_edgeVertices = new Vertex[m_edgeVertexCount]; + size_t edgeVertexIndex = 0; + for (size_t faceIndex = 0; faceIndex < outcome.triangleAndQuads.size(); ++faceIndex) { + const auto &face = outcome.triangleAndQuads[faceIndex]; + for (size_t i = 0; i < face.size(); ++i) { + for (size_t x = 0; x < 2; ++x) { + size_t sourceIndex = face[(i + x) % face.size()]; + const QVector3D *srcVert = &outcome.vertices[sourceIndex]; + Vertex *dest = &m_edgeVertices[edgeVertexIndex]; + memset(dest, 0, sizeof(Vertex)); + dest->colorR = 0.0; + dest->colorG = 0.0; + dest->colorB = 0.0; + dest->posX = srcVert->x(); + dest->posY = srcVert->y(); + dest->posZ = srcVert->z(); + dest->metalness = m_defaultMetalness; + dest->roughness = m_defaultRoughness; + edgeVertexIndex++; + } + } + } + */ } MeshLoader::MeshLoader() : diff --git a/src/modelmeshbinder.cpp b/src/modelmeshbinder.cpp index 15578a7d..67b4356e 100644 --- a/src/modelmeshbinder.cpp +++ b/src/modelmeshbinder.cpp @@ -14,7 +14,7 @@ ModelMeshBinder::ModelMeshBinder() : m_renderTriangleVertexCount(0), m_renderEdgeVertexCount(0), m_newMeshComing(false), - m_showWireframes(false), + m_showWireframes(true), m_hasTexture(false), m_texture(nullptr), m_hasNormalMap(false), diff --git a/src/outcome.h b/src/outcome.h index 54b57345..a9e30e38 100644 --- a/src/outcome.h +++ b/src/outcome.h @@ -32,6 +32,7 @@ public: std::vector> triangleAndQuads; std::vector> triangles; std::vector triangleNormals; + std::vector triangleColors; const std::vector> *triangleSourceNodes() const { diff --git a/src/partwidget.cpp b/src/partwidget.cpp index 41a3f10d..2d0f5f5c 100644 --- a/src/partwidget.cpp +++ b/src/partwidget.cpp @@ -320,7 +320,7 @@ void PartWidget::showColorSettingPopup(const QPoint &pos) QPushButton *pickButton = new QPushButton(); initToolButtonWithoutFont(pickButton); QPalette palette = pickButton->palette(); - QColor choosenColor = part->hasColor ? part->color : Theme::white; + QColor choosenColor = part->hasColor ? part->color : Qt::white; palette.setColor(QPalette::Window, choosenColor); palette.setColor(QPalette::Button, choosenColor); pickButton->setPalette(palette); @@ -331,7 +331,7 @@ void PartWidget::showColorSettingPopup(const QPoint &pos) colorLayout->addStretch(); connect(colorEraser, &QPushButton::clicked, [=]() { - emit setPartColorState(m_partId, false, Theme::white); + emit setPartColorState(m_partId, false, Qt::white); emit groupOperationAdded(); });