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.
master
Jeremy Hu 2019-05-20 23:08:01 +09:30
parent b09df89da2
commit bb19489ee1
12 changed files with 169 additions and 5 deletions

View File

@ -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

View File

@ -893,7 +893,10 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &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);

View File

@ -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);

View File

@ -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);

View File

@ -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<QVector2D> 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);

5
src/partbase.cpp Normal file
View File

@ -0,0 +1,5 @@
#include "partbase.h"
IMPL_PartBaseFromString
IMPL_PartBaseToString
IMPL_PartBaseToDispName

64
src/partbase.h Normal file
View File

@ -0,0 +1,64 @@
#ifndef DUST3D_PART_BASE_H
#define DUST3D_PART_BASE_H
#include <QString>
#include <QObject>
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

View File

@ -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<void (QComboBox::*)(int)>(&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);

View File

@ -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);

View File

@ -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;

View File

@ -289,10 +289,45 @@ bool Builder::validateNormal(const QVector3D &normal)
return true;
}
std::pair<QVector3D, bool> Builder::calculateBaseNormal(const std::vector<QVector3D> &directs,
const std::vector<QVector3D> &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<QVector3D, bool> Builder::calculateBaseNormal(const std::vector<QVector3D> &inputDirects,
const std::vector<QVector3D> &inputPositions,
const std::vector<float> &weights)
{
std::vector<QVector3D> directs = inputDirects;
std::vector<QVector3D> 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<QVector3D, bool> {
auto normal = QVector3D::crossProduct(directs[i0], directs[i1]).normalized();
if (validateNormal(normal)) {

View File

@ -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<QVector3D> &generatedVertices();
const std::vector<std::vector<size_t>> &generatedFaces();
const std::vector<size_t> &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<QVector3D, bool> calculateBaseNormal(const std::vector<QVector3D> &directs,
const std::vector<QVector3D> &positions,
std::pair<QVector3D, bool> calculateBaseNormal(const std::vector<QVector3D> &inputDirects,
const std::vector<QVector3D> &inputPositions,
const std::vector<float> &weights);
bool validateNormal(const QVector3D &normal);
void resolveBaseNormalRecursively(size_t nodeIndex);