Add side visibility toggle button to pose editor
parent
bc565923aa
commit
56ddc1f8ee
|
@ -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<RiggerBone> *rigBones,
|
|||
neckJoint1BoneDirection);
|
||||
|
||||
std::map<QString, std::pair<QUuid, QUuid>> 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<RiggerBone> *rigBones,
|
|||
|
||||
void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
|
||||
std::map<QString, std::pair<QUuid, QUuid>> *boneNameToIdsMap,
|
||||
QUuid *bonesPartId,
|
||||
std::map<SkeletonSide, QUuid> *m_partIdMap,
|
||||
bool isOther)
|
||||
{
|
||||
if (nullptr == rigBones || rigBones->empty()) {
|
||||
|
@ -302,9 +321,19 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
|
|||
std::set<QUuid> newAddedNodeIds;
|
||||
std::set<QUuid> 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<RiggerBone> *rigBones,
|
|||
std::map<int, QUuid> 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<RiggerBone> *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<RiggerBone> *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<RiggerBone> *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<RiggerBone> *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<RiggerBone> *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<float>::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;
|
||||
|
|
|
@ -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<RiggerBone> *rigBones,
|
||||
const std::map<QString, std::map<QString, QString>> ¶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<QUuid> 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<RiggerBone> *rigBones,
|
||||
std::map<QString, std::pair<QUuid, QUuid>> *boneNameToIdsMap,
|
||||
QUuid *bonesPartId,
|
||||
std::map<SkeletonSide, QUuid> *m_partIdMap,
|
||||
bool isOther=false);
|
||||
void updateBonesFromParameters(std::vector<RiggerBone> *bones,
|
||||
const std::map<QString, std::map<QString, QString>> ¶meters,
|
||||
|
@ -72,12 +77,13 @@ private:
|
|||
const QVector3D &neckJoint1BoneDirection);
|
||||
|
||||
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_redoItems;
|
||||
std::vector<RiggerBone> m_riggerBones;
|
||||
std::vector<std::map<QString, std::map<QString, QString>>> m_otherFramesParameters;
|
||||
std::set<QUuid> m_otherIds;
|
||||
std::set<SkeletonSide> m_hiddenSides;
|
||||
|
||||
static float fromOutcomeX(float x);
|
||||
static float toOutcomeX(float x);
|
||||
|
|
|
@ -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();
|
||||
|
@ -95,6 +96,56 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) :
|
|||
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();
|
||||
sliderLayout->addSpacing(50);
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -24,5 +24,6 @@ QString SkeletonSideToDispName(SkeletonSide side) \
|
|||
return ""; \
|
||||
} \
|
||||
}
|
||||
SkeletonSide SkeletonSideFromBoneName(const QString &boneName);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue