Add hollow thickness parameter

master
Jeremy Hu 2019-08-18 21:32:39 +09:30
parent 0077986201
commit 9ed4c29b39
9 changed files with 120 additions and 2 deletions

View File

@ -747,6 +747,10 @@ Tips:
<source>Map Scale</source>
<translation></translation>
</message>
<message>
<source>Hollow</source>
<translation></translation>
</message>
</context>
<context>
<name>PoseEditWidget</name>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<size_t> vertices;
insertCutVertices(cut, vertices, nodeIndex, cutNormal, cutFlipped);
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<size_t> 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) {

View File

@ -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<std::vector<size_t>> 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);