Add part countershading setting
Countershaded parts will be belly white colored.master
parent
d5d46b8337
commit
5c34ae61ce
|
@ -606,16 +606,16 @@ Tips:
|
||||||
<translation>曲线</translation>
|
<translation>曲线</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Accelerating:</source>
|
<source>Accelerating</source>
|
||||||
<translation>加速:</translation>
|
<translation>加速</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Bouncing:</source>
|
<source>Decelerating</source>
|
||||||
<translation>回弹:</translation>
|
<translation>减速</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Decelerating:</source>
|
<source>Bouncing</source>
|
||||||
<translation>减速:</translation>
|
<translation>回弹</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -807,6 +807,10 @@ Tips:
|
||||||
<source>Transparency</source>
|
<source>Transparency</source>
|
||||||
<translation>透明度</translation>
|
<translation>透明度</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Countershaded</source>
|
||||||
|
<translation>肚白效果</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PoseEditWidget</name>
|
<name>PoseEditWidget</name>
|
||||||
|
|
|
@ -1081,6 +1081,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
|
||||||
part["name"] = partIt.second.name;
|
part["name"] = partIt.second.name;
|
||||||
if (partIt.second.materialAdjusted())
|
if (partIt.second.materialAdjusted())
|
||||||
part["materialId"] = partIt.second.materialId.toString();
|
part["materialId"] = partIt.second.materialId.toString();
|
||||||
|
if (partIt.second.countershaded)
|
||||||
|
part["countershaded"] = "true";
|
||||||
snapshot->parts[part["id"]] = part;
|
snapshot->parts[part["id"]] = part;
|
||||||
}
|
}
|
||||||
for (const auto &nodeIt: nodeMap) {
|
for (const auto &nodeIt: nodeMap) {
|
||||||
|
@ -1386,6 +1388,7 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
|
||||||
const auto &materialIdIt = partKv.second.find("materialId");
|
const auto &materialIdIt = partKv.second.find("materialId");
|
||||||
if (materialIdIt != partKv.second.end())
|
if (materialIdIt != partKv.second.end())
|
||||||
part.materialId = oldNewIdMap[QUuid(materialIdIt->second)];
|
part.materialId = oldNewIdMap[QUuid(materialIdIt->second)];
|
||||||
|
part.countershaded = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "countershaded"));
|
||||||
newAddedPartIds.insert(part.id);
|
newAddedPartIds.insert(part.id);
|
||||||
}
|
}
|
||||||
for (const auto &it: cutFaceLinkedIdModifyMap) {
|
for (const auto &it: cutFaceLinkedIdModifyMap) {
|
||||||
|
@ -2744,6 +2747,21 @@ void Document::setPartHollowThickness(QUuid partId, float hollowThickness)
|
||||||
emit skeletonChanged();
|
emit skeletonChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Document::setPartCountershaded(QUuid partId, bool countershaded)
|
||||||
|
{
|
||||||
|
auto part = partMap.find(partId);
|
||||||
|
if (part == partMap.end()) {
|
||||||
|
qDebug() << "Part not found:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (part->second.countershaded == countershaded)
|
||||||
|
return;
|
||||||
|
part->second.countershaded = countershaded;
|
||||||
|
part->second.dirty = true;
|
||||||
|
emit partCountershadeStateChanged(partId);
|
||||||
|
emit textureChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void Document::setPartCutRotation(QUuid partId, float cutRotation)
|
void Document::setPartCutRotation(QUuid partId, float cutRotation)
|
||||||
{
|
{
|
||||||
auto part = partMap.find(partId);
|
auto part = partMap.find(partId);
|
||||||
|
|
|
@ -434,6 +434,7 @@ signals:
|
||||||
void partTargetChanged(QUuid partId);
|
void partTargetChanged(QUuid partId);
|
||||||
void partColorSolubilityChanged(QUuid partId);
|
void partColorSolubilityChanged(QUuid partId);
|
||||||
void partHollowThicknessChanged(QUuid partId);
|
void partHollowThicknessChanged(QUuid partId);
|
||||||
|
void partCountershadeStateChanged(QUuid partId);
|
||||||
void componentCombineModeChanged(QUuid componentId);
|
void componentCombineModeChanged(QUuid componentId);
|
||||||
void cleanup();
|
void cleanup();
|
||||||
void cleanupScript();
|
void cleanupScript();
|
||||||
|
@ -622,6 +623,7 @@ public slots:
|
||||||
void setPartTarget(QUuid partId, PartTarget target);
|
void setPartTarget(QUuid partId, PartTarget target);
|
||||||
void setPartColorSolubility(QUuid partId, float solubility);
|
void setPartColorSolubility(QUuid partId, float solubility);
|
||||||
void setPartHollowThickness(QUuid partId, float hollowThickness);
|
void setPartHollowThickness(QUuid partId, float hollowThickness);
|
||||||
|
void setPartCountershaded(QUuid partId, bool countershaded);
|
||||||
void setComponentCombineMode(QUuid componentId, CombineMode combineMode);
|
void setComponentCombineMode(QUuid componentId, CombineMode combineMode);
|
||||||
void moveComponentUp(QUuid componentId);
|
void moveComponentUp(QUuid componentId);
|
||||||
void moveComponentDown(QUuid componentId);
|
void moveComponentDown(QUuid componentId);
|
||||||
|
|
|
@ -1018,6 +1018,7 @@ DocumentWindow::DocumentWindow() :
|
||||||
connect(m_document, &Document::partHollowThicknessChanged, partTreeWidget, &PartTreeWidget::partHollowThicknessChanged);
|
connect(m_document, &Document::partHollowThicknessChanged, partTreeWidget, &PartTreeWidget::partHollowThicknessChanged);
|
||||||
connect(m_document, &Document::partMaterialIdChanged, partTreeWidget, &PartTreeWidget::partMaterialIdChanged);
|
connect(m_document, &Document::partMaterialIdChanged, partTreeWidget, &PartTreeWidget::partMaterialIdChanged);
|
||||||
connect(m_document, &Document::partColorSolubilityChanged, partTreeWidget, &PartTreeWidget::partColorSolubilityChanged);
|
connect(m_document, &Document::partColorSolubilityChanged, partTreeWidget, &PartTreeWidget::partColorSolubilityChanged);
|
||||||
|
connect(m_document, &Document::partCountershadeStateChanged, partTreeWidget, &PartTreeWidget::partCountershadeStateChanged);
|
||||||
connect(m_document, &Document::partRemoved, partTreeWidget, &PartTreeWidget::partRemoved);
|
connect(m_document, &Document::partRemoved, partTreeWidget, &PartTreeWidget::partRemoved);
|
||||||
connect(m_document, &Document::cleanup, partTreeWidget, &PartTreeWidget::removeAllContent);
|
connect(m_document, &Document::cleanup, partTreeWidget, &PartTreeWidget::removeAllContent);
|
||||||
connect(m_document, &Document::partChecked, partTreeWidget, &PartTreeWidget::partChecked);
|
connect(m_document, &Document::partChecked, partTreeWidget, &PartTreeWidget::partChecked);
|
||||||
|
|
|
@ -587,6 +587,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
||||||
const auto &originNodeIdString = nodeIndexToIdStringMap[node.originNodeIndex];
|
const auto &originNodeIdString = nodeIndexToIdStringMap[node.originNodeIndex];
|
||||||
|
|
||||||
OutcomePaintNode paintNode;
|
OutcomePaintNode paintNode;
|
||||||
|
paintNode.originNodeIndex = node.originNodeIndex;
|
||||||
paintNode.originNodeId = QUuid(originNodeIdString);
|
paintNode.originNodeId = QUuid(originNodeIdString);
|
||||||
paintNode.radius = node.radius;
|
paintNode.radius = node.radius;
|
||||||
paintNode.origin = node.position;
|
paintNode.origin = node.position;
|
||||||
|
@ -615,6 +616,8 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
||||||
paintNode.baseNormal = builder->nodeBaseNormal(i);
|
paintNode.baseNormal = builder->nodeBaseNormal(i);
|
||||||
paintNode.direction = builder->nodeTraverseDirection(i);
|
paintNode.direction = builder->nodeTraverseDirection(i);
|
||||||
paintNode.order = builder->nodeTraverseOrder(i);
|
paintNode.order = builder->nodeTraverseOrder(i);
|
||||||
|
|
||||||
|
partCache.outcomeNodes[paintNode.originNodeIndex].direction = paintNode.direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasMeshError = false;
|
bool hasMeshError = false;
|
||||||
|
|
|
@ -193,9 +193,20 @@ void MotionTimelineWidget::showInterpolationSettingPopup(int clipIndex, const QP
|
||||||
QWidget *cubicWidget = new QWidget;
|
QWidget *cubicWidget = new QWidget;
|
||||||
|
|
||||||
QCheckBox *hasAcceleratingBox = new QCheckBox();
|
QCheckBox *hasAcceleratingBox = new QCheckBox();
|
||||||
|
hasAcceleratingBox->setText(tr("Accelerating"));
|
||||||
|
Theme::initCheckbox(hasAcceleratingBox);
|
||||||
|
|
||||||
QCheckBox *hasDeceleratingBox = new QCheckBox();
|
QCheckBox *hasDeceleratingBox = new QCheckBox();
|
||||||
|
hasDeceleratingBox->setText(tr("Decelerating"));
|
||||||
|
Theme::initCheckbox(hasDeceleratingBox);
|
||||||
|
|
||||||
QCheckBox *bouncingBeginBox = new QCheckBox();
|
QCheckBox *bouncingBeginBox = new QCheckBox();
|
||||||
|
bouncingBeginBox->setText(tr("Bouncing"));
|
||||||
|
Theme::initCheckbox(bouncingBeginBox);
|
||||||
|
|
||||||
QCheckBox *bouncingEndBox = new QCheckBox();
|
QCheckBox *bouncingEndBox = new QCheckBox();
|
||||||
|
bouncingEndBox->setText(tr("Bouncing"));
|
||||||
|
Theme::initCheckbox(bouncingEndBox);
|
||||||
|
|
||||||
QStackedWidget *stackedWidget = new QStackedWidget;
|
QStackedWidget *stackedWidget = new QStackedWidget;
|
||||||
stackedWidget->addWidget(linearWidget);
|
stackedWidget->addWidget(linearWidget);
|
||||||
|
@ -271,29 +282,13 @@ void MotionTimelineWidget::showInterpolationSettingPopup(int clipIndex, const QP
|
||||||
QVBoxLayout *cubicLayout = new QVBoxLayout;
|
QVBoxLayout *cubicLayout = new QVBoxLayout;
|
||||||
|
|
||||||
QHBoxLayout *acceleratingLayout = new QHBoxLayout;
|
QHBoxLayout *acceleratingLayout = new QHBoxLayout;
|
||||||
{
|
acceleratingLayout->addWidget(hasAcceleratingBox);
|
||||||
QFormLayout *formLayout = new QFormLayout;
|
acceleratingLayout->addWidget(bouncingBeginBox);
|
||||||
formLayout->addRow(tr("Accelerating:"), hasAcceleratingBox);
|
|
||||||
acceleratingLayout->addLayout(formLayout);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
QFormLayout *formLayout = new QFormLayout;
|
|
||||||
formLayout->addRow(tr("Bouncing:"), bouncingBeginBox);
|
|
||||||
acceleratingLayout->addLayout(formLayout);
|
|
||||||
}
|
|
||||||
cubicLayout->addLayout(acceleratingLayout);
|
cubicLayout->addLayout(acceleratingLayout);
|
||||||
|
|
||||||
QHBoxLayout *deceleratingLayout = new QHBoxLayout;
|
QHBoxLayout *deceleratingLayout = new QHBoxLayout;
|
||||||
{
|
deceleratingLayout->addWidget(hasDeceleratingBox);
|
||||||
QFormLayout *formLayout = new QFormLayout;
|
deceleratingLayout->addWidget(bouncingEndBox);
|
||||||
formLayout->addRow(tr("Decelerating:"), hasDeceleratingBox);
|
|
||||||
deceleratingLayout->addLayout(formLayout);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
QFormLayout *formLayout = new QFormLayout;
|
|
||||||
formLayout->addRow(tr("Bouncing:"), bouncingEndBox);
|
|
||||||
deceleratingLayout->addLayout(formLayout);
|
|
||||||
}
|
|
||||||
cubicLayout->addLayout(deceleratingLayout);
|
cubicLayout->addLayout(deceleratingLayout);
|
||||||
|
|
||||||
cubicWidget->setLayout(cubicLayout);
|
cubicWidget->setLayout(cubicLayout);
|
||||||
|
|
|
@ -23,10 +23,12 @@ struct OutcomeNode
|
||||||
QUuid mirrorFromPartId;
|
QUuid mirrorFromPartId;
|
||||||
QUuid mirroredByPartId;
|
QUuid mirroredByPartId;
|
||||||
BoneMark boneMark;
|
BoneMark boneMark;
|
||||||
|
QVector3D direction;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OutcomePaintNode
|
struct OutcomePaintNode
|
||||||
{
|
{
|
||||||
|
int originNodeIndex;
|
||||||
QUuid originNodeId;
|
QUuid originNodeId;
|
||||||
QVector3D origin;
|
QVector3D origin;
|
||||||
float radius = 0;
|
float radius = 0;
|
||||||
|
|
|
@ -1046,6 +1046,17 @@ void PartTreeWidget::partColorSolubilityChanged(QUuid partId)
|
||||||
widget->updateColorButton();
|
widget->updateColorButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PartTreeWidget::partCountershadeStateChanged(QUuid partId)
|
||||||
|
{
|
||||||
|
auto item = m_partItemMap.find(partId);
|
||||||
|
if (item == m_partItemMap.end()) {
|
||||||
|
qDebug() << "Part item not found:" << partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PartWidget *widget = (PartWidget *)itemWidget(item->second, 0);
|
||||||
|
widget->updateColorButton();
|
||||||
|
}
|
||||||
|
|
||||||
void PartTreeWidget::partChecked(QUuid partId)
|
void PartTreeWidget::partChecked(QUuid partId)
|
||||||
{
|
{
|
||||||
auto item = m_partItemMap.find(partId);
|
auto item = m_partItemMap.find(partId);
|
||||||
|
|
|
@ -71,6 +71,7 @@ public slots:
|
||||||
void partHollowThicknessChanged(QUuid partId);
|
void partHollowThicknessChanged(QUuid partId);
|
||||||
void partMaterialIdChanged(QUuid partId);
|
void partMaterialIdChanged(QUuid partId);
|
||||||
void partColorSolubilityChanged(QUuid partId);
|
void partColorSolubilityChanged(QUuid partId);
|
||||||
|
void partCountershadeStateChanged(QUuid partId);
|
||||||
void partChecked(QUuid partId);
|
void partChecked(QUuid partId);
|
||||||
void partUnchecked(QUuid partId);
|
void partUnchecked(QUuid partId);
|
||||||
void partComponentChecked(QUuid partId);
|
void partComponentChecked(QUuid partId);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <QSizePolicy>
|
#include <QSizePolicy>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QSizePolicy>
|
#include <QSizePolicy>
|
||||||
|
#include <QCheckBox>
|
||||||
#include <nodemesh/misc.h>
|
#include <nodemesh/misc.h>
|
||||||
#include "partwidget.h"
|
#include "partwidget.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
|
@ -172,6 +173,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
||||||
connect(this, &PartWidget::setPartMaterialId, m_document, &Document::setPartMaterialId);
|
connect(this, &PartWidget::setPartMaterialId, m_document, &Document::setPartMaterialId);
|
||||||
connect(this, &PartWidget::setPartColorSolubility, m_document, &Document::setPartColorSolubility);
|
connect(this, &PartWidget::setPartColorSolubility, m_document, &Document::setPartColorSolubility);
|
||||||
connect(this, &PartWidget::setPartHollowThickness, m_document, &Document::setPartHollowThickness);
|
connect(this, &PartWidget::setPartHollowThickness, m_document, &Document::setPartHollowThickness);
|
||||||
|
connect(this, &PartWidget::setPartCountershaded, m_document, &Document::setPartCountershaded);
|
||||||
connect(this, &PartWidget::checkPart, m_document, &Document::checkPart);
|
connect(this, &PartWidget::checkPart, m_document, &Document::checkPart);
|
||||||
connect(this, &PartWidget::enableBackgroundBlur, m_document, &Document::enableBackgroundBlur);
|
connect(this, &PartWidget::enableBackgroundBlur, m_document, &Document::enableBackgroundBlur);
|
||||||
connect(this, &PartWidget::disableBackgroundBlur, m_document, &Document::disableBackgroundBlur);
|
connect(this, &PartWidget::disableBackgroundBlur, m_document, &Document::disableBackgroundBlur);
|
||||||
|
@ -361,10 +363,21 @@ void PartWidget::showColorSettingPopup(const QPoint &pos)
|
||||||
palette.setColor(QPalette::Button, choosenColor);
|
palette.setColor(QPalette::Button, choosenColor);
|
||||||
pickButton->setPalette(palette);
|
pickButton->setPalette(palette);
|
||||||
|
|
||||||
|
QCheckBox *countershadeStateBox = new QCheckBox();
|
||||||
|
Theme::initCheckbox(countershadeStateBox);
|
||||||
|
countershadeStateBox->setText(tr("Countershaded"));
|
||||||
|
countershadeStateBox->setChecked(part->countershaded);
|
||||||
|
|
||||||
|
connect(countershadeStateBox, &QCheckBox::stateChanged, this, [=]() {
|
||||||
|
emit setPartCountershaded(m_partId, countershadeStateBox->isChecked());
|
||||||
|
emit groupOperationAdded();
|
||||||
|
});
|
||||||
|
|
||||||
QHBoxLayout *colorLayout = new QHBoxLayout;
|
QHBoxLayout *colorLayout = new QHBoxLayout;
|
||||||
colorLayout->addWidget(colorEraser);
|
colorLayout->addWidget(colorEraser);
|
||||||
colorLayout->addWidget(pickButton);
|
colorLayout->addWidget(pickButton);
|
||||||
colorLayout->addStretch();
|
colorLayout->addStretch();
|
||||||
|
colorLayout->addWidget(countershadeStateBox);
|
||||||
|
|
||||||
connect(colorEraser, &QPushButton::clicked, [=]() {
|
connect(colorEraser, &QPushButton::clicked, [=]() {
|
||||||
emit setPartColorState(m_partId, false, Qt::white);
|
emit setPartColorState(m_partId, false, Qt::white);
|
||||||
|
@ -894,7 +907,7 @@ void PartWidget::updateColorButton()
|
||||||
qDebug() << "Part not found:" << m_partId;
|
qDebug() << "Part not found:" << m_partId;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (part->hasColor || part->materialAdjusted() || part->colorSolubilityAdjusted())
|
if (part->hasColor || part->materialAdjusted() || part->colorSolubilityAdjusted() || part->countershaded)
|
||||||
updateButton(m_colorButton, QChar(fa::eyedropper), true);
|
updateButton(m_colorButton, QChar(fa::eyedropper), true);
|
||||||
else
|
else
|
||||||
updateButton(m_colorButton, QChar(fa::eyedropper), false);
|
updateButton(m_colorButton, QChar(fa::eyedropper), false);
|
||||||
|
|
|
@ -29,6 +29,7 @@ signals:
|
||||||
void setPartMaterialId(QUuid partId, QUuid materialId);
|
void setPartMaterialId(QUuid partId, QUuid materialId);
|
||||||
void setPartColorSolubility(QUuid partId, float colorSolubility);
|
void setPartColorSolubility(QUuid partId, float colorSolubility);
|
||||||
void setPartHollowThickness(QUuid partId, float hollowThickness);
|
void setPartHollowThickness(QUuid partId, float hollowThickness);
|
||||||
|
void setPartCountershaded(QUuid partId, bool countershaded);
|
||||||
void movePartUp(QUuid partId);
|
void movePartUp(QUuid partId);
|
||||||
void movePartDown(QUuid partId);
|
void movePartDown(QUuid partId);
|
||||||
void movePartToTop(QUuid partId);
|
void movePartToTop(QUuid partId);
|
||||||
|
|
|
@ -58,11 +58,13 @@ PreferencesWidget::PreferencesWidget(const Document *document, QWidget *parent)
|
||||||
});
|
});
|
||||||
|
|
||||||
QCheckBox *flatShadingBox = new QCheckBox();
|
QCheckBox *flatShadingBox = new QCheckBox();
|
||||||
|
Theme::initCheckbox(flatShadingBox);
|
||||||
connect(flatShadingBox, &QCheckBox::stateChanged, this, [=]() {
|
connect(flatShadingBox, &QCheckBox::stateChanged, this, [=]() {
|
||||||
Preferences::instance().setFlatShading(flatShadingBox->isChecked());
|
Preferences::instance().setFlatShading(flatShadingBox->isChecked());
|
||||||
});
|
});
|
||||||
|
|
||||||
QCheckBox *threeNodesBranchEnabledBox = new QCheckBox();
|
QCheckBox *threeNodesBranchEnabledBox = new QCheckBox();
|
||||||
|
Theme::initCheckbox(threeNodesBranchEnabledBox);
|
||||||
connect(threeNodesBranchEnabledBox, &QCheckBox::stateChanged, this, [=]() {
|
connect(threeNodesBranchEnabledBox, &QCheckBox::stateChanged, this, [=]() {
|
||||||
Preferences::instance().setThreeNodesBranchEnableState(threeNodesBranchEnabledBox->isChecked());
|
Preferences::instance().setThreeNodesBranchEnableState(threeNodesBranchEnabledBox->isChecked());
|
||||||
});
|
});
|
||||||
|
|
|
@ -87,6 +87,7 @@ void ScriptVariablesWidget::reload()
|
||||||
auto defaultValue = isTrueValueString(valueOfKeyInMapOrEmpty(variable.second, "defaultValue"));
|
auto defaultValue = isTrueValueString(valueOfKeyInMapOrEmpty(variable.second, "defaultValue"));
|
||||||
|
|
||||||
QCheckBox *checkBox = new QCheckBox;
|
QCheckBox *checkBox = new QCheckBox;
|
||||||
|
Theme::initCheckbox(checkBox);
|
||||||
checkBox->setText(name);
|
checkBox->setText(name);
|
||||||
checkBox->setChecked(value);
|
checkBox->setChecked(value);
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,7 @@ public:
|
||||||
float deformMapScale;
|
float deformMapScale;
|
||||||
QUuid deformMapImageId;
|
QUuid deformMapImageId;
|
||||||
float hollowThickness;
|
float hollowThickness;
|
||||||
|
bool countershaded;
|
||||||
SkeletonPart(const QUuid &withId=QUuid()) :
|
SkeletonPart(const QUuid &withId=QUuid()) :
|
||||||
visible(true),
|
visible(true),
|
||||||
locked(false),
|
locked(false),
|
||||||
|
@ -198,7 +199,8 @@ public:
|
||||||
target(PartTarget::Model),
|
target(PartTarget::Model),
|
||||||
colorSolubility(0.0),
|
colorSolubility(0.0),
|
||||||
deformMapScale(1.0),
|
deformMapScale(1.0),
|
||||||
hollowThickness(0.0)
|
hollowThickness(0.0),
|
||||||
|
countershaded(false)
|
||||||
{
|
{
|
||||||
id = withId.isNull() ? QUuid::createUuid() : withId;
|
id = withId.isNull() ? QUuid::createUuid() : withId;
|
||||||
}
|
}
|
||||||
|
@ -311,6 +313,7 @@ public:
|
||||||
materialId = other.materialId;
|
materialId = other.materialId;
|
||||||
target = other.target;
|
target = other.target;
|
||||||
colorSolubility = other.colorSolubility;
|
colorSolubility = other.colorSolubility;
|
||||||
|
countershaded = other.countershaded;
|
||||||
}
|
}
|
||||||
void updatePreviewMesh(MeshLoader *previewMesh)
|
void updatePreviewMesh(MeshLoader *previewMesh)
|
||||||
{
|
{
|
||||||
|
|
|
@ -171,6 +171,8 @@ void TextureGenerator::prepare()
|
||||||
materialId = QUuid(materialIdIt->second);
|
materialId = QUuid(materialIdIt->second);
|
||||||
QUuid partId = QUuid(partIt.first);
|
QUuid partId = QUuid(partIt.first);
|
||||||
updatedMaterialIdMap.insert({partId, materialId});
|
updatedMaterialIdMap.insert({partId, materialId});
|
||||||
|
if (isTrueValueString(valueOfKeyInMapOrEmpty(partIt.second, "countershaded")))
|
||||||
|
m_countershadedPartIds.insert(partId);
|
||||||
}
|
}
|
||||||
for (const auto &bmeshNode: m_outcome->nodes) {
|
for (const auto &bmeshNode: m_outcome->nodes) {
|
||||||
for (size_t i = 0; i < (int)TextureType::Count - 1; ++i) {
|
for (size_t i = 0; i < (int)TextureType::Count - 1; ++i) {
|
||||||
|
@ -221,6 +223,7 @@ void TextureGenerator::generate()
|
||||||
const auto &triangleVertexUvs = *m_outcome->triangleVertexUvs();
|
const auto &triangleVertexUvs = *m_outcome->triangleVertexUvs();
|
||||||
const auto &triangleSourceNodes = *m_outcome->triangleSourceNodes();
|
const auto &triangleSourceNodes = *m_outcome->triangleSourceNodes();
|
||||||
const auto &partUvRects = *m_outcome->partUvRects();
|
const auto &partUvRects = *m_outcome->partUvRects();
|
||||||
|
const auto &triangleNormals = m_outcome->triangleNormals;
|
||||||
|
|
||||||
std::map<QUuid, QColor> partColorMap;
|
std::map<QUuid, QColor> partColorMap;
|
||||||
std::map<std::pair<QUuid, QUuid>, const OutcomeNode *> nodeMap;
|
std::map<std::pair<QUuid, QUuid>, const OutcomeNode *> nodeMap;
|
||||||
|
@ -382,7 +385,7 @@ void TextureGenerator::generate()
|
||||||
drawTexture(partRoughnessTexturePixmaps, textureRoughnessPainter, false);
|
drawTexture(partRoughnessTexturePixmaps, textureRoughnessPainter, false);
|
||||||
drawTexture(partAmbientOcclusionTexturePixmaps, textureAmbientOcclusionPainter, false);
|
drawTexture(partAmbientOcclusionTexturePixmaps, textureAmbientOcclusionPainter, false);
|
||||||
|
|
||||||
auto drawGradient = [&](const QUuid &partId, size_t triangleIndex, size_t firstVertexIndex, size_t secondVertexIndex,
|
auto drawBySolubility = [&](const QUuid &partId, size_t triangleIndex, size_t firstVertexIndex, size_t secondVertexIndex,
|
||||||
const QUuid &neighborPartId) {
|
const QUuid &neighborPartId) {
|
||||||
const std::vector<QVector2D> &uv = triangleVertexUvs[triangleIndex];
|
const std::vector<QVector2D> &uv = triangleVertexUvs[triangleIndex];
|
||||||
const auto &allRects = partUvRects.find(partId);
|
const auto &allRects = partUvRects.find(partId);
|
||||||
|
@ -495,8 +498,110 @@ void TextureGenerator::generate()
|
||||||
const std::pair<QUuid, QUuid> &oppositeSource = triangleSourceNodes[std::get<0>(opposite->second)];
|
const std::pair<QUuid, QUuid> &oppositeSource = triangleSourceNodes[std::get<0>(opposite->second)];
|
||||||
if (source.first == oppositeSource.first)
|
if (source.first == oppositeSource.first)
|
||||||
continue;
|
continue;
|
||||||
drawGradient(source.first, std::get<0>(it.second), std::get<1>(it.second), std::get<2>(it.second), oppositeSource.first);
|
drawBySolubility(source.first, std::get<0>(it.second), std::get<1>(it.second), std::get<2>(it.second), oppositeSource.first);
|
||||||
drawGradient(oppositeSource.first, std::get<0>(opposite->second), std::get<1>(opposite->second), std::get<2>(opposite->second), source.first);
|
drawBySolubility(oppositeSource.first, std::get<0>(opposite->second), std::get<1>(opposite->second), std::get<2>(opposite->second), source.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw belly white
|
||||||
|
texturePainter.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||||
|
for (size_t triangleIndex = 0; triangleIndex < m_outcome->triangles.size(); ++triangleIndex) {
|
||||||
|
const auto &normal = triangleNormals[triangleIndex];
|
||||||
|
const std::pair<QUuid, QUuid> &source = triangleSourceNodes[triangleIndex];
|
||||||
|
const auto &partId = source.first;
|
||||||
|
if (m_countershadedPartIds.find(partId) == m_countershadedPartIds.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto &allRects = partUvRects.find(partId);
|
||||||
|
if (allRects == partUvRects.end()) {
|
||||||
|
qDebug() << "Found part uv rects failed";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &findOutcomeNode = nodeMap.find(source);
|
||||||
|
if (findOutcomeNode == nodeMap.end())
|
||||||
|
continue;
|
||||||
|
const OutcomeNode *outcomeNode = findOutcomeNode->second;
|
||||||
|
if (qAbs(QVector3D::dotProduct(outcomeNode->direction, QVector3D(0, 1, 0))) >= 0.707) {
|
||||||
|
if (QVector3D::dotProduct(normal, QVector3D(0, 0, 1)) <= 0.0)
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (QVector3D::dotProduct(normal, QVector3D(0, -1, 0)) <= 0.0)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &triangleIndices = m_outcome->triangles[triangleIndex];
|
||||||
|
if (triangleIndices.size() != 3) {
|
||||||
|
qDebug() << "Found invalid triangle indices";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<QVector2D> &uv = triangleVertexUvs[triangleIndex];
|
||||||
|
QVector2D middlePoint = (uv[0] + uv[1] + uv[2]) / 3.0;
|
||||||
|
float finalRadius = (uv[0].distanceToPoint(uv[1]) +
|
||||||
|
uv[1].distanceToPoint(uv[2]) +
|
||||||
|
uv[2].distanceToPoint(uv[0])) / 3.0;
|
||||||
|
QRadialGradient gradient(QPointF(middlePoint.x() * TextureGenerator::m_textureSize,
|
||||||
|
middlePoint.y() * TextureGenerator::m_textureSize),
|
||||||
|
finalRadius * TextureGenerator::m_textureSize);
|
||||||
|
gradient.setColorAt(0.0, Qt::white);
|
||||||
|
gradient.setColorAt(1.0, Qt::transparent);
|
||||||
|
for (const auto &it: allRects->second) {
|
||||||
|
if (it.contains(middlePoint.x(), middlePoint.y())) {
|
||||||
|
QRectF fillTarget((middlePoint.x() - finalRadius),
|
||||||
|
(middlePoint.y() - finalRadius),
|
||||||
|
(finalRadius + finalRadius),
|
||||||
|
(finalRadius + finalRadius));
|
||||||
|
auto clippedRect = it.intersected(fillTarget);
|
||||||
|
QRectF translatedRect = {
|
||||||
|
clippedRect.left() * TextureGenerator::m_textureSize,
|
||||||
|
clippedRect.top() * TextureGenerator::m_textureSize,
|
||||||
|
clippedRect.width() * TextureGenerator::m_textureSize,
|
||||||
|
clippedRect.height() * TextureGenerator::m_textureSize
|
||||||
|
};
|
||||||
|
texturePainter.fillRect(translatedRect, gradient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the neighbor halfedges
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
int j = (i + 1) % 3;
|
||||||
|
auto oppositeHalfEdge = std::make_pair(triangleIndices[j], triangleIndices[i]);
|
||||||
|
const auto &opposite = halfEdgeToTriangleMap.find(oppositeHalfEdge);
|
||||||
|
if (opposite == halfEdgeToTriangleMap.end())
|
||||||
|
continue;
|
||||||
|
auto oppositeTriangleIndex = std::get<0>(opposite->second);
|
||||||
|
const std::pair<QUuid, QUuid> &oppositeSource = triangleSourceNodes[oppositeTriangleIndex];
|
||||||
|
if (partId == oppositeSource.first)
|
||||||
|
continue;
|
||||||
|
const auto &oppositeAllRects = partUvRects.find(oppositeSource.first);
|
||||||
|
if (oppositeAllRects == partUvRects.end()) {
|
||||||
|
qDebug() << "Found part uv rects failed";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const std::vector<QVector2D> &oppositeUv = triangleVertexUvs[oppositeTriangleIndex];
|
||||||
|
QVector2D oppositeMiddlePoint = (oppositeUv[std::get<1>(opposite->second)] + oppositeUv[std::get<2>(opposite->second)]) * 0.5;
|
||||||
|
QRadialGradient oppositeGradient(QPointF(oppositeMiddlePoint.x() * TextureGenerator::m_textureSize,
|
||||||
|
oppositeMiddlePoint.y() * TextureGenerator::m_textureSize),
|
||||||
|
finalRadius * TextureGenerator::m_textureSize);
|
||||||
|
oppositeGradient.setColorAt(0.0, Qt::white);
|
||||||
|
oppositeGradient.setColorAt(1.0, Qt::transparent);
|
||||||
|
for (const auto &it: oppositeAllRects->second) {
|
||||||
|
if (it.contains(oppositeMiddlePoint.x(), oppositeMiddlePoint.y())) {
|
||||||
|
QRectF fillTarget((oppositeMiddlePoint.x() - finalRadius),
|
||||||
|
(oppositeMiddlePoint.y() - finalRadius),
|
||||||
|
(finalRadius + finalRadius),
|
||||||
|
(finalRadius + finalRadius));
|
||||||
|
auto clippedRect = it.intersected(fillTarget);
|
||||||
|
QRectF translatedRect = {
|
||||||
|
clippedRect.left() * TextureGenerator::m_textureSize,
|
||||||
|
clippedRect.top() * TextureGenerator::m_textureSize,
|
||||||
|
clippedRect.width() * TextureGenerator::m_textureSize,
|
||||||
|
clippedRect.height() * TextureGenerator::m_textureSize
|
||||||
|
};
|
||||||
|
texturePainter.fillRect(translatedRect, oppositeGradient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasNormalMap = !m_partNormalTextureMap.empty();
|
hasNormalMap = !m_partNormalTextureMap.empty();
|
||||||
|
|
|
@ -58,6 +58,7 @@ private:
|
||||||
std::map<QUuid, std::pair<QImage, float>> m_partMetalnessTextureMap;
|
std::map<QUuid, std::pair<QImage, float>> m_partMetalnessTextureMap;
|
||||||
std::map<QUuid, std::pair<QImage, float>> m_partRoughnessTextureMap;
|
std::map<QUuid, std::pair<QImage, float>> m_partRoughnessTextureMap;
|
||||||
std::map<QUuid, std::pair<QImage, float>> m_partAmbientOcclusionTextureMap;
|
std::map<QUuid, std::pair<QImage, float>> m_partAmbientOcclusionTextureMap;
|
||||||
|
std::set<QUuid> m_countershadedPartIds;
|
||||||
Snapshot *m_snapshot;
|
Snapshot *m_snapshot;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,13 @@ void Theme::initToolButton(QPushButton *button)
|
||||||
button->setFocusPolicy(Qt::NoFocus);
|
button->setFocusPolicy(Qt::NoFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Theme::initCheckbox(QCheckBox *checkbox)
|
||||||
|
{
|
||||||
|
QPalette palette = checkbox->palette();
|
||||||
|
palette.setColor(QPalette::Background, Theme::white);
|
||||||
|
checkbox->setPalette(palette);
|
||||||
|
}
|
||||||
|
|
||||||
QWidget *Theme::createHorizontalLineWidget()
|
QWidget *Theme::createHorizontalLineWidget()
|
||||||
{
|
{
|
||||||
QWidget *hrLightWidget = new QWidget;
|
QWidget *hrLightWidget = new QWidget;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QCheckBox>
|
||||||
#include "QtAwesome.h"
|
#include "QtAwesome.h"
|
||||||
|
|
||||||
class Theme
|
class Theme
|
||||||
|
@ -53,6 +54,7 @@ public:
|
||||||
static void initAwesomeToolButtonWithoutFont(QPushButton *button);
|
static void initAwesomeToolButtonWithoutFont(QPushButton *button);
|
||||||
static void initAwsomeBaseSizes();
|
static void initAwsomeBaseSizes();
|
||||||
static void initToolButton(QPushButton *button);
|
static void initToolButton(QPushButton *button);
|
||||||
|
static void initCheckbox(QCheckBox *checkbox);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue