diff --git a/src/animalposer.cpp b/src/animalposer.cpp index 95d6d705..9ef75f2f 100644 --- a/src/animalposer.cpp +++ b/src/animalposer.cpp @@ -9,27 +9,8 @@ AnimalPoser::AnimalPoser(const std::vector &bones) : { } -void AnimalPoser::resolveTranslation() +void AnimalPoser::resolveTransform() { - for (const auto &item: parameters()) { - int boneIndex = findBoneIndex(item.first); - if (-1 == boneIndex) { - continue; - } - auto findTranslateXResult = item.second.find("translateX"); - auto findTranslateYResult = item.second.find("translateY"); - auto findTranslateZResult = item.second.find("translateZ"); - if (findTranslateXResult != item.second.end() || - findTranslateYResult != item.second.end() || - findTranslateZResult != item.second.end()) { - float x = valueOfKeyInMapOrEmpty(item.second, "translateX").toFloat(); - float y = valueOfKeyInMapOrEmpty(item.second, "translateY").toFloat(); - float z = valueOfKeyInMapOrEmpty(item.second, "translateZ").toFloat(); - QVector3D translation = {x, y, z}; - m_jointNodeTree.addTranslation(boneIndex, translation); - continue; - } - } QRegularExpression reJoints("^([a-zA-Z]+\\d*)_Joint\\d+$"); QRegularExpression reSpine("^([a-zA-Z]+)\\d*$"); std::map> chains; @@ -56,12 +37,63 @@ void AnimalPoser::resolveTranslation() std::sort(chain.second.begin(), chain.second.end(), [](const QString &first, const QString &second) { return first < second; }); - //qDebug() << "Chain:"; - //for (const auto &chainJoint: chain.second) { - // qDebug() << chainJoint; - //} 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; + + //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)); + } } std::pair AnimalPoser::findQVector3DFromMap(const std::map &map, const QString &xName, const QString &yName, const QString &zName) @@ -231,7 +263,7 @@ void AnimalPoser::resolveChainRotation(const std::vector &limbBoneNames void AnimalPoser::commit() { - resolveTranslation(); + resolveTransform(); Poser::commit(); } diff --git a/src/animalposer.h b/src/animalposer.h index 7f3c9250..45b148cc 100644 --- a/src/animalposer.h +++ b/src/animalposer.h @@ -11,7 +11,7 @@ public: void commit() override; private: - void resolveTranslation(); + void resolveTransform(); void resolveChainRotation(const std::vector &limbBoneNames); std::pair findQVector3DFromMap(const std::map &map, const QString &xName, const QString &yName, const QString &zName); std::pair> findBonePositionsFromParameters(const std::map &map); diff --git a/src/animalrigger.cpp b/src/animalrigger.cpp index bf311878..27c1da94 100644 --- a/src/animalrigger.cpp +++ b/src/animalrigger.cpp @@ -595,6 +595,8 @@ QString AnimalRigger::namingChain(const QString &baseName, SkeletonSide side, in QString AnimalRigger::namingSpine(int spineOrder) { + if (1 == spineOrder) + return Rigger::firstSpineBoneName; return "Spine" + QString::number(spineOrder); } diff --git a/src/animalrigger.h b/src/animalrigger.h index 9d929391..0a7c846e 100644 --- a/src/animalrigger.h +++ b/src/animalrigger.h @@ -19,10 +19,10 @@ protected: BoneMark translateBoneMark(BoneMark boneMark) override; private: bool collectJontsForChain(int markIndex, std::vector &jointMarkIndicies); - QString namingSpine(int spineOrder); - QString namingConnector(const QString &spineName, const QString &chainName); - QString namingChain(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide, int jointOrder); - QString namingChainPrefix(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide); + static QString namingSpine(int spineOrder); + static QString namingConnector(const QString &spineName, const QString &chainName); + static QString namingChain(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide, int jointOrder); + static QString namingChainPrefix(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide); QVector3D findExtremPointFrom(const std::set &verticies, const QVector3D &from); }; diff --git a/src/jointnodetree.cpp b/src/jointnodetree.cpp index eea19dd1..e6cb031d 100644 --- a/src/jointnodetree.cpp +++ b/src/jointnodetree.cpp @@ -25,6 +25,7 @@ void JointNodeTree::reset() { for (auto &node: m_boneNodes) { node.rotation = QQuaternion(); + node.translation = node.bindTranslation; } } @@ -77,6 +78,7 @@ JointNodeTree::JointNodeTree(const std::vector *resultRigBones) } else { node.translation = node.position; } + node.bindTranslation = node.translation; QMatrix4x4 translateMatrix; translateMatrix.translate(node.translation); node.transformMatrix = parentTransformMatrix * translateMatrix; diff --git a/src/jointnodetree.h b/src/jointnodetree.h index 689017a1..d9728270 100644 --- a/src/jointnodetree.h +++ b/src/jointnodetree.h @@ -10,6 +10,7 @@ struct JointNode int parentIndex; QString name; QVector3D position; + QVector3D bindTranslation; QVector3D translation; QQuaternion rotation; QMatrix4x4 transformMatrix; diff --git a/src/posedocument.cpp b/src/posedocument.cpp index 1dca089d..63070f9b 100644 --- a/src/posedocument.cpp +++ b/src/posedocument.cpp @@ -159,7 +159,7 @@ void PoseDocument::fromParameters(const std::vector *rigBones, if (&m_riggerBones != rigBones) m_riggerBones = *rigBones; - QVector3D rootTranslation; + float heightAboveGroundLevel = 0; std::vector bones = *rigBones; for (auto &bone: bones) { const auto findParameterResult = parameters.find(bone.name); @@ -205,25 +205,17 @@ void PoseDocument::fromParameters(const std::vector *rigBones, if (findRoot != parameters.end()) { const auto &map = findRoot->second; { - auto findXResult = map.find("translateX"); - auto findYResult = map.find("translateY"); - auto findZResult = map.find("translateZ"); - if (findXResult != map.end() || - findYResult != map.end() || - findZResult != map.end()) { - rootTranslation = { - valueOfKeyInMapOrEmpty(map, "translateX").toFloat(), - valueOfKeyInMapOrEmpty(map, "translateY").toFloat(), - valueOfKeyInMapOrEmpty(map, "translateZ").toFloat() - }; + auto findHeightAboveGroundLevel = map.find("heightAboveGroundLevel"); + if (findHeightAboveGroundLevel != map.end()) { + heightAboveGroundLevel = valueOfKeyInMapOrEmpty(map, "heightAboveGroundLevel").toFloat(); } } } - updateRigBones(&bones, rootTranslation); + updateRigBones(&bones, heightAboveGroundLevel); } -void PoseDocument::updateRigBones(const std::vector *rigBones, const QVector3D &rootTranslation) +void PoseDocument::updateRigBones(const std::vector *rigBones, const float heightAboveGroundLevel) { reset(); @@ -349,7 +341,10 @@ void PoseDocument::updateRigBones(const std::vector *rigBones, const auto &groundPart = partMap[m_groundPartId]; groundPart.id = m_groundPartId; - float groundY = findGroundY() + rootTranslation.y(); + float footBottomY = findFootBottomY(); + float legHeight = findLegHeight(); + float myHeightAboveGroundLevel = heightAboveGroundLevel * legHeight; + float groundNodeY = footBottomY + m_groundPlaneHalfThickness + myHeightAboveGroundLevel; std::pair groundNodesPair; { @@ -358,7 +353,7 @@ void PoseDocument::updateRigBones(const std::vector *rigBones, const node.id = QUuid::createUuid(); node.setRadius(m_groundPlaneHalfThickness); node.x = -100; - node.y = groundY + m_groundPlaneHalfThickness; + node.y = groundNodeY; node.z = -100; nodeMap[node.id] = node; newAddedNodeIds.insert(node.id); @@ -371,7 +366,7 @@ void PoseDocument::updateRigBones(const std::vector *rigBones, const node.id = QUuid::createUuid(); node.setRadius(m_groundPlaneHalfThickness); node.x = 100; - node.y = groundY + m_groundPlaneHalfThickness; + node.y = groundNodeY; node.z = 100; nodeMap[node.id] = node; newAddedNodeIds.insert(node.id); @@ -432,7 +427,7 @@ void PoseDocument::setNodeOrigin(QUuid nodeId, float x, float y, float z) emit parametersChanged(); } -float PoseDocument::findGroundY() const +float PoseDocument::findFootBottomY() const { auto maxY = std::numeric_limits::lowest(); for (const auto &nodeIt: nodeMap) { @@ -445,9 +440,31 @@ float PoseDocument::findGroundY() const return maxY; } +float PoseDocument::findLegHeight() const +{ + float firstSpineY = findFirstSpineY(); + float footBottomY = findFootBottomY(); + return std::abs(footBottomY - firstSpineY); +} + +float PoseDocument::findFirstSpineY() const +{ + const auto &findFirstSpine = m_boneNameToIdsMap.find(Rigger::firstSpineBoneName); + if (findFirstSpine == m_boneNameToIdsMap.end()) { + qDebug() << "Find first spine bone failed:" << Rigger::firstSpineBoneName; + return 0; + } + const SkeletonNode *firstSpineNode = findNode(findFirstSpine->second.first); + if (nullptr == firstSpineNode) { + qDebug() << "Find first spine node failed"; + return 0; + } + return firstSpineNode->y; +} + void PoseDocument::toParameters(std::map> ¶meters, const std::set &limitNodeIds) const { - float translateY = 0; + float heightAboveGroundLevel = 0; auto findGroundEdge = edgeMap.find(m_groundEdgeId); if (findGroundEdge != edgeMap.end()) { const auto &nodeIds = findGroundEdge->second.nodeIds; @@ -456,15 +473,19 @@ void PoseDocument::toParameters(std::map> &p auto findSecondNode = nodeMap.find(nodeIds[1]); if (findFirstNode != nodeMap.end() && findSecondNode != nodeMap.end()) { if (limitNodeIds.empty() || limitNodeIds.find(findFirstNode->first) != limitNodeIds.end() || - limitNodeIds.find(findSecondNode->first) != limitNodeIds.end()) - translateY = (findFirstNode->second.y + findSecondNode->second.y) / 2 - - (findGroundY() + m_groundPlaneHalfThickness); + limitNodeIds.find(findSecondNode->first) != limitNodeIds.end()) { + float myHeightAboveGroundLevel = (findFirstNode->second.y + findSecondNode->second.y) / 2 - + (findFootBottomY() + m_groundPlaneHalfThickness); + float legHeight = findLegHeight(); + if (legHeight > 0) + heightAboveGroundLevel = myHeightAboveGroundLevel / legHeight; + } } } } - if (!qFuzzyIsNull(translateY)) { + if (!qFuzzyIsNull(heightAboveGroundLevel)) { auto &boneParameter = parameters[Rigger::rootBoneName]; - boneParameter["translateY"] = QString::number(translateY); + boneParameter["heightAboveGroundLevel"] = QString::number(heightAboveGroundLevel); } for (const auto &item: m_boneNameToIdsMap) { const auto &boneNodeIdPair = item.second; diff --git a/src/posedocument.h b/src/posedocument.h index 5e4ad536..d723110c 100644 --- a/src/posedocument.h +++ b/src/posedocument.h @@ -32,7 +32,7 @@ public: void copyNodes(std::set nodeIdSet) const override; void updateTurnaround(const QImage &image); - void updateRigBones(const std::vector *rigBones, const QVector3D &rootTranslation=QVector3D(0, 0, 0)); + void updateRigBones(const std::vector *rigBones, const float heightAboveGroundLevel=0.0); void reset(); void toParameters(std::map> ¶meters, const std::set &limitNodeIds=std::set()) const; @@ -48,7 +48,6 @@ public slots: void moveNodeBy(QUuid nodeId, float x, float y, float z); void setNodeOrigin(QUuid nodeId, float x, float y, float z); - float findGroundY() const; void switchChainSide(const std::set nodeIds); public: @@ -59,6 +58,9 @@ public: private: QString findBoneNameByNodeId(const QUuid &nodeId); + float findFootBottomY() const; + float findFirstSpineY() const; + float findLegHeight() const; std::map> m_boneNameToIdsMap; QUuid m_groundPartId; diff --git a/src/rigger.cpp b/src/rigger.cpp index 7d5fe0a6..1c0475c8 100644 --- a/src/rigger.cpp +++ b/src/rigger.cpp @@ -7,6 +7,7 @@ size_t Rigger::m_maxCutOffSplitterExpandRound = 3; QString Rigger::rootBoneName = "Body"; +QString Rigger::firstSpineBoneName = "Spine1"; Rigger::Rigger(const std::vector &verticesPositions, const std::set &inputTriangles) : diff --git a/src/rigger.h b/src/rigger.h index 8c5c7cd1..9cc848bb 100644 --- a/src/rigger.h +++ b/src/rigger.h @@ -131,6 +131,7 @@ public: const std::map &resultWeights(); virtual bool rig() = 0; static QString rootBoneName; + static QString firstSpineBoneName; protected: virtual bool validate() = 0; virtual bool isCutOffSplitter(BoneMark boneMark) = 0;