Improve mirrored mesh quality
parent
fbfed42773
commit
3233e7add0
|
@ -344,7 +344,9 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
|
||||||
*retryable = true;
|
*retryable = true;
|
||||||
|
|
||||||
bool isDisabled = isTrueValueString(valueOfKeyInMapOrEmpty(part, "disabled"));
|
bool isDisabled = isTrueValueString(valueOfKeyInMapOrEmpty(part, "disabled"));
|
||||||
bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part, "xMirrored"));
|
//bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part, "xMirrored"));
|
||||||
|
bool _xFlip = isTrueValueString(valueOfKeyInMapOrEmpty(part, "_xFlip"));
|
||||||
|
QString _xMirroredFrom = valueOfKeyInMapOrEmpty(part, "_xMirroredFrom");
|
||||||
bool subdived = isTrueValueString(valueOfKeyInMapOrEmpty(part, "subdived"));
|
bool subdived = isTrueValueString(valueOfKeyInMapOrEmpty(part, "subdived"));
|
||||||
bool rounded = isTrueValueString(valueOfKeyInMapOrEmpty(part, "rounded"));
|
bool rounded = isTrueValueString(valueOfKeyInMapOrEmpty(part, "rounded"));
|
||||||
bool chamfered = isTrueValueString(valueOfKeyInMapOrEmpty(part, "chamfered"));
|
bool chamfered = isTrueValueString(valueOfKeyInMapOrEmpty(part, "chamfered"));
|
||||||
|
@ -358,6 +360,8 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
|
||||||
auto target = PartTargetFromString(valueOfKeyInMapOrEmpty(part, "target").toUtf8().constData());
|
auto target = PartTargetFromString(valueOfKeyInMapOrEmpty(part, "target").toUtf8().constData());
|
||||||
auto base = PartBaseFromString(valueOfKeyInMapOrEmpty(part, "base").toUtf8().constData());
|
auto base = PartBaseFromString(valueOfKeyInMapOrEmpty(part, "base").toUtf8().constData());
|
||||||
|
|
||||||
|
QString searchPartIdString = _xMirroredFrom.isEmpty() ? partIdString : _xMirroredFrom;
|
||||||
|
|
||||||
QString cutFaceString = valueOfKeyInMapOrEmpty(part, "cutFace");
|
QString cutFaceString = valueOfKeyInMapOrEmpty(part, "cutFace");
|
||||||
std::vector<QVector2D> cutTemplate;
|
std::vector<QVector2D> cutTemplate;
|
||||||
cutFaceStringToCutTemplate(cutFaceString, cutTemplate);
|
cutFaceStringToCutTemplate(cutFaceString, cutTemplate);
|
||||||
|
@ -417,7 +421,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
|
||||||
fillMeshFileId = QUuid(fillMeshString);
|
fillMeshFileId = QUuid(fillMeshString);
|
||||||
if (!fillMeshFileId.isNull()) {
|
if (!fillMeshFileId.isNull()) {
|
||||||
*retryable = false;
|
*retryable = false;
|
||||||
xMirrored = false;
|
//xMirrored = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,7 +449,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
|
||||||
QString cutFace;
|
QString cutFace;
|
||||||
};
|
};
|
||||||
std::map<QString, NodeInfo> nodeInfos;
|
std::map<QString, NodeInfo> nodeInfos;
|
||||||
for (const auto &nodeIdString: m_partNodeIds[partIdString]) {
|
for (const auto &nodeIdString: m_partNodeIds[searchPartIdString]) {
|
||||||
auto findNode = m_snapshot->nodes.find(nodeIdString);
|
auto findNode = m_snapshot->nodes.find(nodeIdString);
|
||||||
if (findNode == m_snapshot->nodes.end()) {
|
if (findNode == m_snapshot->nodes.end()) {
|
||||||
qDebug() << "Find node failed:" << nodeIdString;
|
qDebug() << "Find node failed:" << nodeIdString;
|
||||||
|
@ -484,7 +488,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<std::pair<QString, QString>> edges;
|
std::set<std::pair<QString, QString>> edges;
|
||||||
for (const auto &edgeIdString: m_partEdgeIds[partIdString]) {
|
for (const auto &edgeIdString: m_partEdgeIds[searchPartIdString]) {
|
||||||
auto findEdge = m_snapshot->edges.find(edgeIdString);
|
auto findEdge = m_snapshot->edges.find(edgeIdString);
|
||||||
if (findEdge == m_snapshot->edges.end()) {
|
if (findEdge == m_snapshot->edges.end()) {
|
||||||
qDebug() << "Find edge failed:" << edgeIdString;
|
qDebug() << "Find edge failed:" << edgeIdString;
|
||||||
|
@ -515,13 +519,13 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
|
||||||
std::map<int, QString> nodeIndexToIdStringMap;
|
std::map<int, QString> nodeIndexToIdStringMap;
|
||||||
StrokeModifier *strokeModifier = nullptr;
|
StrokeModifier *strokeModifier = nullptr;
|
||||||
|
|
||||||
QString mirroredPartIdString;
|
//QString mirroredPartIdString;
|
||||||
QUuid mirroredPartId;
|
//QUuid mirroredPartId;
|
||||||
if (xMirrored) {
|
//if (xMirrored) {
|
||||||
mirroredPartId = QUuid().createUuid();
|
// mirroredPartId = QUuid().createUuid();
|
||||||
mirroredPartIdString = mirroredPartId.toString();
|
// mirroredPartIdString = mirroredPartId.toString();
|
||||||
m_cacheContext->partMirrorIdMap[mirroredPartIdString] = partIdString;
|
// m_cacheContext->partMirrorIdMap[mirroredPartIdString] = partIdString;
|
||||||
}
|
//}
|
||||||
|
|
||||||
auto addNodeToPartCache = [&](const QString &nodeIdString, const NodeInfo &nodeInfo) {
|
auto addNodeToPartCache = [&](const QString &nodeIdString, const NodeInfo &nodeInfo) {
|
||||||
OutcomeNode outcomeNode;
|
OutcomeNode outcomeNode;
|
||||||
|
@ -534,28 +538,31 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
|
||||||
outcomeNode.countershaded = countershaded;
|
outcomeNode.countershaded = countershaded;
|
||||||
outcomeNode.colorSolubility = colorSolubility;
|
outcomeNode.colorSolubility = colorSolubility;
|
||||||
outcomeNode.boneMark = nodeInfo.boneMark;
|
outcomeNode.boneMark = nodeInfo.boneMark;
|
||||||
outcomeNode.mirroredByPartId = mirroredPartId;
|
//outcomeNode.mirroredByPartId = mirroredPartId;
|
||||||
outcomeNode.joined = partCache.joined;
|
outcomeNode.joined = partCache.joined;
|
||||||
partCache.outcomeNodes.push_back(outcomeNode);
|
if (_xFlip) {
|
||||||
if (xMirrored) {
|
|
||||||
outcomeNode.partId = mirroredPartId;
|
|
||||||
outcomeNode.mirrorFromPartId = QUuid(partId);
|
|
||||||
outcomeNode.mirroredByPartId = QUuid();
|
|
||||||
outcomeNode.origin.setX(-nodeInfo.position.x());
|
outcomeNode.origin.setX(-nodeInfo.position.x());
|
||||||
partCache.outcomeNodes.push_back(outcomeNode);
|
|
||||||
}
|
}
|
||||||
|
partCache.outcomeNodes.push_back(outcomeNode);
|
||||||
|
//if (xMirrored) {
|
||||||
|
// outcomeNode.partId = mirroredPartId;
|
||||||
|
// outcomeNode.mirrorFromPartId = QUuid(partId);
|
||||||
|
// outcomeNode.mirroredByPartId = QUuid();
|
||||||
|
// outcomeNode.origin.setX(-nodeInfo.position.x());
|
||||||
|
// partCache.outcomeNodes.push_back(outcomeNode);
|
||||||
|
//}
|
||||||
};
|
};
|
||||||
auto addEdgeToPartCache = [&](const QString &firstNodeIdString, const QString &secondNodeIdString) {
|
auto addEdgeToPartCache = [&](const QString &firstNodeIdString, const QString &secondNodeIdString) {
|
||||||
partCache.outcomeEdges.push_back({
|
partCache.outcomeEdges.push_back({
|
||||||
{QUuid(partIdString), QUuid(firstNodeIdString)},
|
{QUuid(partIdString), QUuid(firstNodeIdString)},
|
||||||
{QUuid(partIdString), QUuid(secondNodeIdString)}
|
{QUuid(partIdString), QUuid(secondNodeIdString)}
|
||||||
});
|
});
|
||||||
if (xMirrored) {
|
//if (xMirrored) {
|
||||||
partCache.outcomeEdges.push_back({
|
// partCache.outcomeEdges.push_back({
|
||||||
{mirroredPartId, QUuid(firstNodeIdString)},
|
// {mirroredPartId, QUuid(firstNodeIdString)},
|
||||||
{mirroredPartId, QUuid(secondNodeIdString)}
|
// {mirroredPartId, QUuid(secondNodeIdString)}
|
||||||
});
|
// });
|
||||||
}
|
//}
|
||||||
};
|
};
|
||||||
|
|
||||||
strokeModifier = new StrokeModifier;
|
strokeModifier = new StrokeModifier;
|
||||||
|
@ -663,6 +670,12 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
|
||||||
|
|
||||||
partCache.vertices = strokeMeshBuilder->generatedVertices();
|
partCache.vertices = strokeMeshBuilder->generatedVertices();
|
||||||
partCache.faces = strokeMeshBuilder->generatedFaces();
|
partCache.faces = strokeMeshBuilder->generatedFaces();
|
||||||
|
if (_xFlip) {
|
||||||
|
for (auto &it: partCache.vertices)
|
||||||
|
it.setX(-it.x());
|
||||||
|
for (auto &it: partCache.faces)
|
||||||
|
std::reverse(it.begin(), it.end());
|
||||||
|
}
|
||||||
sourceNodeIndices = strokeMeshBuilder->generatedVerticesSourceNodeIndices();
|
sourceNodeIndices = strokeMeshBuilder->generatedVerticesSourceNodeIndices();
|
||||||
for (size_t i = 0; i < partCache.vertices.size(); ++i) {
|
for (size_t i = 0; i < partCache.vertices.size(); ++i) {
|
||||||
const auto &position = partCache.vertices[i];
|
const auto &position = partCache.vertices[i];
|
||||||
|
@ -684,9 +697,16 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
|
||||||
partCache.outcomeNodes[paintNode.originNodeIndex].direction = paintNode.direction;
|
partCache.outcomeNodes[paintNode.originNodeIndex].direction = paintNode.direction;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (strokeMeshBuilder->buildBaseNormalsOnly())
|
if (strokeMeshBuilder->buildBaseNormalsOnly()) {
|
||||||
buildSucceed = fillPartWithMesh(partCache, fillMeshFileId,
|
buildSucceed = fillPartWithMesh(partCache, fillMeshFileId,
|
||||||
deformThickness, deformWidth, cutRotation, strokeMeshBuilder);
|
deformThickness, deformWidth, cutRotation, strokeMeshBuilder);
|
||||||
|
if (_xFlip) {
|
||||||
|
for (auto &it: partCache.vertices)
|
||||||
|
it.setX(-it.x());
|
||||||
|
for (auto &it: partCache.faces)
|
||||||
|
std::reverse(it.begin(), it.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete strokeMeshBuilder;
|
delete strokeMeshBuilder;
|
||||||
|
@ -698,43 +718,43 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
|
||||||
if (buildSucceed) {
|
if (buildSucceed) {
|
||||||
mesh = new MeshCombiner::Mesh(partCache.vertices, partCache.faces, false);
|
mesh = new MeshCombiner::Mesh(partCache.vertices, partCache.faces, false);
|
||||||
if (!mesh->isNull()) {
|
if (!mesh->isNull()) {
|
||||||
if (xMirrored) {
|
//if (xMirrored) {
|
||||||
std::vector<QVector3D> xMirroredVertices;
|
// std::vector<QVector3D> xMirroredVertices;
|
||||||
std::vector<std::vector<size_t>> xMirroredFaces;
|
// std::vector<std::vector<size_t>> xMirroredFaces;
|
||||||
makeXmirror(partCache.vertices, partCache.faces, &xMirroredVertices, &xMirroredFaces);
|
// makeXmirror(partCache.vertices, partCache.faces, &xMirroredVertices, &xMirroredFaces);
|
||||||
for (size_t i = 0; i < xMirroredVertices.size(); ++i) {
|
// for (size_t i = 0; i < xMirroredVertices.size(); ++i) {
|
||||||
const auto &position = xMirroredVertices[i];
|
// const auto &position = xMirroredVertices[i];
|
||||||
size_t nodeIndex = 0;
|
// size_t nodeIndex = 0;
|
||||||
const auto &source = sourceNodeIndices[i];
|
// const auto &source = sourceNodeIndices[i];
|
||||||
nodeIndex = strokeModifier->nodes()[source].originNodeIndex;
|
// nodeIndex = strokeModifier->nodes()[source].originNodeIndex;
|
||||||
const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex];
|
// const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex];
|
||||||
partCache.outcomeNodeVertices.push_back({position, {mirroredPartIdString, nodeIdString}});
|
// partCache.outcomeNodeVertices.push_back({position, {mirroredPartIdString, nodeIdString}});
|
||||||
}
|
// }
|
||||||
size_t xMirrorStart = partCache.vertices.size();
|
// size_t xMirrorStart = partCache.vertices.size();
|
||||||
for (const auto &vertex: xMirroredVertices)
|
// for (const auto &vertex: xMirroredVertices)
|
||||||
partCache.vertices.push_back(vertex);
|
// partCache.vertices.push_back(vertex);
|
||||||
for (const auto &face: xMirroredFaces) {
|
// for (const auto &face: xMirroredFaces) {
|
||||||
std::vector<size_t> newFace = face;
|
// std::vector<size_t> newFace = face;
|
||||||
for (auto &it: newFace)
|
// for (auto &it: newFace)
|
||||||
it += xMirrorStart;
|
// it += xMirrorStart;
|
||||||
partCache.faces.push_back(newFace);
|
// partCache.faces.push_back(newFace);
|
||||||
}
|
// }
|
||||||
MeshCombiner::Mesh *newMesh = nullptr;
|
// MeshCombiner::Mesh *newMesh = nullptr;
|
||||||
MeshCombiner::Mesh *xMirroredMesh = new MeshCombiner::Mesh(xMirroredVertices, xMirroredFaces, false);
|
// MeshCombiner::Mesh *xMirroredMesh = new MeshCombiner::Mesh(xMirroredVertices, xMirroredFaces, false);
|
||||||
if (!xMirroredMesh->isNull()) {
|
// if (!xMirroredMesh->isNull()) {
|
||||||
newMesh = combineTwoMeshes(*mesh,
|
// newMesh = combineTwoMeshes(*mesh,
|
||||||
*xMirroredMesh, MeshCombiner::Method::Union);
|
// *xMirroredMesh, MeshCombiner::Method::Union);
|
||||||
}
|
// }
|
||||||
delete xMirroredMesh;
|
// delete xMirroredMesh;
|
||||||
if (newMesh && !newMesh->isNull()) {
|
// if (newMesh && !newMesh->isNull()) {
|
||||||
delete mesh;
|
// delete mesh;
|
||||||
mesh = newMesh;
|
// mesh = newMesh;
|
||||||
} else {
|
// } else {
|
||||||
hasMeshError = true;
|
// hasMeshError = true;
|
||||||
qDebug() << "Xmirrored mesh generate failed";
|
// qDebug() << "Xmirrored mesh generate failed";
|
||||||
delete newMesh;
|
// delete newMesh;
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
} else {
|
} else {
|
||||||
hasMeshError = true;
|
hasMeshError = true;
|
||||||
qDebug() << "Mesh built is uncombinable";
|
qDebug() << "Mesh built is uncombinable";
|
||||||
|
@ -1442,158 +1462,6 @@ void MeshGenerator::process()
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshGenerator::generate()
|
|
||||||
{
|
|
||||||
if (nullptr == m_snapshot)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_isSuccessful = true;
|
|
||||||
|
|
||||||
QElapsedTimer countTimeConsumed;
|
|
||||||
countTimeConsumed.start();
|
|
||||||
|
|
||||||
m_outcome = new Outcome;
|
|
||||||
m_outcome->meshId = m_id;
|
|
||||||
//m_cutFaceTransforms = new std::map<QUuid, nodemesh::Builder::CutFaceTransform>;
|
|
||||||
//m_nodesCutFaces = new std::map<QUuid, std::map<QString, QVector2D>>;
|
|
||||||
|
|
||||||
bool needDeleteCacheContext = false;
|
|
||||||
if (nullptr == m_cacheContext) {
|
|
||||||
m_cacheContext = new GeneratedCacheContext;
|
|
||||||
needDeleteCacheContext = true;
|
|
||||||
} else {
|
|
||||||
m_cacheEnabled = true;
|
|
||||||
for (auto it = m_cacheContext->parts.begin(); it != m_cacheContext->parts.end(); ) {
|
|
||||||
if (m_snapshot->parts.find(it->first) == m_snapshot->parts.end()) {
|
|
||||||
auto mirrorFrom = m_cacheContext->partMirrorIdMap.find(it->first);
|
|
||||||
if (mirrorFrom != m_cacheContext->partMirrorIdMap.end()) {
|
|
||||||
if (m_snapshot->parts.find(mirrorFrom->second) != m_snapshot->parts.end()) {
|
|
||||||
it++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
m_cacheContext->partMirrorIdMap.erase(mirrorFrom);
|
|
||||||
}
|
|
||||||
it->second.releaseMeshes();
|
|
||||||
it = m_cacheContext->parts.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
for (auto it = m_cacheContext->components.begin(); it != m_cacheContext->components.end(); ) {
|
|
||||||
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
|
||||||
for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) {
|
|
||||||
if (-1 != combinationIt->first.indexOf(it->first)) {
|
|
||||||
//qDebug() << "Removed cached combination:" << combinationIt->first;
|
|
||||||
delete combinationIt->second;
|
|
||||||
combinationIt = m_cacheContext->cachedCombination.erase(combinationIt);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
combinationIt++;
|
|
||||||
}
|
|
||||||
it->second.releaseMeshes();
|
|
||||||
it = m_cacheContext->components.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
collectParts();
|
|
||||||
checkDirtyFlags();
|
|
||||||
|
|
||||||
for (const auto &dirtyComponentId: m_dirtyComponentIds) {
|
|
||||||
for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) {
|
|
||||||
if (-1 != combinationIt->first.indexOf(dirtyComponentId)) {
|
|
||||||
//qDebug() << "Removed dirty cached combination:" << combinationIt->first;
|
|
||||||
delete combinationIt->second;
|
|
||||||
combinationIt = m_cacheContext->cachedCombination.erase(combinationIt);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
combinationIt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dirtyComponentIds.insert(QUuid().toString());
|
|
||||||
|
|
||||||
m_mainProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originX").toFloat();
|
|
||||||
m_mainProfileMiddleY = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originY").toFloat();
|
|
||||||
m_sideProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originZ").toFloat();
|
|
||||||
|
|
||||||
bool remeshed = componentRemeshed(&m_snapshot->canvas);
|
|
||||||
|
|
||||||
CombineMode combineMode;
|
|
||||||
auto combinedMesh = combineComponentMesh(QUuid().toString(), &combineMode);
|
|
||||||
|
|
||||||
const auto &componentCache = m_cacheContext->components[QUuid().toString()];
|
|
||||||
|
|
||||||
m_outcome->nodes = componentCache.outcomeNodes;
|
|
||||||
m_outcome->edges = componentCache.outcomeEdges;
|
|
||||||
m_outcome->paintMaps = componentCache.outcomePaintMaps;
|
|
||||||
m_outcome->nodeVertices = componentCache.outcomeNodeVertices;
|
|
||||||
|
|
||||||
std::vector<QVector3D> combinedVertices;
|
|
||||||
std::vector<std::vector<size_t>> combinedFaces;
|
|
||||||
if (nullptr != combinedMesh) {
|
|
||||||
combinedMesh->fetch(combinedVertices, combinedFaces);
|
|
||||||
if (m_weldEnabled) {
|
|
||||||
if (!remeshed) {
|
|
||||||
size_t totalAffectedNum = 0;
|
|
||||||
size_t affectedNum = 0;
|
|
||||||
do {
|
|
||||||
std::vector<QVector3D> weldedVertices;
|
|
||||||
std::vector<std::vector<size_t>> weldedFaces;
|
|
||||||
affectedNum = weldSeam(combinedVertices, combinedFaces,
|
|
||||||
0.025, componentCache.noneSeamVertices,
|
|
||||||
weldedVertices, weldedFaces);
|
|
||||||
combinedVertices = weldedVertices;
|
|
||||||
combinedFaces = weldedFaces;
|
|
||||||
totalAffectedNum += affectedNum;
|
|
||||||
} while (affectedNum > 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
recoverQuads(combinedVertices, combinedFaces, componentCache.sharedQuadEdges, m_outcome->triangleAndQuads);
|
|
||||||
m_outcome->vertices = combinedVertices;
|
|
||||||
m_outcome->triangles = combinedFaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively check uncombined components
|
|
||||||
collectUncombinedComponent(QUuid().toString());
|
|
||||||
collectIncombinableComponentMeshes(QUuid().toString());
|
|
||||||
|
|
||||||
// Fetch nodes as body nodes before cloth nodes collecting
|
|
||||||
std::set<std::pair<QUuid, QUuid>> bodyNodeMap;
|
|
||||||
m_outcome->bodyNodes.reserve(m_outcome->nodes.size());
|
|
||||||
for (const auto &it: m_outcome->nodes) {
|
|
||||||
if (it.joined) {
|
|
||||||
bodyNodeMap.insert({it.partId, it.nodeId});
|
|
||||||
m_outcome->bodyNodes.push_back(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_outcome->bodyEdges.reserve(m_outcome->edges.size());
|
|
||||||
for (const auto &it: m_outcome->edges) {
|
|
||||||
if (bodyNodeMap.find(it.first) == bodyNodeMap.end())
|
|
||||||
continue;
|
|
||||||
if (bodyNodeMap.find(it.second) == bodyNodeMap.end())
|
|
||||||
continue;
|
|
||||||
m_outcome->bodyEdges.push_back(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
collectClothComponent(QUuid().toString());
|
|
||||||
collectErroredParts();
|
|
||||||
postprocessOutcome(m_outcome);
|
|
||||||
|
|
||||||
m_resultMesh = new Model(*m_outcome);
|
|
||||||
|
|
||||||
delete combinedMesh;
|
|
||||||
|
|
||||||
if (needDeleteCacheContext) {
|
|
||||||
delete m_cacheContext;
|
|
||||||
m_cacheContext = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "The mesh generation took" << countTimeConsumed.elapsed() << "milliseconds";
|
|
||||||
}
|
|
||||||
|
|
||||||
void MeshGenerator::setWeldEnabled(bool enabled)
|
void MeshGenerator::setWeldEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
m_weldEnabled = enabled;
|
m_weldEnabled = enabled;
|
||||||
|
@ -1891,3 +1759,244 @@ void MeshGenerator::setDefaultPartColor(const QColor &color)
|
||||||
{
|
{
|
||||||
m_defaultPartColor = color;
|
m_defaultPartColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString MeshGenerator::reverseUuid(const QString &uuidString)
|
||||||
|
{
|
||||||
|
QUuid uuid(uuidString);
|
||||||
|
QString newRawId = uuid.toString(QUuid::Id128);
|
||||||
|
std::reverse(newRawId.begin(), newRawId.end());
|
||||||
|
return "{" + newRawId.mid(0, 8) + "-" +
|
||||||
|
newRawId.mid(8, 4) + "-" +
|
||||||
|
newRawId.mid(12, 4) + "-" +
|
||||||
|
newRawId.mid(16, 4) + "-" +
|
||||||
|
newRawId.mid(20, 12) + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
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["_xFlip"] = "true";
|
||||||
|
mirroredPart["_xMirroredFrom"] = mirroredPart["id"];
|
||||||
|
mirroredPart["id"] = newPartIdString;
|
||||||
|
mirroredPart["dirty"] = "true";
|
||||||
|
newParts.push_back(mirroredPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
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"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshGenerator::generate()
|
||||||
|
{
|
||||||
|
if (nullptr == m_snapshot)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_isSuccessful = true;
|
||||||
|
|
||||||
|
QElapsedTimer countTimeConsumed;
|
||||||
|
countTimeConsumed.start();
|
||||||
|
|
||||||
|
m_mainProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originX").toFloat();
|
||||||
|
m_mainProfileMiddleY = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originY").toFloat();
|
||||||
|
m_sideProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originZ").toFloat();
|
||||||
|
|
||||||
|
preprocessMirror();
|
||||||
|
|
||||||
|
m_outcome = new Outcome;
|
||||||
|
m_outcome->meshId = m_id;
|
||||||
|
//m_cutFaceTransforms = new std::map<QUuid, nodemesh::Builder::CutFaceTransform>;
|
||||||
|
//m_nodesCutFaces = new std::map<QUuid, std::map<QString, QVector2D>>;
|
||||||
|
|
||||||
|
bool needDeleteCacheContext = false;
|
||||||
|
if (nullptr == m_cacheContext) {
|
||||||
|
m_cacheContext = new GeneratedCacheContext;
|
||||||
|
needDeleteCacheContext = true;
|
||||||
|
} else {
|
||||||
|
m_cacheEnabled = true;
|
||||||
|
for (auto it = m_cacheContext->parts.begin(); it != m_cacheContext->parts.end(); ) {
|
||||||
|
if (m_snapshot->parts.find(it->first) == m_snapshot->parts.end()) {
|
||||||
|
auto mirrorFrom = m_cacheContext->partMirrorIdMap.find(it->first);
|
||||||
|
if (mirrorFrom != m_cacheContext->partMirrorIdMap.end()) {
|
||||||
|
if (m_snapshot->parts.find(mirrorFrom->second) != m_snapshot->parts.end()) {
|
||||||
|
it++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
m_cacheContext->partMirrorIdMap.erase(mirrorFrom);
|
||||||
|
}
|
||||||
|
it->second.releaseMeshes();
|
||||||
|
it = m_cacheContext->parts.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
for (auto it = m_cacheContext->components.begin(); it != m_cacheContext->components.end(); ) {
|
||||||
|
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
||||||
|
for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) {
|
||||||
|
if (-1 != combinationIt->first.indexOf(it->first)) {
|
||||||
|
//qDebug() << "Removed cached combination:" << combinationIt->first;
|
||||||
|
delete combinationIt->second;
|
||||||
|
combinationIt = m_cacheContext->cachedCombination.erase(combinationIt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
combinationIt++;
|
||||||
|
}
|
||||||
|
it->second.releaseMeshes();
|
||||||
|
it = m_cacheContext->components.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collectParts();
|
||||||
|
checkDirtyFlags();
|
||||||
|
|
||||||
|
for (const auto &dirtyComponentId: m_dirtyComponentIds) {
|
||||||
|
for (auto combinationIt = m_cacheContext->cachedCombination.begin(); combinationIt != m_cacheContext->cachedCombination.end(); ) {
|
||||||
|
if (-1 != combinationIt->first.indexOf(dirtyComponentId)) {
|
||||||
|
//qDebug() << "Removed dirty cached combination:" << combinationIt->first;
|
||||||
|
delete combinationIt->second;
|
||||||
|
combinationIt = m_cacheContext->cachedCombination.erase(combinationIt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
combinationIt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dirtyComponentIds.insert(QUuid().toString());
|
||||||
|
|
||||||
|
bool remeshed = componentRemeshed(&m_snapshot->canvas);
|
||||||
|
|
||||||
|
CombineMode combineMode;
|
||||||
|
auto combinedMesh = combineComponentMesh(QUuid().toString(), &combineMode);
|
||||||
|
|
||||||
|
const auto &componentCache = m_cacheContext->components[QUuid().toString()];
|
||||||
|
|
||||||
|
m_outcome->nodes = componentCache.outcomeNodes;
|
||||||
|
m_outcome->edges = componentCache.outcomeEdges;
|
||||||
|
m_outcome->paintMaps = componentCache.outcomePaintMaps;
|
||||||
|
m_outcome->nodeVertices = componentCache.outcomeNodeVertices;
|
||||||
|
|
||||||
|
std::vector<QVector3D> combinedVertices;
|
||||||
|
std::vector<std::vector<size_t>> combinedFaces;
|
||||||
|
if (nullptr != combinedMesh) {
|
||||||
|
combinedMesh->fetch(combinedVertices, combinedFaces);
|
||||||
|
if (m_weldEnabled) {
|
||||||
|
if (!remeshed) {
|
||||||
|
size_t totalAffectedNum = 0;
|
||||||
|
size_t affectedNum = 0;
|
||||||
|
do {
|
||||||
|
std::vector<QVector3D> weldedVertices;
|
||||||
|
std::vector<std::vector<size_t>> weldedFaces;
|
||||||
|
affectedNum = weldSeam(combinedVertices, combinedFaces,
|
||||||
|
0.025, componentCache.noneSeamVertices,
|
||||||
|
weldedVertices, weldedFaces);
|
||||||
|
combinedVertices = weldedVertices;
|
||||||
|
combinedFaces = weldedFaces;
|
||||||
|
totalAffectedNum += affectedNum;
|
||||||
|
} while (affectedNum > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recoverQuads(combinedVertices, combinedFaces, componentCache.sharedQuadEdges, m_outcome->triangleAndQuads);
|
||||||
|
m_outcome->vertices = combinedVertices;
|
||||||
|
m_outcome->triangles = combinedFaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively check uncombined components
|
||||||
|
collectUncombinedComponent(QUuid().toString());
|
||||||
|
collectIncombinableComponentMeshes(QUuid().toString());
|
||||||
|
|
||||||
|
// Fetch nodes as body nodes before cloth nodes collecting
|
||||||
|
std::set<std::pair<QUuid, QUuid>> bodyNodeMap;
|
||||||
|
m_outcome->bodyNodes.reserve(m_outcome->nodes.size());
|
||||||
|
for (const auto &it: m_outcome->nodes) {
|
||||||
|
if (it.joined) {
|
||||||
|
bodyNodeMap.insert({it.partId, it.nodeId});
|
||||||
|
m_outcome->bodyNodes.push_back(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_outcome->bodyEdges.reserve(m_outcome->edges.size());
|
||||||
|
for (const auto &it: m_outcome->edges) {
|
||||||
|
if (bodyNodeMap.find(it.first) == bodyNodeMap.end())
|
||||||
|
continue;
|
||||||
|
if (bodyNodeMap.find(it.second) == bodyNodeMap.end())
|
||||||
|
continue;
|
||||||
|
m_outcome->bodyEdges.push_back(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
collectClothComponent(QUuid().toString());
|
||||||
|
collectErroredParts();
|
||||||
|
postprocessOutcome(m_outcome);
|
||||||
|
|
||||||
|
m_resultMesh = new Model(*m_outcome);
|
||||||
|
|
||||||
|
delete combinedMesh;
|
||||||
|
|
||||||
|
if (needDeleteCacheContext) {
|
||||||
|
delete m_cacheContext;
|
||||||
|
m_cacheContext = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "The mesh generation took" << countTimeConsumed.elapsed() << "milliseconds";
|
||||||
|
}
|
||||||
|
|
|
@ -186,6 +186,8 @@ private:
|
||||||
std::vector<std::pair<QVector3D, std::pair<QUuid, QUuid>>> *outputNodeVertices);
|
std::vector<std::pair<QVector3D, std::pair<QUuid, QUuid>>> *outputNodeVertices);
|
||||||
void postprocessOutcome(Outcome *outcome);
|
void postprocessOutcome(Outcome *outcome);
|
||||||
void collectErroredParts();
|
void collectErroredParts();
|
||||||
|
void preprocessMirror();
|
||||||
|
QString reverseUuid(const QString &uuidString);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue