diff --git a/src/posedocument.cpp b/src/posedocument.cpp index febe2914..18cdabbf 100644 --- a/src/posedocument.cpp +++ b/src/posedocument.cpp @@ -16,6 +16,26 @@ const float PoseDocument::m_groundPlaneHalfThickness = 0.005 / 4; const bool PoseDocument::m_hideRootAndVirtual = true; 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 { const QClipboard *clipboard = QApplication::clipboard(); @@ -157,7 +177,7 @@ void PoseDocument::reset() partMap.clear(); m_otherIds.clear(); m_boneNameToIdsMap.clear(); - m_bonesPartId = QUuid(); + m_partIdMap.clear(); emit cleanup(); emit parametersChanged(); } @@ -275,16 +295,15 @@ void PoseDocument::fromParameters(const std::vector *rigBones, neckJoint1BoneDirection); std::map> boneNameToIdsMap; - QUuid bonesPartId; parametersToNodes(&otherBones, &boneNameToIdsMap, - &bonesPartId, + &m_partIdMap, true); } parametersToNodes(&bones, &m_boneNameToIdsMap, - &m_bonesPartId, + &m_partIdMap, false); emit parametersChanged(); @@ -292,7 +311,7 @@ void PoseDocument::fromParameters(const std::vector *rigBones, void PoseDocument::parametersToNodes(const std::vector *rigBones, std::map> *boneNameToIdsMap, - QUuid *bonesPartId, + std::map *m_partIdMap, bool isOther) { if (nullptr == rigBones || rigBones->empty()) { @@ -302,9 +321,19 @@ void PoseDocument::parametersToNodes(const std::vector *rigBones, std::set newAddedNodeIds; std::set newAddedEdgeIds; - *bonesPartId = QUuid::createUuid(); - auto &bonesPart = partMap[*bonesPartId]; - bonesPart.id = *bonesPartId; + auto addPartIdOfSide = [=](SkeletonSide side) { + if (m_partIdMap->find(side) != m_partIdMap->end()) + 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(); @@ -319,13 +348,16 @@ void PoseDocument::parametersToNodes(const std::vector *rigBones, std::map boneIndexToHeadNodeIdMap; for (const auto &edgePair: edgePairs) { QUuid firstNodeId, secondNodeId; + SkeletonSide firstNodeSide = SkeletonSideFromBoneName((*rigBones)[edgePair.first].name); + SkeletonSide secondNodeSide = SkeletonSideFromBoneName((*rigBones)[edgePair.second].name); auto findFirst = boneIndexToHeadNodeIdMap.find(edgePair.first); if (findFirst == boneIndexToHeadNodeIdMap.end()) { const auto &bone = (*rigBones)[edgePair.first]; if (!bone.name.startsWith("Virtual_") || !m_hideRootAndVirtual) { SkeletonNode node; - node.partId = *bonesPartId; + node.partId = (*m_partIdMap)[firstNodeSide]; node.id = QUuid::createUuid(); + partMap[node.partId].nodeIds.push_back(node.id); node.setRadius(m_nodeRadius); node.x = fromOutcomeX(bone.headPosition.x()); node.y = fromOutcomeY(bone.headPosition.y()); @@ -343,8 +375,9 @@ void PoseDocument::parametersToNodes(const std::vector *rigBones, const auto &bone = (*rigBones)[edgePair.second]; if (!bone.name.startsWith("Virtual_") || !m_hideRootAndVirtual) { SkeletonNode node; - node.partId = *bonesPartId; + node.partId = (*m_partIdMap)[secondNodeSide]; node.id = QUuid::createUuid(); + partMap[node.partId].nodeIds.push_back(node.id); node.setRadius(m_nodeRadius); node.x = fromOutcomeX(bone.headPosition.x()); node.y = fromOutcomeY(bone.headPosition.y()); @@ -361,8 +394,13 @@ void PoseDocument::parametersToNodes(const std::vector *rigBones, if (firstNodeId.isNull() || secondNodeId.isNull()) continue; + if (firstNodeSide != secondNodeSide) { + qDebug() << "First node side:" << SkeletonSideToDispName(firstNodeSide) << "is different with second node side:" << SkeletonSideToDispName(secondNodeSide); + continue; + } + SkeletonEdge edge; - edge.partId = *bonesPartId; + edge.partId = (*m_partIdMap)[firstNodeSide]; edge.id = QUuid::createUuid(); edge.nodeIds.push_back(firstNodeId); edge.nodeIds.push_back(secondNodeId); @@ -377,11 +415,14 @@ void PoseDocument::parametersToNodes(const std::vector *rigBones, if (m_hideRootAndVirtual && bone.name.startsWith("Virtual_")) continue; if (bone.children.empty()) { + SkeletonSide side = SkeletonSideFromBoneName(bone.name); + const QUuid &firstNodeId = boneIndexToHeadNodeIdMap[i]; SkeletonNode node; - node.partId = *bonesPartId; + node.partId = (*m_partIdMap)[side]; node.id = QUuid::createUuid(); + partMap[node.partId].nodeIds.push_back(node.id); node.setRadius(m_nodeRadius / 2); node.x = fromOutcomeX(bone.tailPosition.x()); node.y = fromOutcomeY(bone.tailPosition.y()); @@ -391,8 +432,9 @@ void PoseDocument::parametersToNodes(const std::vector *rigBones, (*boneNameToIdsMap)[bone.name] = {firstNodeId, node.id}; SkeletonEdge edge; - edge.partId = *bonesPartId; + edge.partId = (*m_partIdMap)[side]; edge.id = QUuid::createUuid(); + partMap[node.partId].nodeIds.push_back(node.id); edge.nodeIds.push_back(firstNodeId); edge.nodeIds.push_back(node.id); edgeMap[edge.id] = edge; @@ -426,6 +468,10 @@ void PoseDocument::parametersToNodes(const std::vector *rigBones, for (const auto &edgeIt: newAddedEdgeIds) { emit edgeAdded(edgeIt); } + + for (const auto &it: *m_partIdMap) { + emit partVisibleStateChanged(it.second); + } } void PoseDocument::moveNodeBy(QUuid nodeId, float x, float y, float z) @@ -463,8 +509,6 @@ float PoseDocument::findFootBottomY() const { auto maxY = std::numeric_limits::lowest(); for (const auto &nodeIt: nodeMap) { - if (nodeIt.second.partId != m_bonesPartId) - continue; auto y = nodeIt.second.y + nodeIt.second.radius; if (y > maxY) maxY = y; diff --git a/src/posedocument.h b/src/posedocument.h index f2270e1e..6a1cbd79 100644 --- a/src/posedocument.h +++ b/src/posedocument.h @@ -21,6 +21,8 @@ signals: void edgeAdded(QUuid edgeId); void nodeOriginChanged(QUuid nodeId); void parametersChanged(); + void sideVisibleStateChanged(SkeletonSide side); + void partVisibleStateChanged(QUuid partId); public: bool undoable() const override; @@ -41,6 +43,8 @@ public: void fromParameters(const std::vector *rigBones, const std::map> ¶meters); + bool isSideVisible(SkeletonSide side); + public slots: void saveHistoryItem(); void clearHistories(); @@ -51,6 +55,7 @@ public slots: void moveNodeBy(QUuid nodeId, float x, float y, float z); void setNodeOrigin(QUuid nodeId, float x, float y, float z); void switchChainSide(const std::set nodeIds); + void setSideVisiableState(SkeletonSide side, bool visible); public: static const float m_nodeRadius; @@ -63,7 +68,7 @@ private: float findFootBottomY() const; void parametersToNodes(const std::vector *rigBones, std::map> *boneNameToIdsMap, - QUuid *bonesPartId, + std::map *m_partIdMap, bool isOther=false); void updateBonesFromParameters(std::vector *bones, const std::map> ¶meters, @@ -72,12 +77,13 @@ private: const QVector3D &neckJoint1BoneDirection); std::map> m_boneNameToIdsMap; - QUuid m_bonesPartId; + std::map m_partIdMap; std::deque m_undoItems; std::deque m_redoItems; std::vector m_riggerBones; std::vector>> m_otherFramesParameters; std::set m_otherIds; + std::set m_hiddenSides; static float fromOutcomeX(float x); static float toOutcomeX(float x); diff --git a/src/poseeditwidget.cpp b/src/poseeditwidget.cpp index cda592c1..3a3471bd 100644 --- a/src/poseeditwidget.cpp +++ b/src/poseeditwidget.cpp @@ -77,6 +77,7 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : connect(m_poseDocument, &PoseDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded); connect(m_poseDocument, &PoseDocument::edgeAdded, graphicsWidget, &SkeletonGraphicsWidget::edgeAdded); connect(m_poseDocument, &PoseDocument::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged); + connect(m_poseDocument, &PoseDocument::partVisibleStateChanged, graphicsWidget, &SkeletonGraphicsWidget::partVisibleStateChanged); connect(m_poseDocument, &PoseDocument::parametersChanged, this, [&]() { m_currentParameters.clear(); @@ -94,6 +95,56 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : connect(opacitySlider, &QSlider::valueChanged, this, [=](int value) { 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; sliderLayout->addStretch(); @@ -106,6 +157,7 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : QVBoxLayout *previewLayout = new QVBoxLayout; previewLayout->addStretch(); + previewLayout->addLayout(sideButtonLayout); previewLayout->addLayout(sliderLayout); previewLayout->addSpacing(20); @@ -232,6 +284,27 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : 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) { QMenu popupMenu; diff --git a/src/poseeditwidget.h b/src/poseeditwidget.h index 59df00cd..b60c664f 100644 --- a/src/poseeditwidget.h +++ b/src/poseeditwidget.h @@ -13,6 +13,7 @@ #include "skeletongraphicswidget.h" #include "posedocument.h" #include "floatnumberwidget.h" +#include "skeletonside.h" class PoseEditWidget : public QDialog { @@ -77,6 +78,8 @@ private: QPushButton *m_framesSettingButton = nullptr; QSlider *m_currentFrameSlider = nullptr; static float m_defaultBlur; + void initSideButton(QPushButton *button); + void updateSideButtonState(QPushButton *button, bool visible); }; #endif diff --git a/src/skeletonside.cpp b/src/skeletonside.cpp index 908f60a7..a7587e52 100644 --- a/src/skeletonside.cpp +++ b/src/skeletonside.cpp @@ -2,3 +2,12 @@ #include "skeletonside.h" 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; +} diff --git a/src/skeletonside.h b/src/skeletonside.h index 344954c5..b9a528f6 100644 --- a/src/skeletonside.h +++ b/src/skeletonside.h @@ -24,5 +24,6 @@ QString SkeletonSideToDispName(SkeletonSide side) \ return ""; \ } \ } +SkeletonSide SkeletonSideFromBoneName(const QString &boneName); #endif