Fix pose setting: Height above ground level.

Root bone will have heightAboveGroundLevel parameter to indicate the distance between ground and foot. It's a relative value, comparing with the distance of the first spine head position and foot.
master
Jeremy Hu 2018-11-11 00:05:49 +08:00
parent 205a9f512f
commit 9231ce9162
10 changed files with 118 additions and 56 deletions

View File

@ -9,27 +9,8 @@ AnimalPoser::AnimalPoser(const std::vector<RiggerBone> &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 reJoints("^([a-zA-Z]+\\d*)_Joint\\d+$");
QRegularExpression reSpine("^([a-zA-Z]+)\\d*$"); QRegularExpression reSpine("^([a-zA-Z]+)\\d*$");
std::map<QString, std::vector<QString>> chains; std::map<QString, std::vector<QString>> chains;
@ -56,12 +37,63 @@ void AnimalPoser::resolveTranslation()
std::sort(chain.second.begin(), chain.second.end(), [](const QString &first, const QString &second) { std::sort(chain.second.begin(), chain.second.end(), [](const QString &first, const QString &second) {
return first < second; return first < second;
}); });
//qDebug() << "Chain:";
//for (const auto &chainJoint: chain.second) {
// qDebug() << chainJoint;
//}
resolveChainRotation(chain.second); 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<float>::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<float>::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<bool, QVector3D> AnimalPoser::findQVector3DFromMap(const std::map<QString, QString> &map, const QString &xName, const QString &yName, const QString &zName) std::pair<bool, QVector3D> AnimalPoser::findQVector3DFromMap(const std::map<QString, QString> &map, const QString &xName, const QString &yName, const QString &zName)
@ -231,7 +263,7 @@ void AnimalPoser::resolveChainRotation(const std::vector<QString> &limbBoneNames
void AnimalPoser::commit() void AnimalPoser::commit()
{ {
resolveTranslation(); resolveTransform();
Poser::commit(); Poser::commit();
} }

View File

@ -11,7 +11,7 @@ public:
void commit() override; void commit() override;
private: private:
void resolveTranslation(); void resolveTransform();
void resolveChainRotation(const std::vector<QString> &limbBoneNames); void resolveChainRotation(const std::vector<QString> &limbBoneNames);
std::pair<bool, QVector3D> findQVector3DFromMap(const std::map<QString, QString> &map, const QString &xName, const QString &yName, const QString &zName); std::pair<bool, QVector3D> findQVector3DFromMap(const std::map<QString, QString> &map, const QString &xName, const QString &yName, const QString &zName);
std::pair<bool, std::pair<QVector3D, QVector3D>> findBonePositionsFromParameters(const std::map<QString, QString> &map); std::pair<bool, std::pair<QVector3D, QVector3D>> findBonePositionsFromParameters(const std::map<QString, QString> &map);

View File

@ -595,6 +595,8 @@ QString AnimalRigger::namingChain(const QString &baseName, SkeletonSide side, in
QString AnimalRigger::namingSpine(int spineOrder) QString AnimalRigger::namingSpine(int spineOrder)
{ {
if (1 == spineOrder)
return Rigger::firstSpineBoneName;
return "Spine" + QString::number(spineOrder); return "Spine" + QString::number(spineOrder);
} }

View File

@ -19,10 +19,10 @@ protected:
BoneMark translateBoneMark(BoneMark boneMark) override; BoneMark translateBoneMark(BoneMark boneMark) override;
private: private:
bool collectJontsForChain(int markIndex, std::vector<int> &jointMarkIndicies); bool collectJontsForChain(int markIndex, std::vector<int> &jointMarkIndicies);
QString namingSpine(int spineOrder); static QString namingSpine(int spineOrder);
QString namingConnector(const QString &spineName, const QString &chainName); static QString namingConnector(const QString &spineName, const QString &chainName);
QString namingChain(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide, int jointOrder); static 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 namingChainPrefix(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide);
QVector3D findExtremPointFrom(const std::set<int> &verticies, const QVector3D &from); QVector3D findExtremPointFrom(const std::set<int> &verticies, const QVector3D &from);
}; };

View File

@ -25,6 +25,7 @@ void JointNodeTree::reset()
{ {
for (auto &node: m_boneNodes) { for (auto &node: m_boneNodes) {
node.rotation = QQuaternion(); node.rotation = QQuaternion();
node.translation = node.bindTranslation;
} }
} }
@ -77,6 +78,7 @@ JointNodeTree::JointNodeTree(const std::vector<RiggerBone> *resultRigBones)
} else { } else {
node.translation = node.position; node.translation = node.position;
} }
node.bindTranslation = node.translation;
QMatrix4x4 translateMatrix; QMatrix4x4 translateMatrix;
translateMatrix.translate(node.translation); translateMatrix.translate(node.translation);
node.transformMatrix = parentTransformMatrix * translateMatrix; node.transformMatrix = parentTransformMatrix * translateMatrix;

