diff --git a/src/animalposer.cpp b/src/animalposer.cpp index 68890b3c..94e78c76 100644 --- a/src/animalposer.cpp +++ b/src/animalposer.cpp @@ -154,6 +154,8 @@ void AnimalPoser::resolveChainRotation(const std::vector &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 &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); diff --git a/src/animalrigger.cpp b/src/animalrigger.cpp index e14ba98d..ab219496 100644 --- a/src/animalrigger.cpp +++ b/src/animalrigger.cpp @@ -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 twoColorsForSpine = {BoneMarkToColor(BoneMark::Neck), BoneMarkToColor(BoneMark::Tail)}; - const std::vector twoColorsForLimb = {BoneMarkToColor(BoneMark::Joint), BoneMarkToColor(BoneMark::Limb)}; + const std::vector twoColorsForChain = {BoneMarkToColor(BoneMark::Joint), BoneMarkToColor(BoneMark::Limb)}; int spineGenerateOrder = 1; std::map, 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 extremCoords(6, jointPositions.back()); - resolveBoundingBox(lastJointBoneVerticies, extremCoords[0], extremCoords[1], extremCoords[2], extremCoords[3], extremCoords[4], extremCoords[5]); - float maxDistance2 = std::numeric_limits::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 &verticies, const QVector3D &from) +{ + std::vector extremCoords(6, from); + resolveBoundingBox(verticies, extremCoords[0], extremCoords[1], extremCoords[2], extremCoords[3], extremCoords[4], extremCoords[5]); + float maxDistance2 = std::numeric_limits::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); diff --git a/src/animalrigger.h b/src/animalrigger.h index c9f028ba..9d929391 100644 --- a/src/animalrigger.h +++ b/src/animalrigger.h @@ -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 &verticies, const QVector3D &from); }; #endif diff --git a/src/bonemark.h b/src/bonemark.h index 3c2f8f22..265a14c2 100644 --- a/src/bonemark.h +++ b/src/bonemark.h @@ -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: \ diff --git a/src/posedocument.cpp b/src/posedocument.cpp index f2382146..4302e410 100644 --- a/src/posedocument.cpp +++ b/src/posedocument.cpp @@ -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 *rigBones, const auto &bonesPart = partMap[m_bonesPartId]; bonesPart.id = m_bonesPartId; + qDebug() << "rigBones size:" << rigBones->size(); + std::vector> 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 *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 *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 *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 *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 *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 *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> &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; +} + diff --git a/src/posedocument.h b/src/posedocument.h index 0910a96c..af9b7bb4 100644 --- a/src/posedocument.h +++ b/src/posedocument.h @@ -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> m_boneNameToIdsMap; @@ -61,6 +62,13 @@ private: std::deque m_undoItems; std::deque m_redoItems; std::vector 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 diff --git a/src/rigger.cpp b/src/rigger.cpp index 889939c3..7d5fe0a6 100644 --- a/src/rigger.cpp +++ b/src/rigger.cpp @@ -38,9 +38,35 @@ bool Rigger::calculateBodyTriangles(std::set &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 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 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; } diff --git a/src/rigger.h b/src/rigger.h index ce0523f4..8c5c7cd1 100644 --- a/src/rigger.h +++ b/src/rigger.h @@ -22,31 +22,49 @@ public: std::set markTriangles; const std::set &bigGroup() const { - return m_firstGroup.size() > m_secondGroup.size() ? - m_firstGroup : - m_secondGroup; + return m_isFirstBiggerThenSecond ? m_firstGroup : m_secondGroup; } const std::set &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 &input, int expandRound=0) + bool split(const std::vector &verticesPositions, const std::set &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 m_firstGroup; std::set m_secondGroup; + bool m_isFirstBiggerThenSecond = false; + + float minDistance2FromOrigin(const std::vector &verticesPositions, const std::set &triangles) + { + float minDistance2 = std::numeric_limits::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 &verticesPositions) + { + m_isFirstBiggerThenSecond = minDistance2FromOrigin(verticesPositions, m_firstGroup) < + minDistance2FromOrigin(verticesPositions, m_secondGroup); + } }; class RiggerBone