Improve the quality of main body resolving.
master
Jeremy Hu 2018-11-07 16:02:55 +08:00
parent 2c8975b1cd
commit 1543c1a6c7
8 changed files with 153 additions and 53 deletions

View File

@ -154,6 +154,8 @@ void AnimalPoser::resolveChainRotation(const std::vector<QString> &limbBoneNames
float targetMiddleBoneLength = (middleBone.headPosition - middleBone.tailPosition).length();
float targetLimbLength = targetBeginBoneLength + targetMiddleBoneLength;
//qDebug() << beginBoneName << "targetLimbLength:" << targetLimbLength << "matchLimbLength:" << matchLimbLength;
float targetDistanceBetweenBeginAndEndBones = matchDistanceBetweenBeginAndEndBones * (targetLimbLength / matchLimbLength);
QVector3D targetEndBoneStartPosition = beginBone.headPosition + matchDirectionBetweenBeginAndEndPones * targetDistanceBetweenBeginAndEndBones;
@ -177,12 +179,15 @@ void AnimalPoser::resolveChainRotation(const std::vector<QString> &limbBoneNames
auto oldBeginBoneDirection = (beginBone.tailPosition - beginBone.headPosition).normalized();
auto newBeginBoneDirection = (targetMiddleBoneStartPosition - beginBone.headPosition).normalized();
//qDebug() << beginBoneName << "oldBeginBoneDirection:" << oldBeginBoneDirection << "newBeginBoneDirection:" << newBeginBoneDirection;
auto beginBoneRotation = QQuaternion::rotationTo(oldBeginBoneDirection, newBeginBoneDirection);
m_jointNodeTree.updateRotation(beginBoneIndex, beginBoneRotation);
auto oldMiddleBoneDirection = (middleBone.tailPosition - middleBone.headPosition).normalized();
auto newMiddleBoneDirection = (targetEndBoneStartPosition - targetMiddleBoneStartPosition).normalized();
//qDebug() << beginBoneName << "oldMiddleBoneDirection:" << oldMiddleBoneDirection << "newMiddleBoneDirection:" << newMiddleBoneDirection;
oldMiddleBoneDirection = beginBoneRotation.rotatedVector(oldMiddleBoneDirection);
//qDebug() << beginBoneName << "oldMiddleBoneDirection:" << oldMiddleBoneDirection << "after rotation";
auto middleBoneRotation = QQuaternion::rotationTo(oldMiddleBoneDirection, newMiddleBoneDirection);
m_jointNodeTree.updateRotation(middleBoneIndex, middleBoneRotation);

View File

@ -14,7 +14,7 @@ bool AnimalRigger::validate()
{
if (m_marksMap.empty()) {
m_messages.push_back(std::make_pair(QtCriticalMsg,
tr("Please tell me where is the neck, limbs and joints by mark the nodes from context menu")));
tr("Please mark the neck, limbs and joints from the context menu")));
return false;
}
@ -207,6 +207,7 @@ bool AnimalRigger::rig()
resolveBoundingBox(bodyVerticies, xMin, xMax, yMin, yMax, zMin, zMax);
isMainBodyVerticalAligned = fabs(yMax.y() - yMin.y()) > fabs(zMax.z() - zMin.z());
}
qDebug() << "isMainBodyVerticalAligned:" << isMainBodyVerticalAligned;
// Collect all branchs
auto neckIndicies = m_marksMap.find(std::make_pair(BoneMark::Neck, SkeletonSide::None));
@ -299,7 +300,7 @@ bool AnimalRigger::rig()
}
}
if (countOfChains <= 0) {
qDebug() << "Should not come here, there must be at least one limb";
qDebug() << "Should not come here, there must be at least one chain";
break;
}
@ -311,7 +312,7 @@ bool AnimalRigger::rig()
}
if (rawSpineNodes.empty()) {
qDebug() << "Couldn't find limbs to create a spine";
qDebug() << "Couldn't find chain to create a spine";
return false;
}
@ -355,7 +356,7 @@ bool AnimalRigger::rig()
auto remainingSpineVerticies = bodyVerticies;
const std::vector<QColor> twoColorsForSpine = {BoneMarkToColor(BoneMark::Neck), BoneMarkToColor(BoneMark::Tail)};
const std::vector<QColor> twoColorsForLimb = {BoneMarkToColor(BoneMark::Joint), BoneMarkToColor(BoneMark::Limb)};
const std::vector<QColor> twoColorsForChain = {BoneMarkToColor(BoneMark::Joint), BoneMarkToColor(BoneMark::Limb)};
int spineGenerateOrder = 1;
std::map<std::pair<QString, SkeletonSide>, int> chainOrderMapBySide;
for (int spineNodeIndex = 0; spineNodeIndex < (int)spineNodes.size(); ++spineNodeIndex) {
@ -402,11 +403,7 @@ bool AnimalRigger::rig()
tailPosition = spineNodes[spineNodeIndex + 1].position;
} else {
spineBoneVertices = remainingSpineVerticies;
if (isMainBodyVerticalAligned) {
tailPosition = findMaxY(spineBoneVertices);
} else {
tailPosition = findMaxZ(spineBoneVertices);
}
tailPosition = findExtremPointFrom(spineBoneVertices, spineNode.position);
}
QVector3D spineBoneHeadPosition = averagePosition(spineBoneVertices);
@ -428,7 +425,7 @@ bool AnimalRigger::rig()
addVerticesToWeights(spineBoneVertices, spineBone.index);
boneIndexMap[spineBone.name] = spineBone.index;
//qDebug() << spineBone.name << "head:" << spineBone.headPosition << "tail:" << spineBone.tailPosition;
qDebug() << "Added spine:" << spineBone.name << "head:" << spineBone.headPosition << "tail:" << spineBone.tailPosition;
if (1 == spineGenerateOrder) {
m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition = spineBone.headPosition;
@ -450,6 +447,7 @@ bool AnimalRigger::rig()
ribBone.index = m_resultBones.size() - 1;
ribBone.name = namingConnector(spineName, chainName);
ribBone.headPosition = spineBoneHeadPosition;
qDebug() << "Added connector:" << ribBone.name;
boneIndexMap[ribBone.name] = ribBone.index;
if (1 == spineGenerateOrder) {
m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(ribBone.index);
@ -462,12 +460,12 @@ bool AnimalRigger::rig()
m_jointErrorItems.push_back(chainName);
}
//qDebug() << "Limb markIndex:" << limbMarkIndex << " joints:" << jointMarkIndicies.size();
qDebug() << "Adding chain:" << chainName << " joints:" << jointMarkIndicies.size();
int jointGenerateOrder = 1;
auto boneColor = [&]() {
return twoColorsForLimb[jointGenerateOrder % 2];
return twoColorsForChain[jointGenerateOrder % 2];
};
auto addToParentBone = [&](QVector3D headPosition, SkeletonSide side, int boneIndex) {
if (1 == jointGenerateOrder) {
@ -507,21 +505,8 @@ bool AnimalRigger::rig()
lastJointBoneVerticies = remainingLimbVertices;
}
// Calculate the tail position from remaining verticies
std::vector<QVector3D> extremCoords(6, jointPositions.back());
resolveBoundingBox(lastJointBoneVerticies, extremCoords[0], extremCoords[1], extremCoords[2], extremCoords[3], extremCoords[4], extremCoords[5]);
float maxDistance2 = std::numeric_limits<float>::min();
QVector3D choosenExtreamCoord = jointPositions.back();
for (size_t i = 0; i < 6; ++i) {
const auto &position = extremCoords[i];
auto length2 = (position - jointPositions.back()).lengthSquared();
if (length2 >= maxDistance2) {
maxDistance2 = length2;
choosenExtreamCoord = position;
}
}
jointPositions.push_back(choosenExtreamCoord);
jointPositions.push_back(findExtremPointFrom(lastJointBoneVerticies, jointPositions.back()));
QVector3D lastPosition = spineBoneHeadPosition;
for (jointGenerateOrder = 1; jointGenerateOrder <= (int)jointMarkIndicies.size(); ++jointGenerateOrder) {
int jointMarkIndex = jointMarkIndicies[jointGenerateOrder - 1];
const auto &jointMark = m_marks[jointMarkIndex];
@ -567,8 +552,6 @@ bool AnimalRigger::rig()
boneIndexMap[jointBone.name] = jointBone.index;
addToParentBone(jointBone.headPosition, chainMark.boneSide, jointBone.index);
lastPosition = jointPositions[jointGenerateOrder - 1];
}
++chainGenerateOrder;
@ -585,6 +568,23 @@ bool AnimalRigger::rig()
return true;
}
QVector3D AnimalRigger::findExtremPointFrom(const std::set<int> &verticies, const QVector3D &from)
{
std::vector<QVector3D> extremCoords(6, from);
resolveBoundingBox(verticies, extremCoords[0], extremCoords[1], extremCoords[2], extremCoords[3], extremCoords[4], extremCoords[5]);
float maxDistance2 = std::numeric_limits<float>::min();
QVector3D choosenExtreamCoord = from;
for (size_t i = 0; i < 6; ++i) {
const auto &position = extremCoords[i];
auto length2 = (position - from).lengthSquared();
if (length2 >= maxDistance2) {
maxDistance2 = length2;
choosenExtreamCoord = position;
}
}
return choosenExtreamCoord;
}
QString AnimalRigger::namingChain(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide, int jointOrder)
{
return namingChainPrefix(baseName, side, orderInSide, totalInSide) + "_Joint" + QString::number(jointOrder);

View File

@ -23,6 +23,7 @@ private:
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);
QVector3D findExtremPointFrom(const std::set<int> &verticies, const QVector3D &from);
};
#endif

