From bb19489ee18736bddc39fd7382b205e2a0d14462 Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Mon, 20 May 2019 23:08:01 +0930 Subject: [PATCH] Add base plane constrain settings Mesh layout and thickness adjustment are depend on base plane calculation. In this build, the base plane calculation could be limited to front plane, side plane, and so on, compare to without any limitation in previous implementation. --- dust3d.pro | 3 ++ src/document.cpp | 21 +++++++- src/document.h | 2 + src/documentwindow.cpp | 1 + src/meshgenerator.cpp | 9 ++++ src/partbase.cpp | 5 ++ src/partbase.h | 64 ++++++++++++++++++++++++ src/parttreewidget.cpp | 15 ++++++ src/parttreewidget.h | 1 + src/skeletondocument.h | 4 ++ thirdparty/nodemesh/nodemesh/builder.cpp | 39 ++++++++++++++- thirdparty/nodemesh/nodemesh/builder.h | 10 +++- 12 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 src/partbase.cpp create mode 100644 src/partbase.h diff --git a/dust3d.pro b/dust3d.pro index e094470f..5e548f07 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -298,6 +298,9 @@ HEADERS += src/cutface.h SOURCES += src/parttarget.cpp HEADERS += src/parttarget.h +SOURCES += src/partbase.cpp +HEADERS += src/partbase.h + SOURCES += src/cutfacewidget.cpp HEADERS += src/cutfacewidget.h diff --git a/src/document.cpp b/src/document.cpp index 86236b01..74e2891f 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -893,7 +893,10 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set &limitNodeId part["subdived"] = partIt.second.subdived ? "true" : "false"; part["disabled"] = partIt.second.disabled ? "true" : "false"; part["xMirrored"] = partIt.second.xMirrored ? "true" : "false"; - part["zMirrored"] = partIt.second.zMirrored ? "true" : "false"; + if (partIt.second.zMirrored) + part["zMirrored"] = partIt.second.zMirrored ? "true" : "false"; + if (partIt.second.base != PartBase::XYZ) + part["base"] = PartBaseToString(partIt.second.base); part["rounded"] = partIt.second.rounded ? "true" : "false"; part["chamfered"] = partIt.second.chamfered ? "true" : "false"; if (PartTarget::Model != partIt.second.target) @@ -1163,6 +1166,7 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste) part.disabled = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "disabled")); part.xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "xMirrored")); part.zMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "zMirrored")); + part.base = PartBaseFromString(valueOfKeyInMapOrEmpty(partKv.second, "base").toUtf8().constData()); part.rounded = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "rounded")); part.chamfered = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "chamfered")); part.target = PartTargetFromString(valueOfKeyInMapOrEmpty(partKv.second, "target").toUtf8().constData()); @@ -2219,6 +2223,21 @@ void Document::setPartDeformThickness(QUuid partId, float thickness) emit skeletonChanged(); } +void Document::setPartBase(QUuid partId, PartBase base) +{ + auto part = partMap.find(partId); + if (part == partMap.end()) { + qDebug() << "Part not found:" << partId; + return; + } + if (part->second.base == base) + return; + part->second.base = base; + part->second.dirty = true; + emit partBaseChanged(partId); + emit skeletonChanged(); +} + void Document::setPartDeformWidth(QUuid partId, float width) { auto part = partMap.find(partId); diff --git a/src/document.h b/src/document.h index abf1d414..9b970ee2 100644 --- a/src/document.h +++ b/src/document.h @@ -396,6 +396,7 @@ signals: void partDisableStateChanged(QUuid partId); void partXmirrorStateChanged(QUuid partId); //void partZmirrorStateChanged(QUuid partId); + void partBaseChanged(QUuid partId); void partDeformThicknessChanged(QUuid partId); void partDeformWidthChanged(QUuid partId); void partRoundStateChanged(QUuid partId); @@ -553,6 +554,7 @@ public slots: void setPartDisableState(QUuid partId, bool disabled); void setPartXmirrorState(QUuid partId, bool mirrored); //void setPartZmirrorState(QUuid partId, bool mirrored); + void setPartBase(QUuid partId, PartBase base); void setPartDeformThickness(QUuid partId, float thickness); void setPartDeformWidth(QUuid partId, float width); void setPartRoundState(QUuid partId, bool rounded); diff --git a/src/documentwindow.cpp b/src/documentwindow.cpp index f81179a3..bb2794be 100644 --- a/src/documentwindow.cpp +++ b/src/documentwindow.cpp @@ -832,6 +832,7 @@ DocumentWindow::DocumentWindow() : connect(partTreeWidget, &PartTreeWidget::setPartVisibleState, m_document, &Document::setPartVisibleState); connect(partTreeWidget, &PartTreeWidget::setComponentCombineMode, m_document, &Document::setComponentCombineMode); connect(partTreeWidget, &PartTreeWidget::setPartTarget, m_document, &Document::setPartTarget); + connect(partTreeWidget, &PartTreeWidget::setPartBase, m_document, &Document::setPartBase); connect(partTreeWidget, &PartTreeWidget::hideDescendantComponents, m_document, &Document::hideDescendantComponents); connect(partTreeWidget, &PartTreeWidget::showDescendantComponents, m_document, &Document::showDescendantComponents); connect(partTreeWidget, &PartTreeWidget::lockDescendantComponents, m_document, &Document::lockDescendantComponents); diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index 011b1e41..c83a36a2 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -13,6 +13,7 @@ #include "cutface.h" #include "parttarget.h" #include "theme.h" +#include "partbase.h" MeshGenerator::MeshGenerator(Snapshot *snapshot) : m_snapshot(snapshot) @@ -171,6 +172,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt float deformWidth = 1.0; float cutRotation = 0.0; auto target = PartTargetFromString(valueOfKeyInMapOrEmpty(part, "target").toUtf8().constData()); + auto base = PartBaseFromString(valueOfKeyInMapOrEmpty(part, "base").toUtf8().constData()); std::vector cutTemplate; QString cutFaceString = valueOfKeyInMapOrEmpty(part, "cutFace"); @@ -441,6 +443,13 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt builder->setDeformThickness(deformThickness); builder->setDeformWidth(deformWidth); builder->setCutRotation(cutRotation); + if (PartBase::YZ == base) { + builder->enableBaseNormalOnX(false); + } else if (PartBase::XY == base) { + builder->enableBaseNormalOnZ(false); + } else if (PartBase::ZX == base) { + builder->enableBaseNormalOnY(false); + } for (const auto &node: modifier->nodes()) builder->addNode(node.position, node.radius, node.cutTemplate); diff --git a/src/partbase.cpp b/src/partbase.cpp new file mode 100644 index 00000000..9af066f8 --- /dev/null +++ b/src/partbase.cpp @@ -0,0 +1,5 @@ +#include "partbase.h" + +IMPL_PartBaseFromString +IMPL_PartBaseToString +IMPL_PartBaseToDispName \ No newline at end of file diff --git a/src/partbase.h b/src/partbase.h new file mode 100644 index 00000000..b80aea08 --- /dev/null +++ b/src/partbase.h @@ -0,0 +1,64 @@ +#ifndef DUST3D_PART_BASE_H +#define DUST3D_PART_BASE_H +#include +#include + +enum class PartBase +{ + XYZ = 0, + YZ, + XY, + ZX, + Count +}; +PartBase PartBaseFromString(const char *baseString); +#define IMPL_PartBaseFromString \ +PartBase PartBaseFromString(const char *baseString) \ +{ \ + QString base = baseString; \ + if (base == "XYZ") \ + return PartBase::XYZ; \ + if (base == "YZ") \ + return PartBase::YZ; \ + if (base == "XY") \ + return PartBase::XY; \ + if (base == "ZX") \ + return PartBase::ZX; \ + return PartBase::XYZ; \ +} +const char *PartBaseToString(PartBase base); +#define IMPL_PartBaseToString \ +const char *PartBaseToString(PartBase base) \ +{ \ + switch (base) { \ + case PartBase::XYZ: \ + return "XYZ"; \ + case PartBase::YZ: \ + return "YZ"; \ + case PartBase::XY: \ + return "XY"; \ + case PartBase::ZX: \ + return "ZX"; \ + default: \ + return "XYZ"; \ + } \ +} +QString PartBaseToDispName(PartBase base); +#define IMPL_PartBaseToDispName \ +QString PartBaseToDispName(PartBase base) \ +{ \ + switch (base) { \ + case PartBase::XYZ: \ + return QObject::tr("Dynamic"); \ + case PartBase::YZ: \ + return QObject::tr("Side Plane"); \ + case PartBase::XY: \ + return QObject::tr("Front Plane"); \ + case PartBase::ZX: \ + return QObject::tr("Top Plane"); \ + default: \ + return QObject::tr("Dynamic"); \ + } \ +} + +#endif diff --git a/src/parttreewidget.cpp b/src/parttreewidget.cpp index 57ef186e..52f81a13 100644 --- a/src/parttreewidget.cpp +++ b/src/parttreewidget.cpp @@ -294,6 +294,19 @@ void PartTreeWidget::showContextMenu(const QPoint &pos) }); } + QComboBox *partBaseSelectBox = nullptr; + if (nullptr != part && nullptr != partWidget) { + partBaseSelectBox = new QComboBox; + for (size_t i = 0; i < (size_t)PartBase::Count; ++i) { + PartBase base = (PartBase)i; + partBaseSelectBox->addItem(PartBaseToDispName(base)); + } + partBaseSelectBox->setCurrentIndex((int)part->base); + connect(partBaseSelectBox, static_cast(&QComboBox::currentIndexChanged), this, [=](int index) { + emit setPartBase(part->id, (PartBase)index); + }); + } + //QHBoxLayout *combineModeLayout = new QHBoxLayout; //combineModeLayout->setAlignment(Qt::AlignCenter); //combineModeLayout->setContentsMargins(0, 0, 0, 0); @@ -301,6 +314,8 @@ void PartTreeWidget::showContextMenu(const QPoint &pos) //combineModeLayout->addWidget(combineModeSelectBox); QFormLayout *componentSettingsLayout = new QFormLayout; + if (nullptr != partBaseSelectBox) + componentSettingsLayout->addRow(tr("Base"), partBaseSelectBox); if (nullptr != partTargetSelectBox) componentSettingsLayout->addRow(tr("Target"), partTargetSelectBox); componentSettingsLayout->addRow(tr("Mode"), combineModeSelectBox); diff --git a/src/parttreewidget.h b/src/parttreewidget.h index 20125617..eb6a48ea 100644 --- a/src/parttreewidget.h +++ b/src/parttreewidget.h @@ -22,6 +22,7 @@ signals: void setComponentSmoothAll(QUuid componentId, float toSmoothAll); void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam); void setPartTarget(QUuid partId, PartTarget target); + void setPartBase(QUuid partId, PartBase base); void moveComponent(QUuid componentId, QUuid toParentId); void removeComponent(QUuid componentId); void hideOtherComponents(QUuid componentId); diff --git a/src/skeletondocument.h b/src/skeletondocument.h index 8b971217..48bb7419 100644 --- a/src/skeletondocument.h +++ b/src/skeletondocument.h @@ -11,6 +11,7 @@ #include "meshloader.h" #include "cutface.h" #include "parttarget.h" +#include "partbase.h" class SkeletonNode { @@ -77,6 +78,7 @@ public: bool disabled; bool xMirrored; bool zMirrored; + PartBase base; float deformThickness; float deformWidth; bool rounded; @@ -98,6 +100,7 @@ public: disabled(false), xMirrored(false), zMirrored(false), + base(PartBase::XYZ), deformThickness(1.0), deformWidth(1.0), rounded(false), @@ -189,6 +192,7 @@ public: disabled = other.disabled; xMirrored = other.xMirrored; zMirrored = other.zMirrored; + base = other.base; deformThickness = other.deformThickness; deformWidth = other.deformWidth; rounded = other.rounded; diff --git a/thirdparty/nodemesh/nodemesh/builder.cpp b/thirdparty/nodemesh/nodemesh/builder.cpp index f4f1683c..48af2a9c 100644 --- a/thirdparty/nodemesh/nodemesh/builder.cpp +++ b/thirdparty/nodemesh/nodemesh/builder.cpp @@ -289,10 +289,45 @@ bool Builder::validateNormal(const QVector3D &normal) return true; } -std::pair Builder::calculateBaseNormal(const std::vector &directs, - const std::vector &positions, +void Builder::enableBaseNormalOnX(bool enabled) +{ + m_baseNormalOnX = enabled; +} + +void Builder::enableBaseNormalOnY(bool enabled) +{ + m_baseNormalOnY = enabled; +} + +void Builder::enableBaseNormalOnZ(bool enabled) +{ + m_baseNormalOnZ = enabled; +} + +std::pair Builder::calculateBaseNormal(const std::vector &inputDirects, + const std::vector &inputPositions, const std::vector &weights) { + std::vector directs = inputDirects; + std::vector positions = inputPositions; + if (!m_baseNormalOnX || !m_baseNormalOnY || !m_baseNormalOnZ) { + for (auto &it: directs) { + if (!m_baseNormalOnX) + it.setX(0); + if (!m_baseNormalOnY) + it.setY(0); + if (!m_baseNormalOnZ) + it.setZ(0); + } + for (auto &it: positions) { + if (!m_baseNormalOnX) + it.setX(0); + if (!m_baseNormalOnY) + it.setY(0); + if (!m_baseNormalOnZ) + it.setZ(0); + } + } auto calculateTwoPointsNormal = [&](size_t i0, size_t i1) -> std::pair { auto normal = QVector3D::crossProduct(directs[i0], directs[i1]).normalized(); if (validateNormal(normal)) { diff --git a/thirdparty/nodemesh/nodemesh/builder.h b/thirdparty/nodemesh/nodemesh/builder.h index 79893957..2b650003 100644 --- a/thirdparty/nodemesh/nodemesh/builder.h +++ b/thirdparty/nodemesh/nodemesh/builder.h @@ -17,6 +17,9 @@ public: void setDeformThickness(float thickness); void setDeformWidth(float width); void setCutRotation(float cutRotation); + void enableBaseNormalOnX(bool enabled); + void enableBaseNormalOnY(bool enabled); + void enableBaseNormalOnZ(bool enabled); const std::vector &generatedVertices(); const std::vector> &generatedFaces(); const std::vector &generatedVerticesSourceNodeIndices(); @@ -92,11 +95,14 @@ private: float m_deformThickness = 1.0; float m_deformWidth = 1.0; float m_cutRotation = 0.0; + bool m_baseNormalOnX = true; + bool m_baseNormalOnY = true; + bool m_baseNormalOnZ = true; void sortNodeIndices(); void prepareNode(size_t nodeIndex); - std::pair calculateBaseNormal(const std::vector &directs, - const std::vector &positions, + std::pair calculateBaseNormal(const std::vector &inputDirects, + const std::vector &inputPositions, const std::vector &weights); bool validateNormal(const QVector3D &normal); void resolveBaseNormalRecursively(size_t nodeIndex);