View File

@ -10,6 +10,7 @@ struct JointNode
int parentIndex; int parentIndex;
QString name; QString name;
QVector3D position; QVector3D position;
QVector3D bindTranslation;
QVector3D translation; QVector3D translation;
QQuaternion rotation; QQuaternion rotation;
QMatrix4x4 transformMatrix; QMatrix4x4 transformMatrix;

View File

@ -159,7 +159,7 @@ void PoseDocument::fromParameters(const std::vector<RiggerBone> *rigBones,
if (&m_riggerBones != rigBones) if (&m_riggerBones != rigBones)
m_riggerBones = *rigBones; m_riggerBones = *rigBones;
QVector3D rootTranslation; float heightAboveGroundLevel = 0;
std::vector<RiggerBone> bones = *rigBones; std::vector<RiggerBone> bones = *rigBones;
for (auto &bone: bones) { for (auto &bone: bones) {
const auto findParameterResult = parameters.find(bone.name); const auto findParameterResult = parameters.find(bone.name);
@ -205,25 +205,17 @@ void PoseDocument::fromParameters(const std::vector<RiggerBone> *rigBones,
if (findRoot != parameters.end()) { if (findRoot != parameters.end()) {
const auto &map = findRoot->second; const auto &map = findRoot->second;
{ {
auto findXResult = map.find("translateX"); auto findHeightAboveGroundLevel = map.find("heightAboveGroundLevel");
auto findYResult = map.find("translateY"); if (findHeightAboveGroundLevel != map.end()) {
auto findZResult = map.find("translateZ"); heightAboveGroundLevel = valueOfKeyInMapOrEmpty(map, "heightAboveGroundLevel").toFloat();
if (findXResult != map.end() ||
findYResult != map.end() ||
findZResult != map.end()) {
rootTranslation = {
valueOfKeyInMapOrEmpty(map, "translateX").toFloat(),
valueOfKeyInMapOrEmpty(map, "translateY").toFloat(),
valueOfKeyInMapOrEmpty(map, "translateZ").toFloat()
};
} }
} }
} }
updateRigBones(&bones, rootTranslation); updateRigBones(&bones, heightAboveGroundLevel);
} }
void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const QVector3D &rootTranslation) void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const float heightAboveGroundLevel)
{ {
reset(); reset();
@ -349,7 +341,10 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
auto &groundPart = partMap[m_groundPartId]; auto &groundPart = partMap[m_groundPartId];
groundPart.id = 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<QUuid, QUuid> groundNodesPair; std::pair<QUuid, QUuid> groundNodesPair;
{ {
@ -358,7 +353,7 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
node.id = QUuid::createUuid(); node.id = QUuid::createUuid();
node.setRadius(m_groundPlaneHalfThickness); node.setRadius(m_groundPlaneHalfThickness);
node.x = -100; node.x = -100;
node.y = groundY + m_groundPlaneHalfThickness; node.y = groundNodeY;
node.z = -100; node.z = -100;
nodeMap[node.id] = node; nodeMap[node.id] = node;
newAddedNodeIds.insert(node.id); newAddedNodeIds.insert(node.id);
@ -371,7 +366,7 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
node.id = QUuid::createUuid(); node.id = QUuid::createUuid();
node.setRadius(m_groundPlaneHalfThickness); node.setRadius(m_groundPlaneHalfThickness);
node.x = 100; node.x = 100;
node.y = groundY + m_groundPlaneHalfThickness; node.y = groundNodeY;
node.z = 100; node.z = 100;
nodeMap[node.id] = node; nodeMap[node.id] = node;
newAddedNodeIds.insert(node.id); newAddedNodeIds.insert(node.id);
@ -432,7 +427,7 @@ void PoseDocument::setNodeOrigin(QUuid nodeId, float x, float y, float z)
emit parametersChanged(); emit parametersChanged();
} }
float PoseDocument::findGroundY() const 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) {
@ -445,9 +440,31 @@ float PoseDocument::findGroundY() const
return maxY; 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<QString, std::map<QString, QString>> &parameters, const std::set<QUuid> &limitNodeIds) const void PoseDocument::toParameters(std::map<QString, std::map<QString, QString>> &parameters, const std::set<QUuid> &limitNodeIds) const
{ {
float translateY = 0; float heightAboveGroundLevel = 0;
auto findGroundEdge = edgeMap.find(m_groundEdgeId); auto findGroundEdge = edgeMap.find(m_groundEdgeId);
if (findGroundEdge != edgeMap.end()) { if (findGroundEdge != edgeMap.end()) {
const auto &nodeIds = findGroundEdge->second.nodeIds; const auto &nodeIds = findGroundEdge->second.nodeIds;
@ -456,15 +473,19 @@ void PoseDocument::toParameters(std::map<QString, std::map<QString, QString>> &p
auto findSecondNode = nodeMap.find(nodeIds[1]); auto findSecondNode = nodeMap.find(nodeIds[1]);
if (findFirstNode != nodeMap.end() && findSecondNode != nodeMap.end()) { if (findFirstNode != nodeMap.end() && findSecondNode != nodeMap.end()) {
if (limitNodeIds.empty() || limitNodeIds.find(findFirstNode->first) != limitNodeIds.end() || if (limitNodeIds.empty() || limitNodeIds.find(findFirstNode->first) != limitNodeIds.end() ||
limitNodeIds.find(findSecondNode->first) != limitNodeIds.end()) limitNodeIds.find(findSecondNode->first) != limitNodeIds.end()) {
translateY = (findFirstNode->second.y + findSecondNode->second.y) / 2 - float myHeightAboveGroundLevel = (findFirstNode->second.y + findSecondNode->second.y) / 2 -
(findGroundY() + m_groundPlaneHalfThickness); (findFootBottomY() + m_groundPlaneHalfThickness);
float legHeight = findLegHeight();
if (legHeight > 0)
heightAboveGroundLevel = myHeightAboveGroundLevel / legHeight;
} }
} }
} }
if (!qFuzzyIsNull(translateY)) { }
if (!qFuzzyIsNull(heightAboveGroundLevel)) {
auto &boneParameter = parameters[Rigger::rootBoneName]; auto &boneParameter = parameters[Rigger::rootBoneName];
boneParameter["translateY"] = QString::number(translateY); boneParameter["heightAboveGroundLevel"] = QString::number(heightAboveGroundLevel);
} }
for (const auto &item: m_boneNameToIdsMap) { for (const auto &item: m_boneNameToIdsMap) {
const auto &boneNodeIdPair = item.second; const auto &boneNodeIdPair = item.second;

View File

@ -32,7 +32,7 @@ public:
void copyNodes(std::set<QUuid> nodeIdSet) const override; void copyNodes(std::set<QUuid> nodeIdSet) const override;
void updateTurnaround(const QImage &image); void updateTurnaround(const QImage &image);
void updateRigBones(const std::vector<RiggerBone> *rigBones, const QVector3D &rootTranslation=QVector3D(0, 0, 0)); void updateRigBones(const std::vector<RiggerBone> *rigBones, const float heightAboveGroundLevel=0.0);
void reset(); void reset();
void toParameters(std::map<QString, std::map<QString, QString>> &parameters, const std::set<QUuid> &limitNodeIds=std::set<QUuid>()) const; void toParameters(std::map<QString, std::map<QString, QString>> &parameters, const std::set<QUuid> &limitNodeIds=std::set<QUuid>()) const;
@ -48,7 +48,6 @@ 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);
float findGroundY() const;
void switchChainSide(const std::set<QUuid> nodeIds); void switchChainSide(const std::set<QUuid> nodeIds);
public: public:
@ -59,6 +58,9 @@ public:
private: private:
QString findBoneNameByNodeId(const QUuid &nodeId); QString findBoneNameByNodeId(const QUuid &nodeId);
float findFootBottomY() const;
float findFirstSpineY() const;
float findLegHeight() const;
std::map<QString, std::pair<QUuid, QUuid>> m_boneNameToIdsMap; std::map<QString, std::pair<QUuid, QUuid>> m_boneNameToIdsMap;
QUuid m_groundPartId; QUuid m_groundPartId;

View File

@ -7,6 +7,7 @@
size_t Rigger::m_maxCutOffSplitterExpandRound = 3; size_t Rigger::m_maxCutOffSplitterExpandRound = 3;
QString Rigger::rootBoneName = "Body"; QString Rigger::rootBoneName = "Body";
QString Rigger::firstSpineBoneName = "Spine1";
Rigger::Rigger(const std::vector<QVector3D> &verticesPositions, Rigger::Rigger(const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &inputTriangles) : const std::set<MeshSplitterTriangle> &inputTriangles) :

View File

@ -131,6 +131,7 @@ public:
const std::map<int, RiggerVertexWeights> &resultWeights(); const std::map<int, RiggerVertexWeights> &resultWeights();
virtual bool rig() = 0; virtual bool rig() = 0;
static QString rootBoneName; static QString rootBoneName;
static QString firstSpineBoneName;
protected: protected:
virtual bool validate() = 0; virtual bool validate() = 0;
virtual bool isCutOffSplitter(BoneMark boneMark) = 0; virtual bool isCutOffSplitter(BoneMark boneMark) = 0;