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.
master
Jeremy Hu 2019-02-21 08:27:15 +09:30
parent 3e1fde4260
commit 8053d3c3fb
7 changed files with 240 additions and 66 deletions

View File

@ -6,7 +6,6 @@ enum class CombineMode
{ {
Normal = 0, Normal = 0,
Inversion, Inversion,
Inflation,
Count Count
}; };
CombineMode CombineModeFromString(const char *modeString); CombineMode CombineModeFromString(const char *modeString);
@ -18,8 +17,6 @@ CombineMode CombineModeFromString(const char *modeString) \
return CombineMode::Normal; \ return CombineMode::Normal; \
if (mode == "Inversion") \ if (mode == "Inversion") \
return CombineMode::Inversion; \ return CombineMode::Inversion; \
if (mode == "Inflation") \
return CombineMode::Inflation; \
return CombineMode::Normal; \ return CombineMode::Normal; \
} }
const char *CombineModeToString(CombineMode mode); const char *CombineModeToString(CombineMode mode);
@ -31,8 +28,6 @@ const char *CombineModeToString(CombineMode mode) \
return "Normal"; \ return "Normal"; \
case CombineMode::Inversion: \ case CombineMode::Inversion: \
return "Inversion"; \ return "Inversion"; \
case CombineMode::Inflation: \
return "Inflation"; \
default: \ default: \
return "Normal"; \ return "Normal"; \
} \ } \
@ -46,8 +41,6 @@ QString CombineModeToDispName(CombineMode mode) \
return QObject::tr("Normal"); \ return QObject::tr("Normal"); \
case CombineMode::Inversion: \ case CombineMode::Inversion: \
return QObject::tr("Inversion"); \ return QObject::tr("Inversion"); \
case CombineMode::Inflation: \
return QObject::tr("Inflation"); \
default: \ default: \
return QObject::tr("Normal"); \ return QObject::tr("Normal"); \
} \ } \

View File

