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
parent
3e1fde4260
commit
8053d3c3fb
|
@ -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"); \
|
||||
} \
|
||||
|
|
|
@ -409,6 +409,53 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
|||
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 *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<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(",")) {
|
||||
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;
|
||||
combineGroups[currentGroupIndex].second.push_back({childIdString, colorName});
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
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;
|
||||
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)
|
||||
|
@ -525,8 +586,75 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &com
|
|||
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::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<QVector3D> combinedVertices;
|
||||
std::vector<std::vector<size_t>> combinedFaces;
|
||||
|
@ -699,6 +827,19 @@ void MeshGenerator::generate()
|
|||
triangleSourceNodeResolve(*m_outcome, 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;
|
||||
generateSmoothTriangleVertexNormals(combinedVertices,
|
||||
combinedFaces,
|
||||
|
|
|
@ -93,10 +93,17 @@ private:
|
|||
void collectSharedQuadEdges(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &faces,
|
||||
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::Method method);
|
||||
nodemesh::Combiner::Method method,
|
||||
bool recombine=true);
|
||||
void generateSmoothTriangleVertexNormals(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &triangles,
|
||||
const std::vector<QVector3D> &triangleNormals,
|
||||
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
|
||||
|
|
|
@ -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() :
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
std::vector<std::vector<size_t>> triangleAndQuads;
|
||||
std::vector<std::vector<size_t>> triangles;
|
||||
std::vector<QVector3D> triangleNormals;
|
||||
std::vector<QColor> triangleColors;
|
||||
|
||||
const std::vector<std::pair<QUuid, QUuid>> *triangleSourceNodes() const
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue