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 reSpine("^([a-zA-Z]+)\\d*$");
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) {
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<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)
@ -231,7 +263,7 @@ void AnimalPoser::resolveChainRotation(const std::vector<QString> &limbBoneNames
void AnimalPoser::commit()
{
resolveTranslation();
resolveTransform();
Poser::commit();
}

View File

@ -11,7 +11,7 @@ public:
void commit() override;
private:
void resolveTranslation();
void resolveTransform();
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, 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)
{
if (1 == spineOrder)
return Rigger::firstSpineBoneName;
return "Spine" + QString::number(spineOrder);
}

View File

@ -19,10 +19,10 @@ protected:
BoneMark translateBoneMark(BoneMark boneMark) override;
private:
bool collectJontsForChain(int markIndex, std::vector<int> &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<int> &verticies, const QVector3D &from);
};

View File

@ -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<RiggerBone> *resultRigBones)
} else {
node.translation = node.position;
}
node.bindTranslation = node.translation;
QMatrix4x4 translateMatrix;
translateMatrix.translate(node.translation);
node.transformMatrix = parentTransformMatrix * translateMatrix;

View File

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

View File

@ -159,7 +159,7 @@ void PoseDocument::fromParameters(const std::vector<RiggerBone> *rigBones,
if (&m_riggerBones != rigBones)
m_riggerBones = *rigBones;
QVector3D rootTranslation;
float heightAboveGroundLevel = 0;
std::vector<RiggerBone> bones = *rigBones;
for (auto &bone: bones) {
const auto findParameterResult = parameters.find(bone.name);
@ -205,25 +205,17 @@ void PoseDocument::fromParameters(const std::vector<RiggerBone> *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<RiggerBone> *rigBones, const QVector3D &rootTranslation)
void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const float heightAboveGroundLevel)
{
reset();
@ -349,7 +341,10 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *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<QUuid, QUuid> groundNodesPair;
{
@ -358,7 +353,7 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *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<RiggerBone> *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<float>::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<QString, std::map<QString, QString>> &parameters, const std::set<QUuid> &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<QString, std::map<QString, QString>> &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;

View File

@ -32,7 +32,7 @@ public:
void copyNodes(std::set<QUuid> nodeIdSet) const override;
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 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 setNodeOrigin(QUuid nodeId, float x, float y, float z);
float findGroundY() const;
void switchChainSide(const std::set<QUuid> nodeIds);
public:
@ -59,6 +58,9 @@ public:
private:
QString findBoneNameByNodeId(const QUuid &nodeId);
float findFootBottomY() const;
float findFirstSpineY() const;
float findLegHeight() const;
std::map<QString, std::pair<QUuid, QUuid>> m_boneNameToIdsMap;
QUuid m_groundPartId;

View File

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

View File

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