@ -409,6 +409,53 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
return mesh; return mesh;
} }
const std::map<QString, QString> *MeshGenerator::findComponent(const QString &componentIdString)
{
const std::map<QString, QString> *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<QString, QString> *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<QString, QString> *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 *MeshGenerator::combineComponentMesh(const QString &componentIdString, CombineMode *combineMode)
{ {
nodemesh::Combiner::Mesh *mesh = nullptr; nodemesh::Combiner::Mesh *mesh = nullptr;
@ -425,11 +472,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &com
component = &findComponent->second; component = &findComponent->second;
} }
*combineMode = CombineModeFromString(valueOfKeyInMapOrEmpty(*component, "combineMode").toUtf8().constData()); *combineMode = componentCombineMode(component);
if (*combineMode == CombineMode::Normal) {
if (isTrueValueString(valueOfKeyInMapOrEmpty(*component, "inverse")))
*combineMode = CombineMode::Inversion;
}
auto &componentCache = m_cacheContext->components[componentIdString]; auto &componentCache = m_cacheContext->components[componentIdString];
@ -462,56 +505,74 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &com
for (const auto &it: partCache.outcomeNodeVertices) for (const auto &it: partCache.outcomeNodeVertices)
componentCache.outcomeNodeVertices.push_back(it); componentCache.outcomeNodeVertices.push_back(it);
} else { } else {
std::vector<std::pair<CombineMode, std::vector<std::pair<QString, QString>>>> combineGroups;
// Firstly, group by combine mode
int currentGroupIndex = -1;
auto lastCombineMode = CombineMode::Count;
for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) { for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
if (childIdString.isEmpty()) if (childIdString.isEmpty())
continue; continue;
CombineMode childCombineMode = CombineMode::Normal; const auto &child = findComponent(childIdString);
nodemesh::Combiner::Mesh *subMesh = combineComponentMesh(childIdString, &childCombineMode); QString colorName = componentColorName(child);
auto combineMode = componentCombineMode(child);
const auto &childComponentCache = m_cacheContext->components[childIdString]; if (lastCombineMode != combineMode || lastCombineMode == CombineMode::Inversion) {
for (const auto &vertex: childComponentCache.noneSeamVertices) qDebug() << "New group[" << currentGroupIndex << "] for combine mode[" << CombineModeToString(combineMode) << "]";
componentCache.noneSeamVertices.insert(vertex); combineGroups.push_back({combineMode, {}});
for (const auto &it: childComponentCache.sharedQuadEdges) ++currentGroupIndex;
componentCache.sharedQuadEdges.insert(it); lastCombineMode = combineMode;
for (const auto &it: childComponentCache.outcomeNodes) }
componentCache.outcomeNodes.push_back(it); if (-1 == currentGroupIndex) {
for (const auto &it: childComponentCache.outcomeNodeVertices) qDebug() << "Should not happen: -1 == currentGroupIndex";
componentCache.outcomeNodeVertices.push_back(it);
qDebug() << "Combine mode:" << CombineModeToString(childCombineMode);
if (nullptr == subMesh) {
m_isSucceed = false;
qDebug() << "Child mesh is null";
continue; continue;
} }
if (subMesh->isNull()) { combineGroups[currentGroupIndex].second.push_back({childIdString, colorName});
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;
}
}
} }
// Secondly, sub group by color
std::vector<std::pair<nodemesh::Combiner::Mesh *, CombineMode>> groupMeshes;
for (const auto &group: combineGroups) {
std::set<size_t> used;
std::vector<std::vector<QString>> 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<std::pair<nodemesh::Combiner::Mesh *, CombineMode>> 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) if (nullptr != mesh)
@ -525,8 +586,75 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &com
return mesh; return mesh;
} }
nodemesh::Combiner::Mesh *MeshGenerator::combineMultipleMeshes(const std::vector<std::pair<nodemesh::Combiner::Mesh *, CombineMode>> &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<QString> &componentIdStrings, GeneratedComponent &componentCache)
{
std::vector<std::pair<nodemesh::Combiner::Mesh *, CombineMode>> 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::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()) if (first.isNull() || second.isNull())
return nullptr; return nullptr;
@ -537,7 +665,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineTwoMeshes(const nodemesh::Combin
&combinedVerticesSources); &combinedVerticesSources);
if (nullptr == newMesh) if (nullptr == newMesh)
return nullptr; return nullptr;
if (!newMesh->isNull()) { if (!newMesh->isNull() && recombine) {
nodemesh::Recombiner recombiner; nodemesh::Recombiner recombiner;
std::vector<QVector3D> combinedVertices; std::vector<QVector3D> combinedVertices;
std::vector<std::vector<size_t>> combinedFaces; std::vector<std::vector<size_t>> combinedFaces;
@ -686,7 +814,7 @@ void MeshGenerator::generate()
combinedVertices[face[2]] combinedVertices[face[2]]
)); ));
} }
recoverQuads(combinedVertices, combinedFaces, componentCache.sharedQuadEdges, m_outcome->triangleAndQuads); recoverQuads(combinedVertices, combinedFaces, componentCache.sharedQuadEdges, m_outcome->triangleAndQuads);
m_outcome->nodes = componentCache.outcomeNodes; m_outcome->nodes = componentCache.outcomeNodes;
@ -699,6 +827,19 @@ void MeshGenerator::generate()
triangleSourceNodeResolve(*m_outcome, sourceNodes); triangleSourceNodeResolve(*m_outcome, sourceNodes);
m_outcome->setTriangleSourceNodes(sourceNodes); m_outcome->setTriangleSourceNodes(sourceNodes);
std::map<std::pair<QUuid, QUuid>, 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<std::pair<QUuid, QUuid>> *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<std::vector<QVector3D>> triangleVertexNormals; std::vector<std::vector<QVector3D>> triangleVertexNormals;
generateSmoothTriangleVertexNormals(combinedVertices, generateSmoothTriangleVertexNormals(combinedVertices,
combinedFaces, combinedFaces,

View File

@ -93,10 +93,17 @@ private:
void collectSharedQuadEdges(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &faces, void collectSharedQuadEdges(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &faces,
std::set<std::pair<nodemesh::PositionKey, nodemesh::PositionKey>> *sharedQuadEdges); std::set<std::pair<nodemesh::PositionKey, nodemesh::PositionKey>> *sharedQuadEdges);
nodemesh::Combiner::Mesh *combineTwoMeshes(const nodemesh::Combiner::Mesh &first, const nodemesh::Combiner::Mesh &second, 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<QVector3D> &vertices, const std::vector<std::vector<size_t>> &triangles, void generateSmoothTriangleVertexNormals(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &triangles,
const std::vector<QVector3D> &triangleNormals, const std::vector<QVector3D> &triangleNormals,
std::vector<std::vector<QVector3D>> *triangleVertexNormals); std::vector<std::vector<QVector3D>> *triangleVertexNormals);
const std::map<QString, QString> *findComponent(const QString &componentIdString);
CombineMode componentCombineMode(const std::map<QString, QString> *component);
nodemesh::Combiner::Mesh *combineComponentChildGroupMesh(const std::vector<QString> &componentIdStrings,
GeneratedComponent &componentCache);
nodemesh::Combiner::Mesh *combineMultipleMeshes(const std::vector<std::pair<nodemesh::Combiner::Mesh *, CombineMode>> &multipleMeshes, bool recombine=true);
QString componentColorName(const std::map<QString, QString> *component);
}; };
#endif #endif

