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
parent
205a9f512f
commit
9231ce9162
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -10,6 +10,7 @@ struct JointNode
|
|||
int parentIndex;
|
||||
QString name;
|
||||
QVector3D position;
|
||||
QVector3D bindTranslation;
|
||||
QVector3D translation;
|
||||
QQuaternion rotation;
|
||||
QMatrix4x4 transformMatrix;
|
||||
|
|
|
@ -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>> ¶meters, 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;
|
||||
|
|
|
@ -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>> ¶meters, 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;
|
||||
|
|
|
@ -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) :
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue