Add side visibility toggle button to pose editor

master
Jeremy Hu 2019-06-17 22:19:08 +09:30
parent bc565923aa
commit 56ddc1f8ee
6 changed files with 153 additions and 17 deletions

View File

@ -16,6 +16,26 @@ const float PoseDocument::m_groundPlaneHalfThickness = 0.005 / 4;
const bool PoseDocument::m_hideRootAndVirtual = true; const bool PoseDocument::m_hideRootAndVirtual = true;
const float PoseDocument::m_outcomeScaleFactor = 0.5; const float PoseDocument::m_outcomeScaleFactor = 0.5;
void PoseDocument::setSideVisiableState(SkeletonSide side, bool visible)
{
bool isSideVisible = m_hiddenSides.find(side) == m_hiddenSides.end();
if (isSideVisible == visible)
return;
if (visible)
m_hiddenSides.erase(side);
else
m_hiddenSides.insert(side);
emit sideVisibleStateChanged(side);
const QUuid partId = m_partIdMap[side];
partMap[partId].visible = visible;
emit partVisibleStateChanged(partId);
}
bool PoseDocument::isSideVisible(SkeletonSide side)
{
return m_hiddenSides.find(side) == m_hiddenSides.end();
}
bool PoseDocument::hasPastableNodesInClipboard() const bool PoseDocument::hasPastableNodesInClipboard() const
{ {
const QClipboard *clipboard = QApplication::clipboard(); const QClipboard *clipboard = QApplication::clipboard();
@ -157,7 +177,7 @@ void PoseDocument::reset()
partMap.clear(); partMap.clear();
m_otherIds.clear(); m_otherIds.clear();
m_boneNameToIdsMap.clear(); m_boneNameToIdsMap.clear();
m_bonesPartId = QUuid(); m_partIdMap.clear();
emit cleanup(); emit cleanup();
emit parametersChanged(); emit parametersChanged();
} }
@ -275,16 +295,15 @@ void PoseDocument::fromParameters(const std::vector<RiggerBone> *rigBones,
neckJoint1BoneDirection); neckJoint1BoneDirection);
std::map<QString, std::pair<QUuid, QUuid>> boneNameToIdsMap; std::map<QString, std::pair<QUuid, QUuid>> boneNameToIdsMap;
QUuid bonesPartId;
parametersToNodes(&otherBones, parametersToNodes(&otherBones,
&boneNameToIdsMap, &boneNameToIdsMap,
&bonesPartId, &m_partIdMap,
true); true);
} }
parametersToNodes(&bones, parametersToNodes(&bones,
&m_boneNameToIdsMap, &m_boneNameToIdsMap,
&m_bonesPartId, &m_partIdMap,
false); false);
emit parametersChanged(); emit parametersChanged();
@ -292,7 +311,7 @@ void PoseDocument::fromParameters(const std::vector<RiggerBone> *rigBones,
void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones, void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
std::map<QString, std::pair<QUuid, QUuid>> *boneNameToIdsMap, std::map<QString, std::pair<QUuid, QUuid>> *boneNameToIdsMap,
QUuid *bonesPartId, std::map<SkeletonSide, QUuid> *m_partIdMap,
bool isOther) bool isOther)
{ {
if (nullptr == rigBones || rigBones->empty()) { if (nullptr == rigBones || rigBones->empty()) {
@ -302,9 +321,19 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
std::set<QUuid> newAddedNodeIds; std::set<QUuid> newAddedNodeIds;
std::set<QUuid> newAddedEdgeIds; std::set<QUuid> newAddedEdgeIds;
*bonesPartId = QUuid::createUuid(); auto addPartIdOfSide = [=](SkeletonSide side) {
auto &bonesPart = partMap[*bonesPartId]; if (m_partIdMap->find(side) != m_partIdMap->end())
bonesPart.id = *bonesPartId; return;
QUuid partId = QUuid::createUuid();
auto &bonesPart = this->partMap[partId];
bonesPart.id = partId;
bonesPart.visible = this->m_hiddenSides.find(side) == this->m_hiddenSides.end();
qDebug() << SkeletonSideToDispName(side) << "partId:" << partId << " visible:" << bonesPart.visible;
(*m_partIdMap)[side] = partId;
};
addPartIdOfSide(SkeletonSide::Left);
addPartIdOfSide(SkeletonSide::None);
addPartIdOfSide(SkeletonSide::Right);
//qDebug() << "rigBones size:" << rigBones->size(); //qDebug() << "rigBones size:" << rigBones->size();
@ -319,13 +348,16 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
std::map<int, QUuid> boneIndexToHeadNodeIdMap; std::map<int, QUuid> boneIndexToHeadNodeIdMap;
for (const auto &edgePair: edgePairs) { for (const auto &edgePair: edgePairs) {
QUuid firstNodeId, secondNodeId; QUuid firstNodeId, secondNodeId;
SkeletonSide firstNodeSide = SkeletonSideFromBoneName((*rigBones)[edgePair.first].name);
SkeletonSide secondNodeSide = SkeletonSideFromBoneName((*rigBones)[edgePair.second].name);
auto findFirst = boneIndexToHeadNodeIdMap.find(edgePair.first); auto findFirst = boneIndexToHeadNodeIdMap.find(edgePair.first);
if (findFirst == boneIndexToHeadNodeIdMap.end()) { if (findFirst == boneIndexToHeadNodeIdMap.end()) {
const auto &bone = (*rigBones)[edgePair.first]; const auto &bone = (*rigBones)[edgePair.first];
if (!bone.name.startsWith("Virtual_") || !m_hideRootAndVirtual) { if (!bone.name.startsWith("Virtual_") || !m_hideRootAndVirtual) {
SkeletonNode node; SkeletonNode node;
node.partId = *bonesPartId; node.partId = (*m_partIdMap)[firstNodeSide];
node.id = QUuid::createUuid(); node.id = QUuid::createUuid();
partMap[node.partId].nodeIds.push_back(node.id);
node.setRadius(m_nodeRadius); node.setRadius(m_nodeRadius);
node.x = fromOutcomeX(bone.headPosition.x()); node.x = fromOutcomeX(bone.headPosition.x());
node.y = fromOutcomeY(bone.headPosition.y()); node.y = fromOutcomeY(bone.headPosition.y());
@ -343,8 +375,9 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
const auto &bone = (*rigBones)[edgePair.second]; const auto &bone = (*rigBones)[edgePair.second];
if (!bone.name.startsWith("Virtual_") || !m_hideRootAndVirtual) { if (!bone.name.startsWith("Virtual_") || !m_hideRootAndVirtual) {
SkeletonNode node; SkeletonNode node;
node.partId = *bonesPartId; node.partId = (*m_partIdMap)[secondNodeSide];
node.id = QUuid::createUuid(); node.id = QUuid::createUuid();
partMap[node.partId].nodeIds.push_back(node.id);
node.setRadius(m_nodeRadius); node.setRadius(m_nodeRadius);
node.x = fromOutcomeX(bone.headPosition.x()); node.x = fromOutcomeX(bone.headPosition.x());
node.y = fromOutcomeY(bone.headPosition.y()); node.y = fromOutcomeY(bone.headPosition.y());
@ -361,8 +394,13 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
if (firstNodeId.isNull() || secondNodeId.isNull()) if (firstNodeId.isNull() || secondNodeId.isNull())
continue; continue;
if (firstNodeSide != secondNodeSide) {
qDebug() << "First node side:" << SkeletonSideToDispName(firstNodeSide) << "is different with second node side:" << SkeletonSideToDispName(secondNodeSide);
continue;
}
SkeletonEdge edge; SkeletonEdge edge;
edge.partId = *bonesPartId; edge.partId = (*m_partIdMap)[firstNodeSide];
edge.id = QUuid::createUuid(); edge.id = QUuid::createUuid();
edge.nodeIds.push_back(firstNodeId); edge.nodeIds.push_back(firstNodeId);
edge.nodeIds.push_back(secondNodeId); edge.nodeIds.push_back(secondNodeId);
@ -377,11 +415,14 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
if (m_hideRootAndVirtual && bone.name.startsWith("Virtual_")) if (m_hideRootAndVirtual && bone.name.startsWith("Virtual_"))
continue; continue;
if (bone.children.empty()) { if (bone.children.empty()) {
SkeletonSide side = SkeletonSideFromBoneName(bone.name);
const QUuid &firstNodeId = boneIndexToHeadNodeIdMap[i]; const QUuid &firstNodeId = boneIndexToHeadNodeIdMap[i];
SkeletonNode node; SkeletonNode node;
node.partId = *bonesPartId; node.partId = (*m_partIdMap)[side];
node.id = QUuid::createUuid(); node.id = QUuid::createUuid();
partMap[node.partId].nodeIds.push_back(node.id);
node.setRadius(m_nodeRadius / 2); node.setRadius(m_nodeRadius / 2);
node.x = fromOutcomeX(bone.tailPosition.x()); node.x = fromOutcomeX(bone.tailPosition.x());
node.y = fromOutcomeY(bone.tailPosition.y()); node.y = fromOutcomeY(bone.tailPosition.y());
@ -391,8 +432,9 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
(*boneNameToIdsMap)[bone.name] = {firstNodeId, node.id}; (*boneNameToIdsMap)[bone.name] = {firstNodeId, node.id};
SkeletonEdge edge; SkeletonEdge edge;
edge.partId = *bonesPartId; edge.partId = (*m_partIdMap)[side];
edge.id = QUuid::createUuid(); edge.id = QUuid::createUuid();
partMap[node.partId].nodeIds.push_back(node.id);
edge.nodeIds.push_back(firstNodeId); edge.nodeIds.push_back(firstNodeId);
edge.nodeIds.push_back(node.id); edge.nodeIds.push_back(node.id);
edgeMap[edge.id] = edge; edgeMap[edge.id] = edge;
@ -426,6 +468,10 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
for (const auto &edgeIt: newAddedEdgeIds) { for (const auto &edgeIt: newAddedEdgeIds) {
emit edgeAdded(edgeIt); emit edgeAdded(edgeIt);
} }
for (const auto &it: *m_partIdMap) {
emit partVisibleStateChanged(it.second);
}
} }
void PoseDocument::moveNodeBy(QUuid nodeId, float x, float y, float z) void PoseDocument::moveNodeBy(QUuid nodeId, float x, float y, float z)
@ -463,8 +509,6 @@ float PoseDocument::findFootBottomY() const
{ {
auto maxY = std::numeric_limits<float>::lowest(); auto maxY = std::numeric_limits<float>::lowest();
for (const auto &nodeIt: nodeMap) { for (const auto &nodeIt: nodeMap) {
if (nodeIt.second.partId != m_bonesPartId)
continue;
auto y = nodeIt.second.y + nodeIt.second.radius; auto y = nodeIt.second.y + nodeIt.second.radius;
if (y > maxY) if (y > maxY)
maxY = y; maxY = y;

View File

@ -21,6 +21,8 @@ signals:
void edgeAdded(QUuid edgeId); void edgeAdded(QUuid edgeId);
void nodeOriginChanged(QUuid nodeId); void nodeOriginChanged(QUuid nodeId);
void parametersChanged(); void parametersChanged();
void sideVisibleStateChanged(SkeletonSide side);
void partVisibleStateChanged(QUuid partId);
public: public:
bool undoable() const override; bool undoable() const override;
@ -41,6 +43,8 @@ public:
void fromParameters(const std::vector<RiggerBone> *rigBones, void fromParameters(const std::vector<RiggerBone> *rigBones,
const std::map<QString, std::map<QString, QString>> &parameters); const std::map<QString, std::map<QString, QString>> &parameters);
bool isSideVisible(SkeletonSide side);
public slots: public slots:
void saveHistoryItem(); void saveHistoryItem();
void clearHistories(); void clearHistories();
@ -51,6 +55,7 @@ public slots:
void moveNodeBy(QUuid nodeId, float x, float y, float z); void moveNodeBy(QUuid nodeId, float x, float y, float z);
void setNodeOrigin(QUuid nodeId, float x, float y, float z); void setNodeOrigin(QUuid nodeId, float x, float y, float z);
void switchChainSide(const std::set<QUuid> nodeIds); void switchChainSide(const std::set<QUuid> nodeIds);
void setSideVisiableState(SkeletonSide side, bool visible);
public: public:
static const float m_nodeRadius; static const float m_nodeRadius;
@ -63,7 +68,7 @@ private:
float findFootBottomY() const; float findFootBottomY() const;
void parametersToNodes(const std::vector<RiggerBone> *rigBones, void parametersToNodes(const std::vector<RiggerBone> *rigBones,
std::map<QString, std::pair<QUuid, QUuid>> *boneNameToIdsMap, std::map<QString, std::pair<QUuid, QUuid>> *boneNameToIdsMap,
QUuid *bonesPartId, std::map<SkeletonSide, QUuid> *m_partIdMap,
bool isOther=false); bool isOther=false);
void updateBonesFromParameters(std::vector<RiggerBone> *bones, void updateBonesFromParameters(std::vector<RiggerBone> *bones,
const std::map<QString, std::map<QString, QString>> &parameters, const std::map<QString, std::map<QString, QString>> &parameters,
@ -72,12 +77,13 @@ private:
const QVector3D &neckJoint1BoneDirection); const QVector3D &neckJoint1BoneDirection);
std::map<QString, std::pair<QUuid, QUuid>> m_boneNameToIdsMap; std::map<QString, std::pair<QUuid, QUuid>> m_boneNameToIdsMap;
QUuid m_bonesPartId; std::map<SkeletonSide, QUuid> m_partIdMap;
std::deque<PoseHistoryItem> m_undoItems; std::deque<PoseHistoryItem> m_undoItems;
std::deque<PoseHistoryItem> m_redoItems; std::deque<PoseHistoryItem> m_redoItems;
std::vector<RiggerBone> m_riggerBones; std::vector<RiggerBone> m_riggerBones;
std::vector<std::map<QString, std::map<QString, QString>>> m_otherFramesParameters; std::vector<std::map<QString, std::map<QString, QString>>> m_otherFramesParameters;
std::set<QUuid> m_otherIds; std::set<QUuid> m_otherIds;
std::set<SkeletonSide> m_hiddenSides;
static float fromOutcomeX(float x); static float fromOutcomeX(float x);
static float toOutcomeX(float x); static float toOutcomeX(float x);

View File

@ -77,6 +77,7 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) :
connect(m_poseDocument, &PoseDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded); connect(m_poseDocument, &PoseDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded);
connect(m_poseDocument, &PoseDocument::edgeAdded, graphicsWidget, &SkeletonGraphicsWidget::edgeAdded); connect(m_poseDocument, &PoseDocument::edgeAdded, graphicsWidget, &SkeletonGraphicsWidget::edgeAdded);
connect(m_poseDocument, &PoseDocument::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged); connect(m_poseDocument, &PoseDocument::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged);
connect(m_poseDocument, &PoseDocument::partVisibleStateChanged, graphicsWidget, &SkeletonGraphicsWidget::partVisibleStateChanged);
connect(m_poseDocument, &PoseDocument::parametersChanged, this, [&]() { connect(m_poseDocument, &PoseDocument::parametersChanged, this, [&]() {
m_currentParameters.clear(); m_currentParameters.clear();
@ -95,6 +96,56 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) :
graphicsWidget->setBackgroundBlur((float)value / 10); graphicsWidget->setBackgroundBlur((float)value / 10);
}); });
auto updateSideVisibleButtonState = [=](QPushButton *button, SkeletonSide side) {
this->updateSideButtonState(button, m_poseDocument->isSideVisible(side));
};
QPushButton *leftSideVisibleButton = new QPushButton(QChar('L'));
leftSideVisibleButton->setToolTip(tr("Toggle Left Side Visibility"));
initSideButton(leftSideVisibleButton);
updateSideVisibleButtonState(leftSideVisibleButton, SkeletonSide::Left);
connect(leftSideVisibleButton, &QPushButton::clicked, this, [=]() {
m_poseDocument->setSideVisiableState(SkeletonSide::Left, !m_poseDocument->isSideVisible(SkeletonSide::Left));
});
QPushButton *middleSideVisibleButton = new QPushButton(QChar('M'));
middleSideVisibleButton->setToolTip(tr("Toggle Middle Side Visibility"));
initSideButton(middleSideVisibleButton);
updateSideVisibleButtonState(middleSideVisibleButton, SkeletonSide::None);
connect(middleSideVisibleButton, &QPushButton::clicked, this, [=]() {
m_poseDocument->setSideVisiableState(SkeletonSide::None, !m_poseDocument->isSideVisible(SkeletonSide::None));
});
QPushButton *rightSideVisibleButton = new QPushButton(QChar('R'));
rightSideVisibleButton->setToolTip(tr("Toggle Right Side Visibility"));
initSideButton(rightSideVisibleButton);
updateSideVisibleButtonState(rightSideVisibleButton, SkeletonSide::Right);
connect(rightSideVisibleButton, &QPushButton::clicked, this, [=]() {
m_poseDocument->setSideVisiableState(SkeletonSide::Right, !m_poseDocument->isSideVisible(SkeletonSide::Right));
});
connect(m_poseDocument, &PoseDocument::sideVisibleStateChanged, this, [=](SkeletonSide side) {
switch (side) {
case SkeletonSide::Left:
updateSideVisibleButtonState(leftSideVisibleButton, side);
break;
case SkeletonSide::None:
updateSideVisibleButtonState(middleSideVisibleButton, side);
break;
case SkeletonSide::Right:
updateSideVisibleButtonState(rightSideVisibleButton, side);
break;
}
});
QHBoxLayout *sideButtonLayout = new QHBoxLayout;
sideButtonLayout->setSpacing(0);
sideButtonLayout->addStretch();
sideButtonLayout->addWidget(leftSideVisibleButton);
sideButtonLayout->addWidget(middleSideVisibleButton);
sideButtonLayout->addWidget(rightSideVisibleButton);
sideButtonLayout->addStretch();
QHBoxLayout *sliderLayout = new QHBoxLayout; QHBoxLayout *sliderLayout = new QHBoxLayout;
sliderLayout->addStretch(); sliderLayout->addStretch();
sliderLayout->addSpacing(50); sliderLayout->addSpacing(50);
@ -106,6 +157,7 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) :
QVBoxLayout *previewLayout = new QVBoxLayout; QVBoxLayout *previewLayout = new QVBoxLayout;
previewLayout->addStretch(); previewLayout->addStretch();
previewLayout->addLayout(sideButtonLayout);
previewLayout->addLayout(sliderLayout); previewLayout->addLayout(sliderLayout);
previewLayout->addSpacing(20); previewLayout->addSpacing(20);
@ -232,6 +284,27 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) :
m_poseDocument->saveHistoryItem(); m_poseDocument->saveHistoryItem();
} }
void PoseEditWidget::initSideButton(QPushButton *button)
{
QFont font;
font.setWeight(QFont::Light);
font.setPixelSize(Theme::toolIconFontSize);
font.setBold(false);
button->setFont(font);
button->setFixedSize(Theme::toolIconSize, Theme::toolIconSize);
button->setStyleSheet("QPushButton {color: #f7d9c8}");
button->setFocusPolicy(Qt::NoFocus);
}
void PoseEditWidget::updateSideButtonState(QPushButton *button, bool visible)
{
if (visible)
button->setStyleSheet("QPushButton {color: #f7d9c8}");
else
button->setStyleSheet("QPushButton {color: #252525}");
}
void PoseEditWidget::showFramesSettingPopup(const QPoint &pos) void PoseEditWidget::showFramesSettingPopup(const QPoint &pos)
{ {
QMenu popupMenu; QMenu popupMenu;

View File

@ -13,6 +13,7 @@
#include "skeletongraphicswidget.h" #include "skeletongraphicswidget.h"
#include "posedocument.h" #include "posedocument.h"
#include "floatnumberwidget.h" #include "floatnumberwidget.h"
#include "skeletonside.h"
class PoseEditWidget : public QDialog class PoseEditWidget : public QDialog
{ {
@ -77,6 +78,8 @@ private:
QPushButton *m_framesSettingButton = nullptr; QPushButton *m_framesSettingButton = nullptr;
QSlider *m_currentFrameSlider = nullptr; QSlider *m_currentFrameSlider = nullptr;
static float m_defaultBlur; static float m_defaultBlur;
void initSideButton(QPushButton *button);
void updateSideButtonState(QPushButton *button, bool visible);
}; };
#endif #endif

View File

@ -2,3 +2,12 @@
#include "skeletonside.h" #include "skeletonside.h"
IMPL_SkeletonSideToDispName IMPL_SkeletonSideToDispName
SkeletonSide SkeletonSideFromBoneName(const QString &boneName)
{
if (boneName.startsWith("Left"))
return SkeletonSide::Left;
else if (boneName.startsWith("Right"))
return SkeletonSide::Right;
return SkeletonSide::None;
}

View File

@ -24,5 +24,6 @@ QString SkeletonSideToDispName(SkeletonSide side) \
return ""; \ return ""; \
} \ } \
} }
SkeletonSide SkeletonSideFromBoneName(const QString &boneName);
#endif #endif