From 516df9a1b77a1d98a3124952292cf56f75b14b04 Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Wed, 19 Jun 2019 22:27:38 +0930 Subject: [PATCH] Add new pose setting: yTranslationScale Normally, when pose been generated, foot is always been pulling to the ground by a y-translation. This setting could be used to scale the y-translation. It's usefull for birds fly kind of motion. --- src/animalposer.cpp | 35 ++------------------ src/document.cpp | 23 +++++++++++-- src/document.h | 10 +++++- src/motioneditwidget.cpp | 2 +- src/motionsgenerator.cpp | 5 ++- src/motionsgenerator.h | 3 +- src/poseeditwidget.cpp | 71 ++++++++++++++++++++++++++++++---------- src/poseeditwidget.h | 7 ++-- src/posemanagewidget.cpp | 1 + src/poser.cpp | 5 +++ src/poser.h | 2 ++ 11 files changed, 105 insertions(+), 59 deletions(-) diff --git a/src/animalposer.cpp b/src/animalposer.cpp index 68fae8be..11238277 100644 --- a/src/animalposer.cpp +++ b/src/animalposer.cpp @@ -20,62 +20,31 @@ void AnimalPoser::resolveTransform() resolveChainRotation(chain.second); } - //int firstSpineBoneIndex = findBoneIndex(Rigger::firstSpineBoneName); - //if (-1 == firstSpineBoneIndex) { - // qDebug() << "Find first spine bone failed:" << Rigger::firstSpineBoneName; - // return; - //} - - //const auto &firstSpineBone = bones()[firstSpineBoneIndex]; - float mostBottomYBeforeTransform = std::numeric_limits::max(); for (const auto &bone: bones()) { if (bone.tailPosition.y() < mostBottomYBeforeTransform) mostBottomYBeforeTransform = bone.tailPosition.y(); } - //float legHeightBeforeTransform = std::abs(mostBottomYBeforeTransform - firstSpineBone.headPosition.y()); auto transformedJointNodeTree = m_jointNodeTree; transformedJointNodeTree.recalculateTransformMatrices(); float mostBottomYAfterTransform = std::numeric_limits::max(); - //QVector3D firstSpineBonePositionAfterTransform = firstSpineBone.headPosition; for (int i = 0; i < (int)transformedJointNodeTree.nodes().size(); ++i) { const auto &bone = bones()[i]; const auto &jointNode = transformedJointNodeTree.nodes()[i]; QVector3D newPosition = jointNode.transformMatrix * bone.tailPosition; - //if (0 == i) { - // Root bone's tail position is the first spine bone's head position - // firstSpineBonePositionAfterTransform = newPosition; - //} if (newPosition.y() < mostBottomYAfterTransform) mostBottomYAfterTransform = newPosition.y(); } - //float legHeightAfterTransform = std::abs(mostBottomYAfterTransform - firstSpineBonePositionAfterTransform.y()); - //float translateY = legHeightAfterTransform - legHeightBeforeTransform; float translateY = mostBottomYBeforeTransform - mostBottomYAfterTransform; - //qDebug() << "Leg height changed, translateY:" << translateY << "legHeightBeforeTransform:" << legHeightBeforeTransform << "legHeightAfterTransform:" << legHeightAfterTransform << "firstSpineBonePositionAfterTransform:" << firstSpineBonePositionAfterTransform << "firstSpineBone.headPosition:" << firstSpineBone.headPosition; - - /* - const auto &findRootParameters = parameters().find(Rigger::rootBoneName); - if (findRootParameters != parameters().end()) { - auto findHeightAboveGroundLevel = findRootParameters->second.find("heightAboveGroundLevel"); - if (findHeightAboveGroundLevel != findRootParameters->second.end()) { - float heightAboveGroundLevel = findHeightAboveGroundLevel->second.toFloat(); - float myHeightAboveGroundLevel = heightAboveGroundLevel * legHeightAfterTransform; - translateY += myHeightAboveGroundLevel; - //qDebug() << "heightAboveGroundLevel:" << heightAboveGroundLevel << "myHeightAboveGroundLevel:" << myHeightAboveGroundLevel << "legHeightBeforeTransform:" << legHeightBeforeTransform << "applied translateY:" << translateY; - } - } - */ - if (!qFuzzyIsNull(translateY)) { int rootBoneIndex = findBoneIndex(Rigger::rootBoneName); if (-1 == rootBoneIndex) { qDebug() << "Find root bone failed:" << Rigger::rootBoneName; return; } - m_jointNodeTree.addTranslation(rootBoneIndex, QVector3D(0, translateY, 0)); + m_jointNodeTree.addTranslation(rootBoneIndex, QVector3D(0, translateY * m_yTranslationScale, 0)); } } @@ -186,7 +155,7 @@ void AnimalPoser::resolveChainRotation(const std::vector &limbBoneNames QVector3D targetMiddleBoneStartPosition; { - //qDebug() << beginBoneName << "Angle:" << angleBetweenDistanceAndMiddleBones; + qDebug() << beginBoneName << "Angle:" << angleBetweenDistanceAndMiddleBones << "Distance:" << targetDistanceBetweenBeginAndEndBones; auto rotation = QQuaternion::fromAxisAndAngle(matchRotatePlaneNormal, angleBetweenDistanceAndMiddleBones); targetMiddleBoneStartPosition = targetEndBoneStartPosition + rotation.rotatedVector(-matchDirectionBetweenBeginAndEndPones).normalized() * targetMiddleBoneLength; } diff --git a/src/document.cpp b/src/document.cpp index cf23a695..42ec479a 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -356,7 +356,7 @@ QUuid Document::createNode(QUuid nodeId, float x, float y, float z, float radius return node.id; } -void Document::addPose(QUuid poseId, QString name, std::vector, std::map>>> frames, QUuid turnaroundImageId) +void Document::addPose(QUuid poseId, QString name, std::vector, std::map>>> frames, QUuid turnaroundImageId, float yTranslationScale) { QUuid newPoseId = poseId; auto &pose = poseMap[newPoseId]; @@ -365,6 +365,7 @@ void Document::addPose(QUuid poseId, QString name, std::vectorsecond.yTranslationScale = scale; + findPoseResult->second.dirty = true; + emit poseYtranslationScaleChanged(poseId); + emit optionsChanged(); +} + void Document::renamePose(QUuid poseId, QString name) { auto findPoseResult = poseMap.find(poseId); @@ -1061,6 +1075,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set &limitNodeId pose["name"] = poseIt.second.name; if (!poseIt.second.turnaroundImageId.isNull()) pose["canvasImageId"] = poseIt.second.turnaroundImageId.toString(); + if (poseIt.second.yTranslationScaleAdjusted()) + pose["yTranslationScale"] = QString::number(poseIt.second.yTranslationScale); snapshot->poses.push_back(std::make_pair(pose, poseIt.second.frames)); } } @@ -1341,6 +1357,9 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste) auto findCanvasImageId = poseAttributes.find("canvasImageId"); if (findCanvasImageId != poseAttributes.end()) newPose.turnaroundImageId = QUuid(findCanvasImageId->second); + auto findYtranslationScale = poseAttributes.find("yTranslationScale"); + if (findYtranslationScale != poseAttributes.end()) + newPose.yTranslationScale = findYtranslationScale->second.toFloat(); newPose.frames = poseIt.second; oldNewIdMap[QUuid(valueOfKeyInMapOrEmpty(poseAttributes, "id"))] = newPoseId; poseIdList.push_back(newPoseId); @@ -2875,7 +2894,7 @@ void Document::generateMotions() m_motionsGenerator = new MotionsGenerator(rigType, rigBones, rigWeights, currentRiggedOutcome()); bool hasDirtyMotion = false; for (const auto &pose: poseMap) { - m_motionsGenerator->addPoseToLibrary(pose.first, pose.second.frames); + m_motionsGenerator->addPoseToLibrary(pose.first, pose.second.frames, pose.second.yTranslationScale); } for (auto &motion: motionMap) { if (motion.second.dirty) { diff --git a/src/document.h b/src/document.h index 4459c9ee..03d08ed0 100644 --- a/src/document.h +++ b/src/document.h @@ -198,6 +198,7 @@ public: QString name; bool dirty = true; QUuid turnaroundImageId; + float yTranslationScale = 1.0; std::vector, std::map>>> frames; // pair void updatePreviewMesh(MeshLoader *previewMesh) { @@ -210,6 +211,10 @@ public: return nullptr; return new MeshLoader(*m_previewMesh); } + bool yTranslationScaleAdjusted() const + { + return fabs(yTranslationScale - 1.0) >= 0.01; + } private: Q_DISABLE_COPY(Pose); MeshLoader *m_previewMesh = nullptr; @@ -434,6 +439,7 @@ signals: void poseNameChanged(QUuid poseId); void poseFramesChanged(QUuid poseId); void poseTurnaroundImageIdChanged(QUuid poseId); + void poseYtranslationScaleChanged(QUuid poseId); void posePreviewChanged(QUuid poseId); void motionAdded(QUuid motionId); void motionRemoved(QUuid motionId); @@ -612,10 +618,12 @@ public slots: void enableWeld(bool enabled); void setRigType(RigType toRigType); void addPose(QUuid poseId, QString name, std::vector, std::map>>> frames, - QUuid turnaroundImageId); + QUuid turnaroundImageId, + float yTranslationScale); void removePose(QUuid poseId); void setPoseFrames(QUuid poseId, std::vector, std::map>>> frames); void setPoseTurnaroundImageId(QUuid poseId, QUuid imageId); + void setPoseYtranslationScale(QUuid poseId, float scale); void renamePose(QUuid poseId, QString name); void addMotion(QUuid motionId, QString name, std::vector clips); void removeMotion(QUuid motionId); diff --git a/src/motioneditwidget.cpp b/src/motioneditwidget.cpp index 9047324c..f4ccaaa7 100644 --- a/src/motioneditwidget.cpp +++ b/src/motioneditwidget.cpp @@ -251,7 +251,7 @@ void MotionEditWidget::generatePreviews() m_previewsGenerator = new MotionsGenerator(m_document->rigType, rigBones, rigWeights, m_document->currentRiggedOutcome()); for (const auto &pose: m_document->poseMap) - m_previewsGenerator->addPoseToLibrary(pose.first, pose.second.frames); + m_previewsGenerator->addPoseToLibrary(pose.first, pose.second.frames, pose.second.yTranslationScale); for (const auto &motion: m_document->motionMap) m_previewsGenerator->addMotionToLibrary(motion.first, motion.second.clips); m_previewsGenerator->addMotionToLibrary(QUuid(), m_timelineWidget->clips()); diff --git a/src/motionsgenerator.cpp b/src/motionsgenerator.cpp index 7cf47d2d..96ba70c8 100644 --- a/src/motionsgenerator.cpp +++ b/src/motionsgenerator.cpp @@ -27,9 +27,10 @@ MotionsGenerator::~MotionsGenerator() delete m_poser; } -void MotionsGenerator::addPoseToLibrary(const QUuid &poseId, const std::vector, std::map>>> &frames) +void MotionsGenerator::addPoseToLibrary(const QUuid &poseId, const std::vector, std::map>>> &frames, float yTranslationScale) { m_poses[poseId] = frames; + m_posesYtranslationScales[poseId] = yTranslationScale; } void MotionsGenerator::addMotionToLibrary(const QUuid &motionId, const std::vector &clips) @@ -231,6 +232,7 @@ const JointNodeTree &MotionsGenerator::poseJointNodeTree(const QUuid &poseId, in return findResult->second; const auto &frames = m_poses[poseId]; + const auto &posesYtranslationScale = m_posesYtranslationScales[poseId]; m_poser->reset(); if (frame < (int)frames.size()) { @@ -240,6 +242,7 @@ const JointNodeTree &MotionsGenerator::poseJointNodeTree(const QUuid &poseId, in std::map> translatedParameters; postDocument.toParameters(translatedParameters); m_poser->parameters() = translatedParameters; + m_poser->setYtranslationScale(posesYtranslationScale); } m_poser->commit(); auto insertResult = m_poseJointNodeTreeMap.insert({{poseId, frame}, m_poser->resultJointNodeTree()}); diff --git a/src/motionsgenerator.h b/src/motionsgenerator.h index 2cdf9557..ece3b107 100644 --- a/src/motionsgenerator.h +++ b/src/motionsgenerator.h @@ -19,7 +19,7 @@ public: const std::map *rigWeights, const Outcome &outcome); ~MotionsGenerator(); - void addPoseToLibrary(const QUuid &poseId, const std::vector, std::map>>> &frames); + void addPoseToLibrary(const QUuid &poseId, const std::vector, std::map>>> &frames, float yTranslationScale); void addMotionToLibrary(const QUuid &motionId, const std::vector &clips); void addRequirement(const QUuid &motionId); std::vector> takeResultPreviewMeshs(const QUuid &motionId); @@ -50,6 +50,7 @@ private: std::map m_rigWeights; Outcome m_outcome; std::map, std::map>>>> m_poses; + std::map m_posesYtranslationScales; std::map> m_motions; std::set m_requiredMotionIds; std::set m_generatedMotionIds; diff --git a/src/poseeditwidget.cpp b/src/poseeditwidget.cpp index 6a11d29f..0730176e 100644 --- a/src/poseeditwidget.cpp +++ b/src/poseeditwidget.cpp @@ -194,6 +194,11 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : connect(saveButton, &QPushButton::clicked, this, &PoseEditWidget::save); saveButton->setDefault(true); + QPushButton *setPoseSettingButton = new QPushButton(Theme::awesome()->icon(fa::gear), tr("")); + connect(setPoseSettingButton, &QPushButton::clicked, this, [=]() { + showPoseSettingPopup(mapFromGlobal(QCursor::pos())); + }); + QPushButton *changeReferenceSheet = new QPushButton(tr("Change Reference Sheet...")); connect(changeReferenceSheet, &QPushButton::clicked, this, &PoseEditWidget::changeTurnaround); connect(m_poseDocument, &PoseDocument::turnaroundChanged, @@ -261,6 +266,7 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : baseInfoLayout->addWidget(new QLabel(tr("Duration"))); baseInfoLayout->addWidget(m_durationEdit); baseInfoLayout->addStretch(); + baseInfoLayout->addWidget(setPoseSettingButton); baseInfoLayout->addWidget(changeReferenceSheet); baseInfoLayout->addWidget(saveButton); @@ -280,6 +286,7 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : connect(this, &PoseEditWidget::renamePose, m_document, &Document::renamePose); connect(this, &PoseEditWidget::setPoseFrames, m_document, &Document::setPoseFrames); connect(this, &PoseEditWidget::setPoseTurnaroundImageId, m_document, &Document::setPoseTurnaroundImageId); + connect(this, &PoseEditWidget::setPoseYtranslationScale, m_document, &Document::setPoseYtranslationScale); updatePoseDocument(); updateTitle(); @@ -308,6 +315,43 @@ void PoseEditWidget::updateSideButtonState(QPushButton *button, bool visible) button->setStyleSheet("QPushButton {color: #252525}"); } +void PoseEditWidget::showPoseSettingPopup(const QPoint &pos) +{ + QMenu popupMenu; + + QWidget *popup = new QWidget; + + FloatNumberWidget *yTranslationScaleWidget = new FloatNumberWidget; + yTranslationScaleWidget->setItemName(tr("Height Adjustment Scale")); + yTranslationScaleWidget->setRange(0, 1); + yTranslationScaleWidget->setValue(m_yTranslationScale); + + connect(yTranslationScaleWidget, &FloatNumberWidget::valueChanged, [&](float value) { + m_yTranslationScale = value; + setUnsaveState(); + }); + + QPushButton *yTranslationScaleEraser = new QPushButton(QChar(fa::eraser)); + Theme::initAwesomeToolButton(yTranslationScaleEraser); + + connect(yTranslationScaleEraser, &QPushButton::clicked, [=]() { + yTranslationScaleWidget->setValue(1.0); + }); + + QHBoxLayout *yTranslationScaleLayout = new QHBoxLayout; + yTranslationScaleLayout->addWidget(yTranslationScaleEraser); + yTranslationScaleLayout->addWidget(yTranslationScaleWidget); + + popup->setLayout(yTranslationScaleLayout); + + QWidgetAction *action = new QWidgetAction(this); + action->setDefaultWidget(popup); + + popupMenu.addAction(action); + + popupMenu.exec(mapToGlobal(pos)); +} + void PoseEditWidget::showFramesSettingPopup(const QPoint &pos) { QMenu popupMenu; @@ -315,13 +359,13 @@ void PoseEditWidget::showFramesSettingPopup(const QPoint &pos) QWidget *popup = new QWidget; QSpinBox *framesEdit = new QSpinBox(); - framesEdit->setMaximum(60); + framesEdit->setMaximum(m_frames.size()); framesEdit->setMinimum(1); framesEdit->setSingleStep(1); framesEdit->setValue(m_frames.size()); connect(framesEdit, static_cast(&QSpinBox::valueChanged), this, [=](int value) { - setFrameCount(value); + setCurrentFrame(value); }); QFormLayout *formLayout = new QFormLayout; @@ -361,21 +405,6 @@ void PoseEditWidget::syncFrameFromCurrent() updateFramesDurations(); } -void PoseEditWidget::setFrameCount(int count) -{ - if (count == (int)m_frames.size()) - return; - - setUnsaveState(); - count = std::max(count, 1); - m_frames.resize(count); - updateFramesDurations(); - updateFramesSettingButton(); - if (m_currentFrame >= count) { - setCurrentFrame(count - 1); - } -} - void PoseEditWidget::updateFramesDurations() { if (m_frames.empty()) @@ -604,6 +633,11 @@ void PoseEditWidget::setEditPoseTurnaroundImageId(QUuid imageId) m_poseDocument->updateTurnaround(*image); } +void PoseEditWidget::setEditPoseYtranslationScale(float yTranslationScale) +{ + m_yTranslationScale = yTranslationScale; +} + void PoseEditWidget::clearUnsaveState() { m_unsaved = false; @@ -620,11 +654,12 @@ void PoseEditWidget::save() { if (m_poseId.isNull()) { m_poseId = QUuid::createUuid(); - emit addPose(m_poseId, m_nameEdit->text(), m_frames, m_imageId); + emit addPose(m_poseId, m_nameEdit->text(), m_frames, m_imageId, m_yTranslationScale); } else if (m_unsaved) { emit renamePose(m_poseId, m_nameEdit->text()); emit setPoseFrames(m_poseId, m_frames); emit setPoseTurnaroundImageId(m_poseId, m_imageId); + emit setPoseYtranslationScale(m_poseId, m_yTranslationScale); } clearUnsaveState(); } diff --git a/src/poseeditwidget.h b/src/poseeditwidget.h index b60c664f..3a436b62 100644 --- a/src/poseeditwidget.h +++ b/src/poseeditwidget.h @@ -19,10 +19,11 @@ class PoseEditWidget : public QDialog { Q_OBJECT signals: - void addPose(QUuid poseId, QString name, std::vector, std::map>>> frames, QUuid turnaroundImageId); + void addPose(QUuid poseId, QString name, std::vector, std::map>>> frames, QUuid turnaroundImageId, float yTranslationScale); void removePose(QUuid poseId); void setPoseFrames(QUuid poseId, std::vector, std::map>>> frames); void setPoseTurnaroundImageId(QUuid poseId, QUuid imageId); + void setPoseYtranslationScale(QUuid poseId, float scale); void renamePose(QUuid poseId, QString name); void parametersAdjusted(); public: @@ -38,10 +39,10 @@ public slots: void setEditPoseName(QString name); void setEditPoseFrames(std::vector, std::map>>> frames); void setEditPoseTurnaroundImageId(QUuid imageId); + void setEditPoseYtranslationScale(float yTranslationScale); void setCurrentFrame(int frame); void insertFrameAfterCurrentFrame(); void removeCurrentFrame(); - void setFrameCount(int count); void setDuration(float duration); void updateTitle(); void save(); @@ -51,6 +52,7 @@ public slots: private slots: void updateFramesSettingButton(); void showFramesSettingPopup(const QPoint &pos); + void showPoseSettingPopup(const QPoint &pos); void updateFramesDurations(); protected: QSize sizeHint() const override; @@ -71,6 +73,7 @@ private: QUuid m_poseId; bool m_unsaved = false; QUuid m_imageId; + float m_yTranslationScale = 1.0; QLineEdit *m_nameEdit = nullptr; QDoubleSpinBox *m_durationEdit = nullptr; PoseDocument *m_poseDocument = nullptr; diff --git a/src/posemanagewidget.cpp b/src/posemanagewidget.cpp index 14d9b416..92f85288 100644 --- a/src/posemanagewidget.cpp +++ b/src/posemanagewidget.cpp @@ -78,6 +78,7 @@ void PoseManageWidget::showPoseDialog(QUuid poseId) poseEditWidget->setEditPoseName(pose->name); poseEditWidget->setEditPoseFrames(pose->frames); poseEditWidget->setEditPoseTurnaroundImageId(pose->turnaroundImageId); + poseEditWidget->setEditPoseYtranslationScale(pose->yTranslationScale); poseEditWidget->clearUnsaveState(); } } diff --git a/src/poser.cpp b/src/poser.cpp index 5a91f66c..47548a32 100644 --- a/src/poser.cpp +++ b/src/poser.cpp @@ -15,6 +15,11 @@ Poser::~Poser() { } +void Poser::setYtranslationScale(float scale) +{ + m_yTranslationScale = scale; +} + int Poser::findBoneIndex(const QString &name) { auto findResult = m_boneNameToIndexMap.find(name); diff --git a/src/poser.h b/src/poser.h index cda44074..9a8fc360 100644 --- a/src/poser.h +++ b/src/poser.h @@ -17,6 +17,7 @@ public: const std::vector &resultNodes() const; const JointNodeTree &resultJointNodeTree() const; std::map> ¶meters(); + void setYtranslationScale(float scale); virtual void commit(); void reset(); static void fetchChains(const std::vector &boneNames, std::map> &chains); @@ -25,6 +26,7 @@ protected: std::map m_boneNameToIndexMap; JointNodeTree m_jointNodeTree; std::map> m_parameters; + float m_yTranslationScale = 1.0; }; #endif