From 9ed4c29b39a805a08eee671c74cba23d329a2556 Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Sun, 18 Aug 2019 21:32:39 +0930 Subject: [PATCH] Add hollow thickness parameter --- languages/dust3d_zh_CN.ts | 4 ++ src/document.cpp | 20 +++++++++ src/document.h | 2 + src/meshgenerator.cpp | 7 ++++ src/partwidget.cpp | 24 +++++++++++ src/partwidget.h | 1 + src/skeletondocument.h | 8 +++- thirdparty/nodemesh/nodemesh/builder.cpp | 52 +++++++++++++++++++++++- thirdparty/nodemesh/nodemesh/builder.h | 4 ++ 9 files changed, 120 insertions(+), 2 deletions(-) diff --git a/languages/dust3d_zh_CN.ts b/languages/dust3d_zh_CN.ts index 3bbe2c52..f9dca8b7 100644 --- a/languages/dust3d_zh_CN.ts +++ b/languages/dust3d_zh_CN.ts @@ -747,6 +747,10 @@ Tips: Map Scale 映像缩放比 + + Hollow + 空心 + PoseEditWidget diff --git a/src/document.cpp b/src/document.cpp index 7d458d88..ee0dbed6 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -1069,6 +1069,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set &limitNodeId part["deformMapImageId"] = partIt.second.deformMapImageId.toString(); if (partIt.second.deformMapScaleAdjusted()) part["deformMapScale"] = QString::number(partIt.second.deformMapScale); + if (partIt.second.hollowThicknessAdjusted()) + part["hollowThickness"] = QString::number(partIt.second.hollowThickness); if (!partIt.second.name.isEmpty()) part["name"] = partIt.second.name; if (partIt.second.materialAdjusted()) @@ -1367,6 +1369,9 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste) const auto &deformMapScaleIt = partKv.second.find("deformMapScale"); if (deformMapScaleIt != partKv.second.end()) part.deformMapScale = deformMapScaleIt->second.toFloat(); + const auto &hollowThicknessIt = partKv.second.find("hollowThickness"); + if (hollowThicknessIt != partKv.second.end()) + part.hollowThickness = hollowThicknessIt->second.toFloat(); const auto &materialIdIt = partKv.second.find("materialId"); if (materialIdIt != partKv.second.end()) part.materialId = oldNewIdMap[QUuid(materialIdIt->second)]; @@ -2713,6 +2718,21 @@ void Document::setPartColorSolubility(QUuid partId, float solubility) emit skeletonChanged(); } +void Document::setPartHollowThickness(QUuid partId, float hollowThickness) +{ + auto part = partMap.find(partId); + if (part == partMap.end()) { + qDebug() << "Part not found:" << partId; + return; + } + if (qFuzzyCompare(part->second.hollowThickness, hollowThickness)) + return; + part->second.hollowThickness = hollowThickness; + part->second.dirty = true; + emit partHollowThicknessChanged(partId); + emit skeletonChanged(); +} + void Document::setPartCutRotation(QUuid partId, float cutRotation) { auto part = partMap.find(partId); diff --git a/src/document.h b/src/document.h index 0b1eabc8..dc05289c 100644 --- a/src/document.h +++ b/src/document.h @@ -422,6 +422,7 @@ signals: void partChamferStateChanged(QUuid partId); void partTargetChanged(QUuid partId); void partColorSolubilityChanged(QUuid partId); + void partHollowThicknessChanged(QUuid partId); void componentCombineModeChanged(QUuid componentId); void cleanup(); void cleanupScript(); @@ -609,6 +610,7 @@ public slots: void setPartChamferState(QUuid partId, bool chamfered); void setPartTarget(QUuid partId, PartTarget target); void setPartColorSolubility(QUuid partId, float solubility); + void setPartHollowThickness(QUuid partId, float hollowThickness); void setComponentCombineMode(QUuid componentId, CombineMode combineMode); void moveComponentUp(QUuid componentId); void moveComponentDown(QUuid componentId); diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index 9ced6701..228f9a86 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -338,6 +338,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt float deformThickness = 1.0; float deformWidth = 1.0; float cutRotation = 0.0; + float hollowThickness = 0.0; auto target = PartTargetFromString(valueOfKeyInMapOrEmpty(part, "target").toUtf8().constData()); auto base = PartBaseFromString(valueOfKeyInMapOrEmpty(part, "base").toUtf8().constData()); @@ -352,6 +353,11 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt cutRotation = cutRotationString.toFloat(); } + QString hollowThicknessString = valueOfKeyInMapOrEmpty(part, "hollowThickness"); + if (!hollowThicknessString.isEmpty()) { + hollowThickness = hollowThicknessString.toFloat(); + } + QString thicknessString = valueOfKeyInMapOrEmpty(part, "deformThickness"); if (!thicknessString.isEmpty()) { deformThickness = thicknessString.toFloat(); @@ -559,6 +565,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt builder->setDeformThickness(deformThickness); builder->setDeformWidth(deformWidth); builder->setDeformMapScale(deformMapScale); + builder->setHollowThickness(hollowThickness); if (nullptr != deformImage) builder->setDeformMapImage(deformImage); if (PartBase::YZ == base) { diff --git a/src/partwidget.cpp b/src/partwidget.cpp index fddf1a28..cd8a9e2c 100644 --- a/src/partwidget.cpp +++ b/src/partwidget.cpp @@ -171,6 +171,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) : connect(this, &PartWidget::setPartColorState, m_document, &Document::setPartColorState); connect(this, &PartWidget::setPartMaterialId, m_document, &Document::setPartMaterialId); connect(this, &PartWidget::setPartColorSolubility, m_document, &Document::setPartColorSolubility); + connect(this, &PartWidget::setPartHollowThickness, m_document, &Document::setPartHollowThickness); connect(this, &PartWidget::checkPart, m_document, &Document::checkPart); connect(this, &PartWidget::enableBackgroundBlur, m_document, &Document::enableBackgroundBlur); connect(this, &PartWidget::disableBackgroundBlur, m_document, &Document::disableBackgroundBlur); @@ -465,6 +466,28 @@ void PartWidget::showCutRotationSettingPopup(const QPoint &pos) rotationLayout->addWidget(rotationEraser); rotationLayout->addWidget(rotationWidget); + FloatNumberWidget *hollowThicknessWidget = new FloatNumberWidget; + hollowThicknessWidget->setItemName(tr("Hollow")); + hollowThicknessWidget->setRange(0.0, 1.0); + hollowThicknessWidget->setValue(part->hollowThickness); + + connect(hollowThicknessWidget, &FloatNumberWidget::valueChanged, [=](float value) { + emit setPartHollowThickness(m_partId, value); + emit groupOperationAdded(); + }); + + QPushButton *hollowThicknessEraser = new QPushButton(QChar(fa::eraser)); + initToolButton(hollowThicknessEraser); + + connect(hollowThicknessEraser, &QPushButton::clicked, [=]() { + hollowThicknessWidget->setValue(0.0); + emit groupOperationAdded(); + }); + + QHBoxLayout *hollowThicknessLayout = new QHBoxLayout; + hollowThicknessLayout->addWidget(hollowThicknessEraser); + hollowThicknessLayout->addWidget(hollowThicknessWidget); + QHBoxLayout *standardFacesLayout = new QHBoxLayout; QPushButton *buttons[(int)CutFace::Count] = {0}; @@ -525,6 +548,7 @@ void PartWidget::showCutRotationSettingPopup(const QPoint &pos) QVBoxLayout *popupLayout = new QVBoxLayout; popupLayout->addLayout(rotationLayout); + popupLayout->addLayout(hollowThicknessLayout); popupLayout->addSpacing(10); popupLayout->addLayout(standardFacesLayout); popupLayout->addWidget(cutFaceListWidget); diff --git a/src/partwidget.h b/src/partwidget.h index 0ca439d4..6244ebc8 100644 --- a/src/partwidget.h +++ b/src/partwidget.h @@ -28,6 +28,7 @@ signals: void setPartCutFaceLinkedId(QUuid partId, QUuid linkedId); void setPartMaterialId(QUuid partId, QUuid materialId); void setPartColorSolubility(QUuid partId, float colorSolubility); + void setPartHollowThickness(QUuid partId, float hollowThickness); void movePartUp(QUuid partId); void movePartDown(QUuid partId); void movePartToTop(QUuid partId); diff --git a/src/skeletondocument.h b/src/skeletondocument.h index 61cb91f5..a845c13e 100644 --- a/src/skeletondocument.h +++ b/src/skeletondocument.h @@ -136,6 +136,7 @@ public: float colorSolubility; float deformMapScale; QUuid deformMapImageId; + float hollowThickness; SkeletonPart(const QUuid &withId=QUuid()) : visible(true), locked(false), @@ -155,7 +156,8 @@ public: cutFace(CutFace::Quad), target(PartTarget::Model), colorSolubility(0.0), - deformMapScale(1.0) + deformMapScale(1.0), + hollowThickness(0.0) { id = withId.isNull() ? QUuid::createUuid() : withId; } @@ -225,6 +227,10 @@ public: { return fabs(cutRotation - 0.0) >= 0.01; } + bool hollowThicknessAdjusted() const + { + return fabs(hollowThickness - 0.0) >= 0.01; + } bool cutFaceAdjusted() const { return cutFace != CutFace::Quad; diff --git a/thirdparty/nodemesh/nodemesh/builder.cpp b/thirdparty/nodemesh/nodemesh/builder.cpp index c54a8958..a48effeb 100644 --- a/thirdparty/nodemesh/nodemesh/builder.cpp +++ b/thirdparty/nodemesh/nodemesh/builder.cpp @@ -422,6 +422,7 @@ bool Builder::build() applyWeld(); applyDeform(); + finalizeHollow(); return succeed; } @@ -609,7 +610,11 @@ bool Builder::generateCutsForNode(size_t nodeIndex) node.hasAdjustableCutFace = true; std::vector vertices; insertCutVertices(cut, vertices, nodeIndex, cutNormal, cutFlipped); - m_generatedFaces.push_back(vertices); + if (qFuzzyIsNull(m_hollowThickness)) { + m_generatedFaces.push_back(vertices); + } else { + m_endCuts.push_back(vertices); + } m_edges[node.edges[0]].cuts.push_back({vertices, -cutNormal}); } else if (2 == neighborsCount) { QVector3D cutNormal = node.cutNormal; @@ -1014,6 +1019,11 @@ void Builder::setDeformMapImage(const QImage *image) m_deformMapImage = image; } +void Builder::setHollowThickness(float hollowThickness) +{ + m_hollowThickness = hollowThickness; +} + void Builder::setDeformMapScale(float scale) { m_deformMapScale = scale; @@ -1027,6 +1037,46 @@ QVector3D Builder::calculateDeformPosition(const QVector3D &vertexPosition, cons return vertexPosition + (scaledProjct - projectRayOnRevisedNormal); } +void Builder::finalizeHollow() +{ + if (qFuzzyIsNull(m_hollowThickness)) + return; + + size_t startVertexIndex = m_generatedVertices.size(); + for (size_t i = 0; i < startVertexIndex; ++i) { + const auto &position = m_generatedVertices[i]; + const auto &node = m_nodes[m_generatedVerticesSourceNodeIndices[i]]; + auto ray = position - node.position; + + auto newPosition = position - ray * m_hollowThickness; + m_generatedVertices.push_back(newPosition); + m_generatedVerticesCutDirects.push_back(m_generatedVerticesCutDirects[i]); + m_generatedVerticesSourceNodeIndices.push_back(m_generatedVerticesSourceNodeIndices[i]); + m_generatedVerticesInfos.push_back(m_generatedVerticesInfos[i]); + } + + size_t oldFaceNum = m_generatedFaces.size(); + for (size_t i = 0; i < oldFaceNum; ++i) { + auto newFace = m_generatedFaces[i]; + std::reverse(newFace.begin(), newFace.end()); + for (auto &it: newFace) + it += startVertexIndex; + m_generatedFaces.push_back(newFace); + } + + for (const auto &cut: m_endCuts) { + for (size_t i = 0; i < cut.size(); ++i) { + size_t j = (i + 1) % cut.size(); + std::vector quad; + quad.push_back(cut[i]); + quad.push_back(cut[j]); + quad.push_back(startVertexIndex + cut[j]); + quad.push_back(startVertexIndex + cut[i]); + m_generatedFaces.push_back(quad); + } + } +} + void Builder::applyDeform() { for (size_t i = 0; i < m_generatedVertices.size(); ++i) { diff --git a/thirdparty/nodemesh/nodemesh/builder.h b/thirdparty/nodemesh/nodemesh/builder.h index c2f5dbc4..1f63c258 100644 --- a/thirdparty/nodemesh/nodemesh/builder.h +++ b/thirdparty/nodemesh/nodemesh/builder.h @@ -31,6 +31,7 @@ public: void setDeformWidth(float width); void setDeformMapImage(const QImage *image); void setDeformMapScale(float scale); + void setHollowThickness(float hollowThickness); void enableBaseNormalOnX(bool enabled); void enableBaseNormalOnY(bool enabled); void enableBaseNormalOnZ(bool enabled); @@ -133,6 +134,8 @@ private: bool m_baseNormalAverageEnabled = false; const QImage *m_deformMapImage = nullptr; float m_deformMapScale = 0.0; + float m_hollowThickness = 0.2; + std::vector> m_endCuts; void sortNodeIndices(); void prepareNode(size_t nodeIndex); @@ -168,6 +171,7 @@ private: void stitchEdgeCuts(); void applyWeld(); void applyDeform(); + void finalizeHollow(); QVector3D calculateDeformPosition(const QVector3D &vertexPosition, const QVector3D &ray, const QVector3D &deformNormal, float deformFactor); bool swallowEdgeForNode(size_t nodeIndex, size_t edgeOrder); static QVector3D calculateBaseNormalFromTraverseDirection(const QVector3D &traverseDirection);