View File

@ -19,7 +19,7 @@ QColor BoneMarkToColor(BoneMark mark) \
{ \
switch (mark) { \
case BoneMark::Neck: \
return QColor(0x51, 0xba, 0xf2); \
return QColor(0xfd, 0x64, 0x61); \
case BoneMark::Limb: \
return QColor(0x29, 0xfd, 0x2f); \
case BoneMark::Tail: \

View File

@ -5,6 +5,7 @@
const float PoseDocument::m_nodeRadius = 0.01;
const float PoseDocument::m_groundPlaneHalfThickness = 0.01 / 4;
const bool PoseDocument::m_hideRootAndVirtual = true;
bool PoseDocument::hasPastableNodesInClipboard() const
{
@ -176,10 +177,13 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
auto &bonesPart = partMap[m_bonesPartId];
bonesPart.id = m_bonesPartId;
qDebug() << "rigBones size:" << rigBones->size();
std::vector<std::pair<int, int>> edgePairs;
for (size_t i = 1; i < rigBones->size(); ++i) {
for (size_t i = m_hideRootAndVirtual ? 1 : 0; i < rigBones->size(); ++i) {
const auto &bone = (*rigBones)[i];
for (const auto &child: bone.children) {
qDebug() << "Add pair:" << bone.name << "->" << (*rigBones)[child].name;
edgePairs.push_back({i, child});
}
}
@ -189,7 +193,7 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
auto findFirst = boneIndexToHeadNodeIdMap.find(edgePair.first);
if (findFirst == boneIndexToHeadNodeIdMap.end()) {
const auto &bone = (*rigBones)[edgePair.first];
if (!bone.name.startsWith("Virtual_")) {
if (!bone.name.startsWith("Virtual_") || !m_hideRootAndVirtual) {
SkeletonNode node;
node.partId = m_bonesPartId;
node.id = QUuid::createUuid();
@ -208,7 +212,7 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
auto findSecond = boneIndexToHeadNodeIdMap.find(edgePair.second);
if (findSecond == boneIndexToHeadNodeIdMap.end()) {
const auto &bone = (*rigBones)[edgePair.second];
if (!bone.name.startsWith("Virtual_")) {
if (!bone.name.startsWith("Virtual_") || !m_hideRootAndVirtual) {
SkeletonNode node;
node.partId = m_bonesPartId;
node.id = QUuid::createUuid();
@ -239,9 +243,9 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
nodeMap[secondNodeId].edgeIds.push_back(edge.id);
}
for (size_t i = 1; i < rigBones->size(); ++i) {
for (size_t i = m_hideRootAndVirtual ? 1 : 0; i < rigBones->size(); ++i) {
const auto &bone = (*rigBones)[i];
if (bone.name.startsWith("Virtual_"))
if (m_hideRootAndVirtual && bone.name.startsWith("Virtual_"))
continue;
if (bone.children.empty()) {
const QUuid &firstNodeId = boneIndexToHeadNodeIdMap[i];
@ -250,9 +254,9 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
node.partId = m_bonesPartId;
node.id = QUuid::createUuid();
node.setRadius(m_nodeRadius / 2);
node.x = bone.tailPosition.x() + 0.5;
node.y = -bone.tailPosition.y() + 0.5;
node.z = -bone.tailPosition.z() + 1;
node.x = fromOutcomeX(bone.tailPosition.x());
node.y = fromOutcomeY(bone.tailPosition.y());
node.z = fromOutcomeZ(bone.tailPosition.z());
nodeMap[node.id] = node;
newAddedNodeIds.insert(node.id);
m_boneNameToIdsMap[bone.name] = {firstNodeId, node.id};
@ -266,6 +270,8 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
newAddedEdgeIds.insert(edge.id);
nodeMap[firstNodeId].edgeIds.push_back(edge.id);
nodeMap[node.id].edgeIds.push_back(edge.id);
qDebug() << "Add pair:" << bone.name << "->" << "~";
continue;
}
for (const auto &child: bone.children) {
@ -273,6 +279,11 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
}
}
auto findRootNodeId = boneIndexToHeadNodeIdMap.find(0);
if (findRootNodeId != boneIndexToHeadNodeIdMap.end()) {
nodeMap[findRootNodeId->second].setRadius(m_nodeRadius * 2);
}
m_groundPartId = QUuid::createUuid();
auto &groundPart = partMap[m_groundPartId];
groundPart.id = m_groundPartId;
@ -401,11 +412,42 @@ void PoseDocument::toParameters(std::map<QString, std::map<QString, QString>> &p
auto findSecondNode = nodeMap.find(boneNodeIdPair.second);
if (findSecondNode == nodeMap.end())
continue;
boneParameter["fromX"] = QString::number(findFirstNode->second.x - 0.5);
boneParameter["fromY"] = QString::number(0.5 - findFirstNode->second.y);
boneParameter["fromZ"] = QString::number(1.0 - findFirstNode->second.z);
boneParameter["toX"] = QString::number(findSecondNode->second.x - 0.5);
boneParameter["toY"] = QString::number(0.5 - findSecondNode->second.y);
boneParameter["toZ"] = QString::number(1.0 - findSecondNode->second.z);
boneParameter["fromX"] = QString::number(toOutcomeX(findFirstNode->second.x));
boneParameter["fromY"] = QString::number(toOutcomeY(findFirstNode->second.y));
boneParameter["fromZ"] = QString::number(toOutcomeZ(findFirstNode->second.z));
boneParameter["toX"] = QString::number(toOutcomeX(findSecondNode->second.x));
boneParameter["toY"] = QString::number(toOutcomeY(findSecondNode->second.y));
boneParameter["toZ"] = QString::number(toOutcomeZ(findSecondNode->second.z));
}
}
float PoseDocument::fromOutcomeX(float x)
{
return x + 0.5;
}
float PoseDocument::toOutcomeX(float x)
{
return x - 0.5;
}
float PoseDocument::fromOutcomeY(float y)
{
return -y + 0.5;
}
float PoseDocument::toOutcomeY(float y)
{
return 0.5 - y;
}
float PoseDocument::fromOutcomeZ(float z)
{
return -z + 1;
}
float PoseDocument::toOutcomeZ(float z)
{
return 1.0 - z;
}

View File

@ -52,6 +52,7 @@ public slots:
public:
static const float m_nodeRadius;
static const float m_groundPlaneHalfThickness;
static const bool m_hideRootAndVirtual;
private:
std::map<QString, std::pair<QUuid, QUuid>> m_boneNameToIdsMap;
@ -61,6 +62,13 @@ private:
std::deque<PoseHistoryItem> m_undoItems;
std::deque<PoseHistoryItem> m_redoItems;
std::vector<RiggerBone> m_riggerBones;
static float fromOutcomeX(float x);
static float toOutcomeX(float x);
static float fromOutcomeY(float y);
static float toOutcomeY(float y);
static float fromOutcomeZ(float z);
static float toOutcomeZ(float z);
};
#endif

View File

@ -38,9 +38,35 @@ bool Rigger::calculateBodyTriangles(std::set<MeshSplitterTriangle> &bodyTriangle
}
if (bodyTriangles.empty()) {
m_messages.push_back(std::make_pair(QtCriticalMsg,
tr("Calculate body from marks failed")));
tr("Calculate body from marks failed, try to move the center anchor around")));
return false;
}
// Check if the calculated body is neighboring with all the cutoff makers
std::set<int> bodyVertices;
addTrianglesToVertices(bodyTriangles, bodyVertices);
for (const auto &marksMapIt: m_marksMap) {
if (isCutOffSplitter(marksMapIt.first.first)) {
for (const auto index: marksMapIt.second) {
auto &mark = m_marks[index];
std::set<int> markSplitterVertices;
addTrianglesToVertices(mark.markTriangles, markSplitterVertices);
bool neighboring = false;
for (const auto &index: markSplitterVertices) {
if (bodyVertices.find(index) != bodyVertices.end()) {
neighboring = true;
break;
}
}
if (!neighboring) {
m_messages.push_back(std::make_pair(QtCriticalMsg,
tr("The center anchor is not located in the center of the model")));
return false;
}
}
}
}
return true;
}
@ -58,7 +84,7 @@ bool Rigger::addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bo
mark.markTriangles = markTriangles;
if (isCutOffSplitter(mark.boneMark)) {
if (!mark.split(m_inputTriangles, m_maxCutOffSplitterExpandRound)) {
if (!mark.split(m_verticesPositions, m_inputTriangles, m_maxCutOffSplitterExpandRound)) {
m_cutoffErrorItems.push_back(SkeletonSideToDispName(mark.boneSide) + " " + BoneMarkToDispName(mark.boneMark));
return false;
}

View File

@ -22,31 +22,49 @@ public:
std::set<MeshSplitterTriangle> markTriangles;
const std::set<MeshSplitterTriangle> &bigGroup() const
{
return m_firstGroup.size() > m_secondGroup.size() ?
m_firstGroup :
m_secondGroup;
return m_isFirstBiggerThenSecond ? m_firstGroup : m_secondGroup;
}
const std::set<MeshSplitterTriangle> &smallGroup() const
{
return m_firstGroup.size() > m_secondGroup.size() ?
m_secondGroup :
m_firstGroup;
return m_isFirstBiggerThenSecond ? m_secondGroup : m_firstGroup;
}
bool split(const std::set<MeshSplitterTriangle> &input, int expandRound=0)
bool split(const std::vector<QVector3D> &verticesPositions, const std::set<MeshSplitterTriangle> &input, int expandRound=0)
{
int totalRound = 1 + expandRound;
for (int round = 0; round < totalRound; ++round) {
m_firstGroup.clear();
m_secondGroup.clear();
bool splitResult = MeshSplitter::split(input, markTriangles, m_firstGroup, m_secondGroup, round > 0);
if (splitResult)
if (splitResult) {
sortByDistanceFromOrigin(verticesPositions);
return true;
}
}
return false;
}
private:
std::set<MeshSplitterTriangle> m_firstGroup;
std::set<MeshSplitterTriangle> m_secondGroup;
bool m_isFirstBiggerThenSecond = false;
float minDistance2FromOrigin(const std::vector<QVector3D> &verticesPositions, const std::set<MeshSplitterTriangle> &triangles)
{
float minDistance2 = std::numeric_limits<float>::max();
for (const auto &triangle: triangles) {
for (size_t i = 0; i < 3; ++i) {
float distance2 = verticesPositions[triangle.indicies[i]].lengthSquared();
if (distance2 < minDistance2)
minDistance2 = distance2;
}
}
return minDistance2;
}
void sortByDistanceFromOrigin(const std::vector<QVector3D> &verticesPositions)
{
m_isFirstBiggerThenSecond = minDistance2FromOrigin(verticesPositions, m_firstGroup) <
minDistance2FromOrigin(verticesPositions, m_secondGroup);
}
};
class RiggerBone