View File

@ -112,6 +112,7 @@ MeshLoader::MeshLoader(Outcome &outcome) :
const QVector2D defaultUv = QVector2D(0, 0); const QVector2D defaultUv = QVector2D(0, 0);
const QVector3D defaultTangent = QVector3D(0, 0, 0); const QVector3D defaultTangent = QVector3D(0, 0, 0);
for (size_t i = 0; i < outcome.triangles.size(); ++i) { for (size_t i = 0; i < outcome.triangles.size(); ++i) {
const auto &triangleColor = &outcome.triangleColors[i];
for (auto j = 0; j < 3; j++) { for (auto j = 0; j < 3; j++) {
int vertexIndex = outcome.triangles[i][j]; int vertexIndex = outcome.triangles[i][j];
const QVector3D *srcVert = &outcome.vertices[vertexIndex]; const QVector3D *srcVert = &outcome.vertices[vertexIndex];
@ -125,9 +126,9 @@ MeshLoader::MeshLoader(Outcome &outcome) :
if (triangleTangents) if (triangleTangents)
srcTangent = &(*triangleTangents)[i]; srcTangent = &(*triangleTangents)[i];
Vertex *dest = &m_triangleVertices[destIndex]; Vertex *dest = &m_triangleVertices[destIndex];
dest->colorR = 0; dest->colorR = triangleColor->redF();
dest->colorG = 0; dest->colorG = triangleColor->greenF();
dest->colorB = 0; dest->colorB = triangleColor->blueF();
dest->posX = srcVert->x(); dest->posX = srcVert->x();
dest->posY = srcVert->y(); dest->posY = srcVert->y();
dest->posZ = srcVert->z(); dest->posZ = srcVert->z();
@ -144,6 +145,37 @@ MeshLoader::MeshLoader(Outcome &outcome) :
destIndex++; 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() : MeshLoader::MeshLoader() :

View File

@ -14,7 +14,7 @@ ModelMeshBinder::ModelMeshBinder() :
m_renderTriangleVertexCount(0), m_renderTriangleVertexCount(0),
m_renderEdgeVertexCount(0), m_renderEdgeVertexCount(0),
m_newMeshComing(false), m_newMeshComing(false),
m_showWireframes(false), m_showWireframes(true),
m_hasTexture(false), m_hasTexture(false),
m_texture(nullptr), m_texture(nullptr),
m_hasNormalMap(false), m_hasNormalMap(false),

View File

@ -32,6 +32,7 @@ public:
std::vector<std::vector<size_t>> triangleAndQuads; std::vector<std::vector<size_t>> triangleAndQuads;
std::vector<std::vector<size_t>> triangles; std::vector<std::vector<size_t>> triangles;
std::vector<QVector3D> triangleNormals; std::vector<QVector3D> triangleNormals;
std::vector<QColor> triangleColors;
const std::vector<std::pair<QUuid, QUuid>> *triangleSourceNodes() const const std::vector<std::pair<QUuid, QUuid>> *triangleSourceNodes() const
{ {

View File

@ -320,7 +320,7 @@ void PartWidget::showColorSettingPopup(const QPoint &pos)
QPushButton *pickButton = new QPushButton(); QPushButton *pickButton = new QPushButton();
initToolButtonWithoutFont(pickButton); initToolButtonWithoutFont(pickButton);
QPalette palette = pickButton->palette(); 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::Window, choosenColor);
palette.setColor(QPalette::Button, choosenColor); palette.setColor(QPalette::Button, choosenColor);
pickButton->setPalette(palette); pickButton->setPalette(palette);
@ -331,7 +331,7 @@ void PartWidget::showColorSettingPopup(const QPoint &pos)
colorLayout->addStretch(); colorLayout->addStretch();
connect(colorEraser, &QPushButton::clicked, [=]() { connect(colorEraser, &QPushButton::clicked, [=]() {
emit setPartColorState(m_partId, false, Theme::white); emit setPartColorState(m_partId, false, Qt::white);
emit groupOperationAdded(); emit groupOperationAdded();
}); });