From 2c8975b1cd159dd98689622fba499127e1bbfbaa Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Wed, 7 Nov 2018 00:26:19 +0800 Subject: [PATCH] Unify rig type of generic and tetrapod as animal Marks also reduced to four types: Neck, Limb, Tail and Joint. --- dust3d.pro | 14 +- src/animalposer.cpp | 232 +++++++ src/{tetrapodposer.h => animalposer.h} | 10 +- src/{genericrigger.cpp => animalrigger.cpp} | 426 +++++++----- src/animalrigger.h | 28 + src/bonemark.h | 75 +-- src/document.cpp | 15 +- src/document.h | 6 +- src/genericposer.cpp | 14 - src/genericposer.h | 14 - src/genericrigger.h | 26 - src/motionmanagewidget.cpp | 2 +- src/posedocument.cpp | 56 +- src/poseeditwidget.h | 2 - src/posemanagewidget.cpp | 2 +- src/poserconstruct.cpp | 11 +- src/riggenerator.cpp | 23 +- src/riggenerator.h | 6 +- src/rigger.cpp | 33 +- src/rigger.h | 18 +- src/riggerconstruct.cpp | 9 +- src/rigtype.h | 21 +- src/rigwidget.cpp | 50 +- src/rigwidget.h | 3 +- src/tetrapodposer.cpp | 219 ------- src/tetrapodrigger.cpp | 689 -------------------- src/tetrapodrigger.h | 19 - 27 files changed, 633 insertions(+), 1390 deletions(-) create mode 100644 src/animalposer.cpp rename src/{tetrapodposer.h => animalposer.h} (65%) rename src/{genericrigger.cpp => animalrigger.cpp} (51%) create mode 100644 src/animalrigger.h delete mode 100644 src/genericposer.cpp delete mode 100644 src/genericposer.h delete mode 100644 src/genericrigger.h delete mode 100644 src/tetrapodposer.cpp delete mode 100644 src/tetrapodrigger.cpp delete mode 100644 src/tetrapodrigger.h diff --git a/dust3d.pro b/dust3d.pro index 54577290..9d82921f 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -168,9 +168,6 @@ HEADERS += src/skinnedmeshcreator.h SOURCES += src/jointnodetree.cpp HEADERS += src/jointnodetree.h -SOURCES += src/tetrapodposer.cpp -HEADERS += src/tetrapodposer.h - SOURCES += src/poser.cpp HEADERS += src/poser.h @@ -276,14 +273,11 @@ HEADERS += src/uvunwrap.h SOURCES += src/triangletangentresolve.cpp HEADERS += src/triangletangentresolve.h -SOURCES += src/tetrapodrigger.cpp -HEADERS += src/tetrapodrigger.h +SOURCES += src/animalrigger.cpp +HEADERS += src/animalrigger.h -SOURCES += src/genericrigger.cpp -HEADERS += src/genericrigger.h - -SOURCES += src/genericposer.cpp -HEADERS += src/genericposer.h +SOURCES += src/animalposer.cpp +HEADERS += src/animalposer.h SOURCES += src/riggerconstruct.cpp HEADERS += src/riggerconstruct.h diff --git a/src/animalposer.cpp b/src/animalposer.cpp new file mode 100644 index 00000000..68890b3c --- /dev/null +++ b/src/animalposer.cpp @@ -0,0 +1,232 @@ +#include +#include +#include +#include "animalposer.h" +#include "util.h" + +AnimalPoser::AnimalPoser(const std::vector &bones) : + Poser(bones) +{ +} + +void AnimalPoser::resolveTranslation() +{ + 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> chains; + for (const auto &item: parameters()) { + QRegularExpressionMatch match = reJoints.match(item.first); + if (match.hasMatch()) { + QString name = match.captured(1); + chains[name].push_back(item.first); + qDebug() << "chains[" << name << "]:" << item.first; + } else { + match = reSpine.match(item.first); + if (match.hasMatch()) { + QString name = match.captured(1); + chains[name].push_back(item.first); + qDebug() << "chains[" << name << "]:" << item.first; + } else if (item.first.startsWith("Virtual_")) { + qDebug() << "Ignore connector:" << item.first; + } else { + qDebug() << "Unrecognized bone name:" << item.first; + } + } + } + for (auto &chain: chains) { + 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); + } +} + +std::pair AnimalPoser::findQVector3DFromMap(const std::map &map, const QString &xName, const QString &yName, const QString &zName) +{ + auto findXResult = map.find(xName); + auto findYResult = map.find(yName); + auto findZResult = map.find(zName); + if (findXResult == map.end() && + findYResult == map.end() && + findZResult == map.end()) { + return {false, QVector3D()}; + } + return {true, { + valueOfKeyInMapOrEmpty(map, xName).toFloat(), + valueOfKeyInMapOrEmpty(map, yName).toFloat(), + valueOfKeyInMapOrEmpty(map, zName).toFloat() + }}; +} + +std::pair> AnimalPoser::findBonePositionsFromParameters(const std::map &map) +{ + auto findBoneStartResult = findQVector3DFromMap(map, "fromX", "fromY", "fromZ"); + auto findBoneStopResult = findQVector3DFromMap(map, "toX", "toY", "toZ"); + + if (!findBoneStartResult.first || !findBoneStopResult.first) + return {false, {QVector3D(), QVector3D()}}; + + return {true, {findBoneStartResult.second, findBoneStopResult.second}}; +} + +void AnimalPoser::resolveChainRotation(const std::vector &limbBoneNames) +{ + std::vector rotationsForEndEffector; + size_t endEffectorStart = 0; + + // We match the poses by the distance and rotation plane + if (limbBoneNames.size() >= 2) { + endEffectorStart = 2; + + const auto &beginBoneName = limbBoneNames[0]; + const auto &middleBoneName = limbBoneNames[1]; + + const auto &beginBoneParameters = parameters().find(beginBoneName); + if (beginBoneParameters == parameters().end()) { + qDebug() << beginBoneName << "'s parameters not found"; + return; + } + + auto matchBeginBonePositions = findBonePositionsFromParameters(beginBoneParameters->second); + if (!matchBeginBonePositions.first) { + qDebug() << beginBoneName << "'s positions not found"; + return; + } + + const auto &middleBoneParameters = parameters().find(middleBoneName); + if (middleBoneParameters == parameters().end()) { + qDebug() << middleBoneName << "'s parameters not found"; + return; + } + + auto matchMiddleBonePositions = findBonePositionsFromParameters(middleBoneParameters->second); + if (!matchMiddleBonePositions.first) { + qDebug() << middleBoneName << "'s positions not found"; + return; + } + + float matchLimbLength = (matchBeginBonePositions.second.first - matchBeginBonePositions.second.second).length() + + (matchMiddleBonePositions.second.first - matchMiddleBonePositions.second.second).length(); + + auto matchDistanceBetweenBeginAndEndBones = (matchBeginBonePositions.second.first - matchMiddleBonePositions.second.second).length(); + auto matchRotatePlaneNormal = QVector3D::crossProduct((matchBeginBonePositions.second.second - matchBeginBonePositions.second.first).normalized(), (matchMiddleBonePositions.second.second - matchBeginBonePositions.second.second).normalized()); + + auto matchDirectionBetweenBeginAndEndPones = (matchMiddleBonePositions.second.second - matchBeginBonePositions.second.first).normalized(); + + int beginBoneIndex = findBoneIndex(beginBoneName); + if (-1 == beginBoneIndex) { + qDebug() << beginBoneName << "not found in rigged bones"; + return; + } + const auto &beginBone = bones()[beginBoneIndex]; + + int middleBoneIndex = findBoneIndex(middleBoneName); + if (-1 == middleBoneIndex) { + qDebug() << middleBoneName << "not found in rigged bones"; + return; + } + const auto &middleBone = bones()[beginBoneIndex]; + + float targetBeginBoneLength = (beginBone.headPosition - beginBone.tailPosition).length(); + float targetMiddleBoneLength = (middleBone.headPosition - middleBone.tailPosition).length(); + float targetLimbLength = targetBeginBoneLength + targetMiddleBoneLength; + + float targetDistanceBetweenBeginAndEndBones = matchDistanceBetweenBeginAndEndBones * (targetLimbLength / matchLimbLength); + QVector3D targetEndBoneStartPosition = beginBone.headPosition + matchDirectionBetweenBeginAndEndPones * targetDistanceBetweenBeginAndEndBones; + + float angleBetweenDistanceAndMiddleBones = 0; + { + const float &a = targetMiddleBoneLength; + const float &b = targetDistanceBetweenBeginAndEndBones; + const float &c = targetBeginBoneLength; + double cosC = (a*a + b*b - c*c) / (2.0*a*b); + angleBetweenDistanceAndMiddleBones = qRadiansToDegrees(acos(cosC)); + } + + QVector3D targetMiddleBoneStartPosition; + { + qDebug() << beginBoneName << "Angle:" << angleBetweenDistanceAndMiddleBones; + auto rotation = QQuaternion::fromAxisAndAngle(matchRotatePlaneNormal, angleBetweenDistanceAndMiddleBones); + targetMiddleBoneStartPosition = targetEndBoneStartPosition + rotation.rotatedVector(-matchDirectionBetweenBeginAndEndPones).normalized() * targetMiddleBoneLength; + } + + // Now the bones' positions have been resolved, we calculate the rotation + + auto oldBeginBoneDirection = (beginBone.tailPosition - beginBone.headPosition).normalized(); + auto newBeginBoneDirection = (targetMiddleBoneStartPosition - beginBone.headPosition).normalized(); + auto beginBoneRotation = QQuaternion::rotationTo(oldBeginBoneDirection, newBeginBoneDirection); + m_jointNodeTree.updateRotation(beginBoneIndex, beginBoneRotation); + + auto oldMiddleBoneDirection = (middleBone.tailPosition - middleBone.headPosition).normalized(); + auto newMiddleBoneDirection = (targetEndBoneStartPosition - targetMiddleBoneStartPosition).normalized(); + oldMiddleBoneDirection = beginBoneRotation.rotatedVector(oldMiddleBoneDirection); + auto middleBoneRotation = QQuaternion::rotationTo(oldMiddleBoneDirection, newMiddleBoneDirection); + m_jointNodeTree.updateRotation(middleBoneIndex, middleBoneRotation); + + rotationsForEndEffector.push_back(beginBoneRotation); + rotationsForEndEffector.push_back(middleBoneRotation); + } + + // Calculate the end effectors' rotation + if (limbBoneNames.size() > endEffectorStart) { + for (size_t i = endEffectorStart; i < limbBoneNames.size(); ++i) { + const auto &boneName = limbBoneNames[i]; + int boneIndex = findBoneIndex(boneName); + if (-1 == boneIndex) { + qDebug() << "Find bone failed:" << boneName; + continue; + } + const auto &bone = bones()[boneIndex]; + const auto &boneParameters = parameters().find(boneName); + if (boneParameters == parameters().end()) { + qDebug() << "Find bone parameters:" << boneName; + continue; + } + auto matchBonePositions = findBonePositionsFromParameters(boneParameters->second); + if (!matchBonePositions.first) { + qDebug() << "Find bone positions failed:" << boneName; + continue; + } + auto matchBoneDirection = (matchBonePositions.second.second - matchBonePositions.second.first).normalized(); + auto oldBoneDirection = (bone.tailPosition - bone.headPosition).normalized(); + auto newBoneDirection = matchBoneDirection; + for (const auto &rotation: rotationsForEndEffector) { + oldBoneDirection = rotation.rotatedVector(oldBoneDirection); + } + auto boneRotation = QQuaternion::rotationTo(oldBoneDirection, newBoneDirection); + m_jointNodeTree.updateRotation(boneIndex, boneRotation); + rotationsForEndEffector.push_back(boneRotation); + } + } +} + +void AnimalPoser::commit() +{ + resolveTranslation(); + + Poser::commit(); +} + diff --git a/src/tetrapodposer.h b/src/animalposer.h similarity index 65% rename from src/tetrapodposer.h rename to src/animalposer.h index 73d4eba1..7f3c9250 100644 --- a/src/tetrapodposer.h +++ b/src/animalposer.h @@ -1,18 +1,18 @@ -#ifndef DUST3D_TETRAPOD_POSER_H -#define DUST3D_TETRAPOD_POSER_H +#ifndef DUST3D_ANIMAL_POSER_H +#define DUST3D_ANIMAL_POSER_H #include #include "poser.h" -class TetrapodPoser : public Poser +class AnimalPoser : public Poser { Q_OBJECT public: - TetrapodPoser(const std::vector &bones); + AnimalPoser(const std::vector &bones); void commit() override; private: void resolveTranslation(); - void resolveLimbRotation(const std::vector &limbBoneNames); + void resolveChainRotation(const std::vector &limbBoneNames); std::pair findQVector3DFromMap(const std::map &map, const QString &xName, const QString &yName, const QString &zName); std::pair> findBonePositionsFromParameters(const std::map &map); }; diff --git a/src/genericrigger.cpp b/src/animalrigger.cpp similarity index 51% rename from src/genericrigger.cpp rename to src/animalrigger.cpp index e1a54d27..e14ba98d 100644 --- a/src/genericrigger.cpp +++ b/src/animalrigger.cpp @@ -2,35 +2,36 @@ #include #include #include -#include "genericrigger.h" +#include "animalrigger.h" -GenericRigger::GenericRigger(const std::vector &verticesPositions, +AnimalRigger::AnimalRigger(const std::vector &verticesPositions, const std::set &inputTriangles) : Rigger(verticesPositions, inputTriangles) { } -bool GenericRigger::validate() +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"))); + return false; + } + return true; } -bool GenericRigger::isCutOffSplitter(BoneMark boneMark) +bool AnimalRigger::isCutOffSplitter(BoneMark boneMark) { - return BoneMark::Limb == boneMark; + return BoneMark::Joint != boneMark; } -BoneMark GenericRigger::translateBoneMark(BoneMark boneMark) +BoneMark AnimalRigger::translateBoneMark(BoneMark boneMark) { - if (boneMark == BoneMark::Neck || - boneMark == BoneMark::Shoulder || - boneMark == BoneMark::Hip || - boneMark == BoneMark::Limb) - return BoneMark::Limb; - return BoneMark::Joint; + return boneMark; } -void GenericRigger::collectJointsForLimb(int markIndex, std::vector &jointMarkIndicies) +bool AnimalRigger::collectJontsForChain(int markIndex, std::vector &jointMarkIndicies) { const auto &mark = m_marks[markIndex]; @@ -51,13 +52,13 @@ void GenericRigger::collectJointsForLimb(int markIndex, std::vector &jointM if (triangleToMarkMap.size() <= 1) { qDebug() << "Collect joints for limb failed because of lack marks"; - return; + return true; } const auto &group = mark.smallGroup(); if (group.empty()) { qDebug() << "Collect joints for limb failed because of lack verticies"; - return; + return false; } // Build the edge to triangle map; @@ -85,7 +86,7 @@ void GenericRigger::collectJointsForLimb(int markIndex, std::vector &jointM if (waitTriangles.empty()) { qDebug() << "Couldn't find a triangle to start"; - return; + return false; } // Traverse all the triangles and fill the triangle to mark map @@ -163,7 +164,7 @@ void GenericRigger::collectJointsForLimb(int markIndex, std::vector &jointM } if (-1 == nearestMarkIndex) { qDebug() << "Find nearest joint failed"; - return; + return false; } jointMarkIndicies.push_back(nearestMarkIndex); visited.insert(nearestMarkIndex); @@ -185,9 +186,11 @@ void GenericRigger::collectJointsForLimb(int markIndex, std::vector &jointM break; findPairResult = pairs.find(linkTo); } + + return true; } -bool GenericRigger::rig() +bool AnimalRigger::rig() { if (!validate()) return false; @@ -205,27 +208,44 @@ bool GenericRigger::rig() isMainBodyVerticalAligned = fabs(yMax.y() - yMin.y()) > fabs(zMax.z() - zMin.z()); } - // Collect all limbs - auto nosideLimbIndicies = m_marksMap.find(std::make_pair(BoneMark::Limb, SkeletonSide::None)); + // Collect all branchs + auto neckIndicies = m_marksMap.find(std::make_pair(BoneMark::Neck, SkeletonSide::None)); + auto tailIndicies = m_marksMap.find(std::make_pair(BoneMark::Tail, SkeletonSide::None)); auto leftLimbIndicies = m_marksMap.find(std::make_pair(BoneMark::Limb, SkeletonSide::Left)); auto rightLimbIndicies = m_marksMap.find(std::make_pair(BoneMark::Limb, SkeletonSide::Right)); + std::vector nosideMarkIndicies; + std::vector leftMarkIndicies; + std::vector righMarkIndicies; + if (neckIndicies != m_marksMap.end()) { + for (const auto &index: neckIndicies->second) + nosideMarkIndicies.push_back(index); + } + if (tailIndicies != m_marksMap.end()) { + for (const auto &index: tailIndicies->second) + nosideMarkIndicies.push_back(index); + } + if (leftLimbIndicies != m_marksMap.end()) { + for (const auto &index: leftLimbIndicies->second) + leftMarkIndicies.push_back(index); + } + if (rightLimbIndicies != m_marksMap.end()) { + for (const auto &index: rightLimbIndicies->second) + righMarkIndicies.push_back(index); + } // Generate spine for main body - auto sortLimbIndiciesInSpineOrder = [=](const int &first, const int &second) { + auto sortMarkIndiciesInSpineOrder = [=](const int &first, const int &second) { return isMainBodyVerticalAligned ? (m_marks[first].bonePosition.y() < m_marks[second].bonePosition.y()) : (m_marks[first].bonePosition.z() < m_marks[second].bonePosition.z()); }; - if (nosideLimbIndicies != m_marksMap.end()) - std::sort(nosideLimbIndicies->second.begin(), nosideLimbIndicies->second.end(), sortLimbIndiciesInSpineOrder); - if (leftLimbIndicies != m_marksMap.end()) - std::sort(leftLimbIndicies->second.begin(), leftLimbIndicies->second.end(), sortLimbIndiciesInSpineOrder); - if (rightLimbIndicies != m_marksMap.end()) - std::sort(rightLimbIndicies->second.begin(), rightLimbIndicies->second.end(), sortLimbIndiciesInSpineOrder); + std::sort(nosideMarkIndicies.begin(), nosideMarkIndicies.end(), sortMarkIndiciesInSpineOrder); + std::sort(leftMarkIndicies.begin(), leftMarkIndicies.end(), sortMarkIndiciesInSpineOrder); + std::sort(righMarkIndicies.begin(), righMarkIndicies.end(), sortMarkIndiciesInSpineOrder); const static std::vector s_empty; - const std::vector *limbColumns[3] = {leftLimbIndicies != m_marksMap.end() ? &leftLimbIndicies->second : &s_empty, - nosideLimbIndicies != m_marksMap.end() ? &nosideLimbIndicies->second : &s_empty, - rightLimbIndicies != m_marksMap.end() ? &rightLimbIndicies->second : &s_empty + const std::vector *chainColumns[3] = {&leftMarkIndicies, + &nosideMarkIndicies, + &righMarkIndicies }; enum Column { @@ -237,18 +257,18 @@ bool GenericRigger::rig() { float coord; QVector3D position; - std::set limbMarkIndicies; + std::set chainMarkIndicies; }; - std::vector spineNodes; + std::vector rawSpineNodes; for (size_t sideIndicies[3] = {0, 0, 0}; - sideIndicies[Column::Noside] < limbColumns[Column::Noside]->size() || - sideIndicies[Column::Left] < limbColumns[Column::Left]->size() || - sideIndicies[Column::Right] < limbColumns[Column::Right]->size();) { + sideIndicies[Column::Noside] < chainColumns[Column::Noside]->size() || + sideIndicies[Column::Left] < chainColumns[Column::Left]->size() || + sideIndicies[Column::Right] < chainColumns[Column::Right]->size();) { float choosenCoord = std::numeric_limits::max(); int choosenColumn = -1; for (size_t side = Column::Left; side <= Column::Right; ++side) { - if (sideIndicies[side] < limbColumns[side]->size()) { - const auto &mark = m_marks[limbColumns[side]->at(sideIndicies[side])]; + if (sideIndicies[side] < chainColumns[side]->size()) { + const auto &mark = m_marks[chainColumns[side]->at(sideIndicies[side])]; const auto &coord = isMainBodyVerticalAligned ? mark.bonePosition.y() : mark.bonePosition.z(); if (coord < choosenCoord) { @@ -261,42 +281,69 @@ bool GenericRigger::rig() qDebug() << "Should not come here, coord corrupted"; break; } - // Find all the limbs before or near this choosenCoord - QVector3D sumOfLimbPositions; - int countOfLimbs = 0; - std::set limbMarkIndicies; + // Find all the chains before or near this choosenCoord + QVector3D sumOfChainPositions; + int countOfChains = 0; + std::set chainMarkIndicies; for (size_t side = Column::Left; side <= Column::Right; ++side) { - if (sideIndicies[side] < limbColumns[side]->size()) { - const auto &mark = m_marks[limbColumns[side]->at(sideIndicies[side])]; + if (sideIndicies[side] < chainColumns[side]->size()) { + const auto &mark = m_marks[chainColumns[side]->at(sideIndicies[side])]; const auto &coord = isMainBodyVerticalAligned ? mark.bonePosition.y() : mark.bonePosition.z(); if (coord <= choosenCoord + 0.001) { - limbMarkIndicies.insert(limbColumns[side]->at(sideIndicies[side])); - sumOfLimbPositions += mark.bonePosition; - ++countOfLimbs; + chainMarkIndicies.insert(chainColumns[side]->at(sideIndicies[side])); + sumOfChainPositions += mark.bonePosition; + ++countOfChains; ++sideIndicies[side]; } } } - if (countOfLimbs <= 0) { + if (countOfChains <= 0) { qDebug() << "Should not come here, there must be at least one limb"; break; } - - //qDebug() << "Create new spine node from" << countOfLimbs << "limbs current coord:" << choosenCoord; - - spineNodes.push_back(SpineNode()); - SpineNode &spineNode = spineNodes.back(); + + rawSpineNodes.push_back(SpineNode()); + SpineNode &spineNode = rawSpineNodes.back(); spineNode.coord = choosenCoord; - spineNode.limbMarkIndicies = limbMarkIndicies; - spineNode.position = sumOfLimbPositions / countOfLimbs; + spineNode.chainMarkIndicies = chainMarkIndicies; + spineNode.position = sumOfChainPositions / countOfChains; } - if (spineNodes.empty()) { + if (rawSpineNodes.empty()) { qDebug() << "Couldn't find limbs to create a spine"; return false; } + // Reassemble spine nodes, each spine will be cut off as two + std::vector spineNodes; + for (size_t i = 0; i < rawSpineNodes.size(); ++i) { + const auto &raw = rawSpineNodes[i]; + spineNodes.push_back(raw); + if (i + 1 < rawSpineNodes.size()) { + SpineNode intermediate; + const auto &nextRaw = rawSpineNodes[i + 1]; + intermediate.coord = (raw.coord + nextRaw.coord) / 2; + intermediate.position = (raw.position + nextRaw.position) / 2; + spineNodes.push_back(intermediate); + } + } + // Move the chain mark indicies to the new generated intermediate spine + for (size_t i = 2; i < spineNodes.size(); i += 2) { + auto &spineNode = spineNodes[i]; + std::vector needMoveIndicies; + for (const auto &markIndex: spineNode.chainMarkIndicies) { + const auto &chain = m_marks[markIndex]; + if (chain.boneSide != SkeletonSide::None) + needMoveIndicies.push_back(markIndex); + } + auto &previousSpineNode = spineNodes[i - 1]; + for (const auto &markIndex: needMoveIndicies) { + previousSpineNode.chainMarkIndicies.insert(markIndex); + spineNode.chainMarkIndicies.erase(markIndex); + } + } + std::map boneIndexMap; m_resultBones.push_back(RiggerBone()); @@ -304,47 +351,54 @@ bool GenericRigger::rig() bodyBone.index = m_resultBones.size() - 1; bodyBone.name = Rigger::rootBoneName; bodyBone.headPosition = QVector3D(0, 0, 0); - bodyBone.hasButton = true; - bodyBone.button = {spineNodes.size(), 0}; - bodyBone.buttonParameterType = RiggerButtonParameterType::Translation; boneIndexMap[bodyBone.name] = bodyBone.index; auto remainingSpineVerticies = bodyVerticies; - const std::vector twoColorsForSpine = {QColor(0x57, 0x43, 0x98), Qt::white}; - const std::vector twoColorsForLimb = {BoneMarkToColor(BoneMark::Joint), BoneMarkToColor(BoneMark::Hip)}; + const std::vector twoColorsForSpine = {BoneMarkToColor(BoneMark::Neck), BoneMarkToColor(BoneMark::Tail)}; + const std::vector twoColorsForLimb = {BoneMarkToColor(BoneMark::Joint), BoneMarkToColor(BoneMark::Limb)}; int spineGenerateOrder = 1; + std::map, int> chainOrderMapBySide; for (int spineNodeIndex = 0; spineNodeIndex < (int)spineNodes.size(); ++spineNodeIndex) { const auto &spineNode = spineNodes[spineNodeIndex]; std::set spineBoneVertices; QVector3D tailPosition; - - int buttonRow = spineNodes.size() - spineGenerateOrder; - + if (spineNodeIndex + 1 < (int)spineNodes.size()) { - std::set frontOrCoincidentVertices; - std::set backVertices; float distance = (spineNodes[spineNodeIndex + 1].position - spineNode.position).length(); - auto planeNormal = (spineNodes[spineNodeIndex + 1].position - spineNode.position).normalized(); + QVector3D currentSpineDirection = (spineNodes[spineNodeIndex + 1].position - spineNode.position).normalized(); + QVector3D previousSpineDirection = currentSpineDirection; + if (spineNodeIndex - 1 >= 0) { + previousSpineDirection = (spineNodes[spineNodeIndex].position - spineNodes[spineNodeIndex - 1].position).normalized(); + } + auto planeNormal = (currentSpineDirection + previousSpineDirection).normalized(); auto pointOnPlane = spineNode.position + planeNormal * distance * 1.25; auto perpVector = isMainBodyVerticalAligned ? QVector3D(1, 0, 0) : QVector3D(0, 1, 0); auto vectorOnPlane = QVector3D::crossProduct(planeNormal, perpVector); // Move this point to far away, so the checking vector will not collapse with the plane normal pointOnPlane += vectorOnPlane.normalized() * 1000; - splitVerticesByPlane(remainingSpineVerticies, - pointOnPlane, - planeNormal, - frontOrCoincidentVertices, - backVertices); - spineBoneVertices = backVertices; + { + std::set frontOrCoincidentVertices; + std::set backVertices; + splitVerticesByPlane(remainingSpineVerticies, + pointOnPlane, + planeNormal, + frontOrCoincidentVertices, + backVertices); + spineBoneVertices = backVertices; + } // Split again, this time, we step back a little bit pointOnPlane = spineNode.position + planeNormal * distance * 0.85; pointOnPlane += vectorOnPlane.normalized() * 1000; - splitVerticesByPlane(remainingSpineVerticies, - pointOnPlane, - planeNormal, - frontOrCoincidentVertices, - backVertices); - remainingSpineVerticies = frontOrCoincidentVertices; + { + std::set frontOrCoincidentVertices; + std::set backVertices; + splitVerticesByPlane(remainingSpineVerticies, + pointOnPlane, + planeNormal, + frontOrCoincidentVertices, + backVertices); + remainingSpineVerticies = frontOrCoincidentVertices; + } tailPosition = spineNodes[spineNodeIndex + 1].position; } else { spineBoneVertices = remainingSpineVerticies; @@ -362,16 +416,15 @@ bool GenericRigger::rig() spineBoneHeadPosition.setZ(spineNode.coord); } + QString spineName = namingSpine(spineGenerateOrder); + m_resultBones.push_back(RiggerBone()); RiggerBone &spineBone = m_resultBones.back(); spineBone.index = m_resultBones.size() - 1; - spineBone.name = "Spine" + QString::number(spineGenerateOrder); + spineBone.name = spineName; spineBone.headPosition = spineBoneHeadPosition; spineBone.tailPosition = tailPosition; spineBone.color = twoColorsForSpine[spineGenerateOrder % 2]; - spineBone.hasButton = true; - spineBone.button = {buttonRow, 0}; - spineBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll; addVerticesToWeights(spineBoneVertices, spineBone.index); boneIndexMap[spineBone.name] = spineBone.index; @@ -381,125 +434,149 @@ bool GenericRigger::rig() m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition = spineBone.headPosition; m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(spineBone.index); } else { - m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder - 1)]].tailPosition = spineBone.headPosition; - m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder - 1)]].children.push_back(spineBone.index); + m_resultBones[boneIndexMap[namingSpine(spineGenerateOrder - 1)]].tailPosition = spineBone.headPosition; + m_resultBones[boneIndexMap[namingSpine(spineGenerateOrder - 1)]].children.push_back(spineBone.index); } - int limbGenerateOrder = 1; - for (const auto &limbMarkIndex: spineNode.limbMarkIndicies) { - const auto &limbMark = m_marks[limbMarkIndex]; + for (const auto &chainMarkIndex: spineNode.chainMarkIndicies) { + const auto &chainMark = m_marks[chainMarkIndex]; + + QString chainBaseName = BoneMarkToString(chainMark.boneMark); + int chainGenerateOrder = ++chainOrderMapBySide[{chainBaseName, chainMark.boneSide}]; + QString chainName = namingChainPrefix(chainBaseName, chainMark.boneSide, chainGenerateOrder, spineNode.chainMarkIndicies.size()); m_resultBones.push_back(RiggerBone()); RiggerBone &ribBone = m_resultBones.back(); ribBone.index = m_resultBones.size() - 1; - ribBone.name = "Rib" + QString::number(spineGenerateOrder) + "x" + QString::number(limbGenerateOrder); + ribBone.name = namingConnector(spineName, chainName); ribBone.headPosition = spineBoneHeadPosition; boneIndexMap[ribBone.name] = ribBone.index; if (1 == spineGenerateOrder) { m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(ribBone.index); } else { - m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder)]].children.push_back(ribBone.index); + m_resultBones[boneIndexMap[namingSpine(spineGenerateOrder)]].children.push_back(ribBone.index); } std::vector jointMarkIndicies; - collectJointsForLimb(limbMarkIndex, jointMarkIndicies); + if (!collectJontsForChain(chainMarkIndex, jointMarkIndicies)) { + m_jointErrorItems.push_back(chainName); + } //qDebug() << "Limb markIndex:" << limbMarkIndex << " joints:" << jointMarkIndicies.size(); int jointGenerateOrder = 1; auto boneColor = [&]() { - return twoColorsForLimb[(jointGenerateOrder + 1) % 2]; - }; - auto boneColumn = [&]() { - return limbMark.boneSide == SkeletonSide::Left ? jointGenerateOrder : -jointGenerateOrder; + return twoColorsForLimb[jointGenerateOrder % 2]; }; auto addToParentBone = [&](QVector3D headPosition, SkeletonSide side, int boneIndex) { if (1 == jointGenerateOrder) { - m_resultBones[boneIndexMap["Rib" + QString::number(spineGenerateOrder) + "x" + QString::number(limbGenerateOrder)]].tailPosition = headPosition; - m_resultBones[boneIndexMap["Rib" + QString::number(spineGenerateOrder) + "x" + QString::number(limbGenerateOrder)]].children.push_back(boneIndex); + m_resultBones[boneIndexMap[namingConnector(spineName, chainName)]].tailPosition = headPosition; + m_resultBones[boneIndexMap[namingConnector(spineName, chainName)]].children.push_back(boneIndex); } else { - QString parentLimbBoneName = namingLimb(spineGenerateOrder, side, limbGenerateOrder, jointGenerateOrder - 1); + QString parentLimbBoneName = namingChain(chainBaseName, side, chainGenerateOrder, spineNode.chainMarkIndicies.size(), jointGenerateOrder - 1); m_resultBones[boneIndexMap[parentLimbBoneName]].tailPosition = headPosition; m_resultBones[boneIndexMap[parentLimbBoneName]].children.push_back(boneIndex); } }; std::set remainingLimbVertices; - addTrianglesToVertices(limbMark.smallGroup(), remainingLimbVertices); - addTrianglesToVertices(limbMark.markTriangles, remainingLimbVertices); - - QVector3D lastPosition = spineBoneHeadPosition; - for (const auto &jointMarkIndex: jointMarkIndicies) { + addTrianglesToVertices(chainMark.smallGroup(), remainingLimbVertices); + addTrianglesToVertices(chainMark.markTriangles, remainingLimbVertices); + + std::vector jointPositions; + for (jointGenerateOrder = 1; jointGenerateOrder <= (int)jointMarkIndicies.size(); ++jointGenerateOrder) { + int jointMarkIndex = jointMarkIndicies[jointGenerateOrder - 1]; const auto jointMark = m_marks[jointMarkIndex]; - int buttonColumn = boneColumn(); + jointPositions.push_back(jointMark.bonePosition); + } + std::set lastJointBoneVerticies; + if (jointPositions.size() >= 2) + { + QVector3D cutoffPlaneNormal = (jointPositions[jointPositions.size() - 1] - jointPositions[jointPositions.size() - 2]).normalized(); + QVector3D pointOnPlane = jointPositions[jointPositions.size() - 1]; + std::set frontOrCoincidentVertices; + std::set backVertices; + splitVerticesByPlane(remainingLimbVertices, + pointOnPlane, + cutoffPlaneNormal, + frontOrCoincidentVertices, + backVertices); + lastJointBoneVerticies = frontOrCoincidentVertices; + } else { + 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); + + QVector3D lastPosition = spineBoneHeadPosition; + for (jointGenerateOrder = 1; jointGenerateOrder <= (int)jointMarkIndicies.size(); ++jointGenerateOrder) { + int jointMarkIndex = jointMarkIndicies[jointGenerateOrder - 1]; + const auto &jointMark = m_marks[jointMarkIndex]; m_resultBones.push_back(RiggerBone()); - RiggerBone &limbBone = m_resultBones.back(); - limbBone.index = m_resultBones.size() - 1; - limbBone.name = namingLimb(spineGenerateOrder, jointMark.boneSide, limbGenerateOrder, jointGenerateOrder); - limbBone.headPosition = jointMark.bonePosition; - limbBone.baseNormal = jointMark.baseNormal; - limbBone.color = boneColor(); - limbBone.hasButton = true; - limbBone.button = {buttonRow, buttonColumn}; - limbBone.buttonParameterType = jointGenerateOrder == 1 ? RiggerButtonParameterType::PitchYawRoll : RiggerButtonParameterType::Intersection; + RiggerBone &jointBone = m_resultBones.back(); + jointBone.index = m_resultBones.size() - 1; + jointBone.name = namingChain(chainBaseName, chainMark.boneSide, chainGenerateOrder, spineNode.chainMarkIndicies.size(), jointGenerateOrder); + jointBone.headPosition = jointPositions[jointGenerateOrder - 1]; + jointBone.tailPosition = jointPositions[jointGenerateOrder]; + jointBone.baseNormal = jointMark.baseNormal; + jointBone.color = boneColor(); if (jointGenerateOrder == (int)jointMarkIndicies.size()) { - // Calculate the tail position from remaining verticies - std::vector extremCoords(6, jointMark.bonePosition); - resolveBoundingBox(remainingLimbVertices, extremCoords[0], extremCoords[1], extremCoords[2], extremCoords[3], extremCoords[4], extremCoords[5]); - float maxDistance2 = std::numeric_limits::min(); - QVector3D choosenExtreamCoord; - for (size_t i = 0; i < 6; ++i) { - const auto &position = extremCoords[i]; - auto length2 = (position - jointMark.bonePosition).lengthSquared(); - if (length2 >= maxDistance2) { - maxDistance2 = length2; - choosenExtreamCoord = position; - } - } - limbBone.tailPosition = choosenExtreamCoord; - addVerticesToWeights(remainingLimbVertices, limbBone.index); + addVerticesToWeights(remainingLimbVertices, jointBone.index); } else { - std::set frontOrCoincidentVertices; - std::set backVertices; - limbBone.tailPosition = m_marks[jointMarkIndicies[jointGenerateOrder]].bonePosition; - auto previousBoneDirection = (limbBone.headPosition - lastPosition).normalized(); - auto currentBoneDirection = (limbBone.tailPosition - limbBone.headPosition).normalized(); - auto planeNormal = (previousBoneDirection + currentBoneDirection).normalized(); - float previousBoneLength = (limbBone.headPosition - lastPosition).length(); - float currentBoneLength = (limbBone.tailPosition - limbBone.headPosition).length(); - auto pointOnPlane = limbBone.tailPosition + currentBoneDirection * currentBoneLength * 0.25; - splitVerticesByPlane(remainingLimbVertices, - pointOnPlane, - planeNormal, - frontOrCoincidentVertices, - backVertices); - addVerticesToWeights(backVertices, limbBone.index); - pointOnPlane = limbBone.tailPosition - previousBoneDirection * previousBoneLength * 0.1 * (currentBoneLength / std::max(previousBoneLength, (float)0.00001)); - splitVerticesByPlane(remainingLimbVertices, - pointOnPlane, - planeNormal, - frontOrCoincidentVertices, - backVertices); - remainingLimbVertices = frontOrCoincidentVertices; + int nextJointMarkIndex = jointMarkIndicies[jointGenerateOrder]; + const auto &nextJointMark = m_marks[nextJointMarkIndex]; + auto nextBoneDirection = (jointPositions[jointGenerateOrder + 1] - jointPositions[jointGenerateOrder]).normalized(); + auto currentBoneDirection = (jointBone.tailPosition - jointBone.headPosition).normalized(); + auto planeNormal = (currentBoneDirection + nextBoneDirection).normalized(); + auto pointOnPlane = jointBone.tailPosition + planeNormal * nextJointMark.nodeRadius; + { + std::set frontOrCoincidentVertices; + std::set backVertices; + splitVerticesByPlane(remainingLimbVertices, + pointOnPlane, + planeNormal, + frontOrCoincidentVertices, + backVertices); + addVerticesToWeights(backVertices, jointBone.index); + } + pointOnPlane = jointBone.tailPosition - planeNormal * nextJointMark.nodeRadius; + { + std::set frontOrCoincidentVertices; + std::set backVertices; + splitVerticesByPlane(remainingLimbVertices, + pointOnPlane, + planeNormal, + frontOrCoincidentVertices, + backVertices); + remainingLimbVertices = frontOrCoincidentVertices; + } } - boneIndexMap[limbBone.name] = limbBone.index; - addToParentBone(limbBone.headPosition, jointMark.boneSide, limbBone.index); + boneIndexMap[jointBone.name] = jointBone.index; + addToParentBone(jointBone.headPosition, chainMark.boneSide, jointBone.index); - lastPosition = jointMark.bonePosition; - - ++jointGenerateOrder; + lastPosition = jointPositions[jointGenerateOrder - 1]; } - ++limbGenerateOrder; + ++chainGenerateOrder; } ++spineGenerateOrder; } - - normalizeButtonColumns(); - + // Finalize weights for (auto &weights: m_resultWeights) { weights.second.finalizeWeights(); @@ -508,27 +585,22 @@ bool GenericRigger::rig() return true; } -void GenericRigger::normalizeButtonColumns() +QString AnimalRigger::namingChain(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide, int jointOrder) { - double minColumn = std::numeric_limits::max(); - double maxColumn = std::numeric_limits::min(); - for (const auto &bone: m_resultBones) { - if (!bone.hasButton) - continue; - if (bone.button.second < minColumn) - minColumn = bone.button.second; - if (bone.button.second > maxColumn) - maxColumn = bone.button.second; - } - int columnNumOfOneSide = (int)std::max(std::abs(minColumn), std::abs(maxColumn)); - for (auto &bone: m_resultBones) { - if (!bone.hasButton) - continue; - bone.button.second += columnNumOfOneSide; - } + return namingChainPrefix(baseName, side, orderInSide, totalInSide) + "_Joint" + QString::number(jointOrder); } -QString GenericRigger::namingLimb(int spineOrder, SkeletonSide side, int limbOrder, int jointOrder) +QString AnimalRigger::namingSpine(int spineOrder) { - return SkeletonSideToDispName(side) + "Limb" + QString::number(spineOrder) + "x" + QString::number(jointOrder); + return "Spine" + QString::number(spineOrder); +} + +QString AnimalRigger::namingConnector(const QString &spineName, const QString &chainName) +{ + return "Virtual_" + spineName + "_" + chainName; +} + +QString AnimalRigger::namingChainPrefix(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide) +{ + return SkeletonSideToDispName(side) + baseName + (totalInSide == 1 ? QString() : QString::number(orderInSide)); } diff --git a/src/animalrigger.h b/src/animalrigger.h new file mode 100644 index 00000000..c9f028ba --- /dev/null +++ b/src/animalrigger.h @@ -0,0 +1,28 @@ +#ifndef DUST3D_ANIMAL_RIGGER_H +#define DUST3D_ANIMAL_RIGGER_H +#include +#include +#include +#include "rigger.h" +#include "meshsplitter.h" + +class AnimalRigger: public Rigger +{ + Q_OBJECT +public: + AnimalRigger(const std::vector &verticesPositions, + const std::set &inputTriangles); +protected: + bool validate() override; + bool isCutOffSplitter(BoneMark boneMark) override; + bool rig() override; + BoneMark translateBoneMark(BoneMark boneMark) override; +private: + bool collectJontsForChain(int markIndex, std::vector &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); +}; + +#endif diff --git a/src/bonemark.h b/src/bonemark.h index 8cd60bcf..3c2f8f22 100644 --- a/src/bonemark.h +++ b/src/bonemark.h @@ -7,40 +7,25 @@ enum class BoneMark { None = 0, Neck, - Shoulder, - Elbow, - Wrist, - Hip, - Knee, - Ankle, Limb, + Tail, Joint, Count }; -#define BoneMarkHasSide(mark) ((mark) != BoneMark::Neck) +#define BoneMarkHasSide(mark) ((mark) == BoneMark::Limb) QColor BoneMarkToColor(BoneMark mark); #define IMPL_BoneMarkToColor \ QColor BoneMarkToColor(BoneMark mark) \ { \ switch (mark) { \ case BoneMark::Neck: \ - return QColor(0xfc, 0x0d, 0x1b); \ - case BoneMark::Shoulder: \ - return QColor(0xfd, 0x80, 0x23); \ - case BoneMark::Elbow: \ - return QColor(0x29, 0xfd, 0x2f); \ - case BoneMark::Wrist: \ - return QColor(0xff, 0xfd, 0x38); \ - case BoneMark::Hip: \ - return QColor(0x2c, 0xff, 0xfe); \ - case BoneMark::Knee: \ - return QColor(0x0b, 0x24, 0xfb); \ - case BoneMark::Ankle: \ - return QColor(0xfc, 0x28, 0xfc); \ + return QColor(0x51, 0xba, 0xf2); \ case BoneMark::Limb: \ - return QColor(0xf7, 0xf7, 0xf7); \ + return QColor(0x29, 0xfd, 0x2f); \ + case BoneMark::Tail: \ + return QColor(0xff, 0xfd, 0x38); \ case BoneMark::Joint: \ - return QColor(0xfd, 0xa9, 0xaa); \ + return QColor(0xcf, 0x83, 0xe1); \ case BoneMark::None: \ return Qt::transparent; \ default: \ @@ -54,20 +39,10 @@ const char *BoneMarkToString(BoneMark mark) \ switch (mark) { \ case BoneMark::Neck: \ return "Neck"; \ - case BoneMark::Shoulder: \ - return "Shoulder"; \ - case BoneMark::Elbow: \ - return "Elbow"; \ - case BoneMark::Wrist: \ - return "Wrist"; \ - case BoneMark::Hip: \ - return "Hip"; \ - case BoneMark::Knee: \ - return "Knee"; \ - case BoneMark::Ankle: \ - return "Ankle"; \ case BoneMark::Limb: \ return "Limb"; \ + case BoneMark::Tail: \ + return "Tail"; \ case BoneMark::Joint: \ return "Joint"; \ case BoneMark::None: \ @@ -83,20 +58,10 @@ BoneMark BoneMarkFromString(const char *markString) \ QString mark = markString; \ if (mark == "Neck") \ return BoneMark::Neck; \ - if (mark == "Shoulder") \ - return BoneMark::Shoulder; \ - if (mark == "Elbow") \ - return BoneMark::Elbow; \ - if (mark == "Wrist") \ - return BoneMark::Wrist; \ - if (mark == "Hip") \ - return BoneMark::Hip; \ - if (mark == "Knee") \ - return BoneMark::Knee; \ - if (mark == "Ankle") \ - return BoneMark::Ankle; \ if (mark == "Limb") \ return BoneMark::Limb; \ + if (mark == "Tail") \ + return BoneMark::Tail; \ if (mark == "Joint") \ return BoneMark::Joint; \ return BoneMark::None; \ @@ -108,22 +73,12 @@ QString BoneMarkToDispName(BoneMark mark) \ switch (mark) { \ case BoneMark::Neck: \ return QObject::tr("Neck"); \ - case BoneMark::Shoulder: \ - return QObject::tr("Shoulder (Arm Start)"); \ - case BoneMark::Elbow: \ - return QObject::tr("Elbow"); \ - case BoneMark::Wrist: \ - return QObject::tr("Wrist"); \ - case BoneMark::Hip: \ - return QObject::tr("Hip (Leg Start)"); \ - case BoneMark::Knee: \ - return QObject::tr("Knee"); \ - case BoneMark::Ankle: \ - return QObject::tr("Ankle"); \ case BoneMark::Limb: \ - return QObject::tr("Limb (Generic)"); \ + return QObject::tr("Limb"); \ + case BoneMark::Tail: \ + return QObject::tr("Tail"); \ case BoneMark::Joint: \ - return QObject::tr("Joint (Generic)"); \ + return QObject::tr("Joint"); \ case BoneMark::None: \ return QObject::tr("None"); \ default: \ diff --git a/src/document.cpp b/src/document.cpp index ca4f2292..ab56206a 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -2559,8 +2559,7 @@ void Document::rigReady() delete m_resultRigWeights; m_resultRigWeights = m_rigGenerator->takeResultWeights(); - m_resultRigMissingMarkNames = m_rigGenerator->missingMarkNames(); - m_resultRigErrorMarkNames = m_rigGenerator->errorMarkNames(); + m_resultRigMessages = m_rigGenerator->messages(); delete m_riggedOutcome; m_riggedOutcome = m_rigGenerator->takeOutcome(); @@ -2602,8 +2601,7 @@ void Document::removeRigResults() delete m_resultRigWeightMesh; m_resultRigWeightMesh = nullptr; - m_resultRigErrorMarkNames.clear(); - m_resultRigMissingMarkNames.clear(); + m_resultRigMessages.clear(); m_currentRigSucceed = false; @@ -2625,14 +2623,9 @@ void Document::setRigType(RigType toRigType) emit rigChanged(); } -const std::vector &Document::resultRigMissingMarkNames() const +const std::vector> &Document::resultRigMessages() const { - return m_resultRigMissingMarkNames; -} - -const std::vector &Document::resultRigErrorMarkNames() const -{ - return m_resultRigErrorMarkNames; + return m_resultRigMessages; } const Outcome &Document::currentRiggedOutcome() const diff --git a/src/document.h b/src/document.h index 52f3f33f..70c7352c 100644 --- a/src/document.h +++ b/src/document.h @@ -499,8 +499,7 @@ public: bool isPostProcessResultObsolete() const; void collectComponentDescendantParts(QUuid componentId, std::vector &partIds) const; void collectComponentDescendantComponents(QUuid componentId, std::vector &componentIds) const; - const std::vector &resultRigMissingMarkNames() const; - const std::vector &resultRigErrorMarkNames() const; + const std::vector> &resultRigMessages() const; const Outcome ¤tRiggedOutcome() const; bool currentRigSucceed() const; bool isMeshGenerating() const; @@ -652,8 +651,7 @@ private: std::deque m_undoItems; std::deque m_redoItems; GeneratedCacheContext m_generatedCacheContext; - std::vector m_resultRigMissingMarkNames; - std::vector m_resultRigErrorMarkNames; + std::vector> m_resultRigMessages; }; #endif diff --git a/src/genericposer.cpp b/src/genericposer.cpp deleted file mode 100644 index 7ed178c0..00000000 --- a/src/genericposer.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include "genericposer.h" - -GenericPoser::GenericPoser(const std::vector &bones) : - Poser(bones) -{ -} - -void GenericPoser::commit() -{ - // TODO: - - Poser::commit(); -} diff --git a/src/genericposer.h b/src/genericposer.h deleted file mode 100644 index ba936cc0..00000000 --- a/src/genericposer.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef DUST3D_GENERIC_POSER_H -#define DUST3D_GENERIC_POSER_H -#include "poser.h" - -class GenericPoser : public Poser -{ - Q_OBJECT -public: - GenericPoser(const std::vector &bones); -public: - void commit() override; -}; - -#endif diff --git a/src/genericrigger.h b/src/genericrigger.h deleted file mode 100644 index 02c0e4d0..00000000 --- a/src/genericrigger.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef DUST3D_GENERIC_RIGGER_H -#define DUST3D_GENERIC_RIGGER_H -#include -#include -#include -#include "rigger.h" -#include "meshsplitter.h" - -class GenericRigger: public Rigger -{ - Q_OBJECT -public: - GenericRigger(const std::vector &verticesPositions, - const std::set &inputTriangles); -protected: - bool validate() override; - bool isCutOffSplitter(BoneMark boneMark) override; - bool rig() override; - BoneMark translateBoneMark(BoneMark boneMark) override; -private: - void collectJointsForLimb(int markIndex, std::vector &jointMarkIndicies); - void normalizeButtonColumns(); - QString namingLimb(int spineOrder, SkeletonSide side, int limbOrder, int jointOrder); -}; - -#endif diff --git a/src/motionmanagewidget.cpp b/src/motionmanagewidget.cpp index 3627f411..25080bd5 100644 --- a/src/motionmanagewidget.cpp +++ b/src/motionmanagewidget.cpp @@ -26,7 +26,7 @@ MotionManageWidget::MotionManageWidget(const Document *document, QWidget *parent auto refreshInfoLabel = [=]() { if (m_document->currentRigSucceed()) { - if (m_document->rigType == RigType::Tetrapod) { + if (m_document->rigType == RigType::Animal) { infoLabel->setText(""); infoLabel->hide(); addMotionButton->show(); diff --git a/src/posedocument.cpp b/src/posedocument.cpp index c8cb5a42..f2382146 100644 --- a/src/posedocument.cpp +++ b/src/posedocument.cpp @@ -189,38 +189,45 @@ void PoseDocument::updateRigBones(const std::vector *rigBones, const auto findFirst = boneIndexToHeadNodeIdMap.find(edgePair.first); if (findFirst == boneIndexToHeadNodeIdMap.end()) { const auto &bone = (*rigBones)[edgePair.first]; - SkeletonNode node; - node.partId = m_bonesPartId; - node.id = QUuid::createUuid(); - node.setRadius(m_nodeRadius); - node.x = bone.headPosition.x() + 0.5; - node.y = -bone.headPosition.y() + 0.5; - node.z = -bone.headPosition.z() + 1; - nodeMap[node.id] = node; - newAddedNodeIds.insert(node.id); - boneIndexToHeadNodeIdMap[edgePair.first] = node.id; - firstNodeId = node.id; + if (!bone.name.startsWith("Virtual_")) { + SkeletonNode node; + node.partId = m_bonesPartId; + node.id = QUuid::createUuid(); + node.setRadius(m_nodeRadius); + node.x = bone.headPosition.x() + 0.5; + node.y = -bone.headPosition.y() + 0.5; + node.z = -bone.headPosition.z() + 1; + nodeMap[node.id] = node; + newAddedNodeIds.insert(node.id); + boneIndexToHeadNodeIdMap[edgePair.first] = node.id; + firstNodeId = node.id; + } } else { firstNodeId = findFirst->second; } auto findSecond = boneIndexToHeadNodeIdMap.find(edgePair.second); if (findSecond == boneIndexToHeadNodeIdMap.end()) { const auto &bone = (*rigBones)[edgePair.second]; - SkeletonNode node; - node.partId = m_bonesPartId; - node.id = QUuid::createUuid(); - node.setRadius(m_nodeRadius); - node.x = bone.headPosition.x() + 0.5; - node.y = -bone.headPosition.y() + 0.5; - node.z = -bone.headPosition.z() + 1; - nodeMap[node.id] = node; - newAddedNodeIds.insert(node.id); - boneIndexToHeadNodeIdMap[edgePair.second] = node.id; - secondNodeId = node.id; + if (!bone.name.startsWith("Virtual_")) { + SkeletonNode node; + node.partId = m_bonesPartId; + node.id = QUuid::createUuid(); + node.setRadius(m_nodeRadius); + node.x = bone.headPosition.x() + 0.5; + node.y = -bone.headPosition.y() + 0.5; + node.z = -bone.headPosition.z() + 1; + nodeMap[node.id] = node; + newAddedNodeIds.insert(node.id); + boneIndexToHeadNodeIdMap[edgePair.second] = node.id; + secondNodeId = node.id; + } } else { secondNodeId = findSecond->second; } + if (firstNodeId.isNull() || secondNodeId.isNull()) + continue; + SkeletonEdge edge; edge.partId = m_bonesPartId; edge.id = QUuid::createUuid(); @@ -231,8 +238,11 @@ void PoseDocument::updateRigBones(const std::vector *rigBones, const nodeMap[firstNodeId].edgeIds.push_back(edge.id); nodeMap[secondNodeId].edgeIds.push_back(edge.id); } - for (size_t i = 0; i < rigBones->size(); ++i) { + + for (size_t i = 1; i < rigBones->size(); ++i) { const auto &bone = (*rigBones)[i]; + if (bone.name.startsWith("Virtual_")) + continue; if (bone.children.empty()) { const QUuid &firstNodeId = boneIndexToHeadNodeIdMap[i]; diff --git a/src/poseeditwidget.h b/src/poseeditwidget.h index 9b8af96c..a1ec1d82 100644 --- a/src/poseeditwidget.h +++ b/src/poseeditwidget.h @@ -11,8 +11,6 @@ #include "skeletongraphicswidget.h" #include "posedocument.h" -typedef RiggerButtonParameterType PopupWidgetType; - class PoseEditWidget : public QDialog { Q_OBJECT diff --git a/src/posemanagewidget.cpp b/src/posemanagewidget.cpp index 2e5d2f65..24b329a0 100644 --- a/src/posemanagewidget.cpp +++ b/src/posemanagewidget.cpp @@ -26,7 +26,7 @@ PoseManageWidget::PoseManageWidget(const Document *document, QWidget *parent) : auto refreshInfoLabel = [=]() { if (m_document->currentRigSucceed()) { - if (m_document->rigType == RigType::Tetrapod) { + if (m_document->rigType == RigType::Animal) { infoLabel->setText(""); infoLabel->hide(); addPoseButton->show(); diff --git a/src/poserconstruct.cpp b/src/poserconstruct.cpp index 59f55ac8..502de74e 100644 --- a/src/poserconstruct.cpp +++ b/src/poserconstruct.cpp @@ -1,12 +1,9 @@ #include "poserconstruct.h" -#include "tetrapodposer.h" -#include "genericposer.h" +#include "animalposer.h" Poser *newPoser(RigType rigType, const std::vector &bones) { - if (rigType == RigType::Tetrapod) - return new TetrapodPoser(bones); - else if (rigType == RigType::Generic) - return new GenericPoser(bones); + if (rigType == RigType::Animal) + return new AnimalPoser(bones); return nullptr; -} \ No newline at end of file +} diff --git a/src/riggenerator.cpp b/src/riggenerator.cpp index e675e367..60176869 100644 --- a/src/riggenerator.cpp +++ b/src/riggenerator.cpp @@ -53,14 +53,9 @@ bool RigGenerator::isSucceed() return m_isSucceed; } -const std::vector &RigGenerator::missingMarkNames() +const std::vector> &RigGenerator::messages() { - return m_missingMarkNames; -} - -const std::vector &RigGenerator::errorMarkNames() -{ - return m_errorMarkNames; + return m_messages; } void RigGenerator::generate() @@ -84,8 +79,7 @@ void RigGenerator::generate() if (bmeshNode.boneMark == BoneMark::None) continue; SkeletonSide boneSide = SkeletonSide::None; - if (BoneMarkHasSide(bmeshNode.boneMark) && - std::abs(bmeshNode.origin.x()) > 0.01 ) { + if (BoneMarkHasSide(bmeshNode.boneMark)) { boneSide = bmeshNode.origin.x() > 0 ? SkeletonSide::Left : SkeletonSide::Right; } //qDebug() << "Add bone mark:" << BoneMarkToString(bmeshNode.boneMark) << "side:" << SkeletonSideToDispName(boneSide); @@ -168,12 +162,11 @@ void RigGenerator::generate() qDebug() << "Rig succeed"; } else { qDebug() << "Rig failed"; - if (nullptr != m_autoRigger) { - m_missingMarkNames = m_autoRigger->missingMarkNames(); - m_errorMarkNames = m_autoRigger->errorMarkNames(); - for (const auto &message: m_autoRigger->messages()) { - qDebug() << "errorType:" << message.first << "Message:" << message.second; - } + } + if (nullptr != m_autoRigger) { + m_messages = m_autoRigger->messages(); + for (const auto &message: m_autoRigger->messages()) { + qDebug() << "errorType:" << message.first << "Message:" << message.second; } } diff --git a/src/riggenerator.h b/src/riggenerator.h index be542d70..6564dab0 100644 --- a/src/riggenerator.h +++ b/src/riggenerator.h @@ -17,8 +17,7 @@ public: MeshLoader *takeResultMesh(); std::vector *takeResultBones(); std::map *takeResultWeights(); - const std::vector &missingMarkNames(); - const std::vector &errorMarkNames(); + const std::vector> &messages(); Outcome *takeOutcome(); bool isSucceed(); void generate(); @@ -33,8 +32,7 @@ private: Rigger *m_autoRigger = nullptr; std::vector *m_resultBones = nullptr; std::map *m_resultWeights = nullptr; - std::vector m_missingMarkNames; - std::vector m_errorMarkNames; + std::vector> m_messages; bool m_isSucceed = false; }; diff --git a/src/rigger.cpp b/src/rigger.cpp index e25d7ccb..889939c3 100644 --- a/src/rigger.cpp +++ b/src/rigger.cpp @@ -11,7 +11,8 @@ QString Rigger::rootBoneName = "Body"; Rigger::Rigger(const std::vector &verticesPositions, const std::set &inputTriangles) : m_verticesPositions(verticesPositions), - m_inputTriangles(inputTriangles) + m_inputTriangles(inputTriangles), + m_extraMessagedAdded(false) { } @@ -58,10 +59,7 @@ bool Rigger::addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bo if (isCutOffSplitter(mark.boneMark)) { if (!mark.split(m_inputTriangles, m_maxCutOffSplitterExpandRound)) { - m_marksMap[std::make_pair(mark.boneMark, mark.boneSide)].push_back(m_marks.size() - 1); - m_errorMarkNames.push_back(SkeletonSideToDispName(mark.boneSide) + " " + BoneMarkToDispName(mark.boneMark)); - m_messages.push_back(std::make_pair(QtCriticalMsg, - tr("Mark \"%1 %2\" couldn't cut off the mesh").arg(SkeletonSideToDispName(mark.boneSide)).arg(BoneMarkToString(boneMark)))); + m_cutoffErrorItems.push_back(SkeletonSideToDispName(mark.boneSide) + " " + BoneMarkToDispName(mark.boneMark)); return false; } } @@ -73,6 +71,22 @@ bool Rigger::addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bo const std::vector> &Rigger::messages() { + if (!m_extraMessagedAdded) { + m_extraMessagedAdded = true; + if (!m_cutoffErrorItems.empty()) { + QStringList cutoffErrorNames; + for (const auto &item: m_cutoffErrorItems) { + cutoffErrorNames.append("" + item + ""); + } + m_messages.push_back(std::make_pair(QtCriticalMsg, tr("The following marks couldn't cut off the mesh, pelease try mark more nearby nodes for them: %1").arg(cutoffErrorNames.join(", ")))); + } + if (!m_jointErrorItems.empty()) { + QStringList jointErrorNames; + for (const auto &item: m_jointErrorItems) + jointErrorNames.append("" + item + ""); + m_messages.push_back(std::make_pair(QtCriticalMsg, tr("The following marks looks like don't contain any vertices, pelease try mark other nearby nodes for them: %1").arg(jointErrorNames.join(", ")))); + } + } return m_messages; } @@ -246,12 +260,3 @@ void Rigger::addVerticesToWeights(const std::set &vertices, int boneIndex) } } -const std::vector &Rigger::missingMarkNames() -{ - return m_missingMarkNames; -} - -const std::vector &Rigger::errorMarkNames() -{ - return m_errorMarkNames; -} diff --git a/src/rigger.h b/src/rigger.h index 771edaaa..ce0523f4 100644 --- a/src/rigger.h +++ b/src/rigger.h @@ -11,14 +11,6 @@ #include "rigtype.h" #include "skeletonside.h" -enum class RiggerButtonParameterType -{ - None = 0, - PitchYawRoll, - Intersection, - Translation -}; - class RiggerMark { public: @@ -65,9 +57,6 @@ public: QVector3D headPosition; QVector3D tailPosition; QColor color; - bool hasButton = false; - RiggerButtonParameterType buttonParameterType = RiggerButtonParameterType::None; - std::pair button = {0, 0}; QVector3D baseNormal; std::vector children; }; @@ -122,8 +111,6 @@ public: const std::vector> &messages(); const std::vector &resultBones(); const std::map &resultWeights(); - const std::vector &missingMarkNames(); - const std::vector &errorMarkNames(); virtual bool rig() = 0; static QString rootBoneName; protected: @@ -152,9 +139,10 @@ protected: std::map, std::vector> m_marksMap; std::vector m_resultBones; std::map m_resultWeights; - std::vector m_missingMarkNames; - std::vector m_errorMarkNames; + std::vector m_cutoffErrorItems; + std::vector m_jointErrorItems; static size_t m_maxCutOffSplitterExpandRound; + bool m_extraMessagedAdded; }; #endif diff --git a/src/riggerconstruct.cpp b/src/riggerconstruct.cpp index 01f218b3..147628e8 100644 --- a/src/riggerconstruct.cpp +++ b/src/riggerconstruct.cpp @@ -1,13 +1,10 @@ #include "riggerconstruct.h" -#include "tetrapodrigger.h" -#include "genericrigger.h" +#include "animalrigger.h" Rigger *newRigger(RigType rigType, const std::vector &verticesPositions, const std::set &inputTriangles) { - if (rigType == RigType::Tetrapod) - return new TetrapodRigger(verticesPositions, inputTriangles); - else if (rigType == RigType::Generic) - return new GenericRigger(verticesPositions, inputTriangles); + if (rigType == RigType::Animal) + return new AnimalRigger(verticesPositions, inputTriangles); return nullptr; } \ No newline at end of file diff --git a/src/rigtype.h b/src/rigtype.h index 693e44d9..d40325c4 100644 --- a/src/rigtype.h +++ b/src/rigtype.h @@ -5,8 +5,7 @@ enum class RigType { None = 0, - Generic, - Tetrapod, + Animal, Count }; RigType RigTypeFromString(const char *typeString); @@ -14,10 +13,8 @@ RigType RigTypeFromString(const char *typeString); RigType RigTypeFromString(const char *typeString) \ { \ QString type = typeString; \ - if (type == "Generic") \ - return RigType::Generic; \ - if (type == "Tetrapod") \ - return RigType::Tetrapod; \ + if (type == "Animal") \ + return RigType::Animal; \ return RigType::None; \ } const char *RigTypeToString(RigType type); @@ -25,10 +22,8 @@ const char *RigTypeToString(RigType type); const char *RigTypeToString(RigType type) \ { \ switch (type) { \ - case RigType::Generic: \ - return "Generic"; \ - case RigType::Tetrapod: \ - return "Tetrapod"; \ + case RigType::Animal: \ + return "Animal"; \ case RigType::None: \ return "None"; \ default: \ @@ -40,10 +35,8 @@ QString RigTypeToDispName(RigType type); QString RigTypeToDispName(RigType type) \ { \ switch (type) { \ - case RigType::Generic: \ - return QObject::tr("Generic"); \ - case RigType::Tetrapod: \ - return QObject::tr("Tetrapod"); \ + case RigType::Animal: \ + return QObject::tr("Animal"); \ case RigType::None: \ return QObject::tr("None"); \ default: \ diff --git a/src/rigwidget.cpp b/src/rigwidget.cpp index 3e02823b..28474063 100644 --- a/src/rigwidget.cpp +++ b/src/rigwidget.cpp @@ -19,13 +19,8 @@ RigWidget::RigWidget(const Document *document, QWidget *parent) : m_rigTypeBox->addItem(RigTypeToDispName(rigType)); } - //m_poseCheckButton = new QPushButton(QChar(fa::child)); - //Theme::initAwesomeButton(m_poseCheckButton); - //m_poseCheckButton->hide(); - QHBoxLayout *controlsLayout = new QHBoxLayout; controlsLayout->addWidget(m_rigTypeBox); - //controlsLayout->addWidget(m_poseCheckButton); formLayout->addRow(tr("Type"), controlsLayout); @@ -38,23 +33,17 @@ RigWidget::RigWidget(const Document *document, QWidget *parent) : m_rigWeightRenderWidget = new ModelWidget(this); m_rigWeightRenderWidget->setMinimumSize(128, 128); - //m_rigWeightRenderWidget->resize(256, 256); - //m_rigWeightRenderWidget->move(-64, 0); m_rigWeightRenderWidget->setXRotation(0); m_rigWeightRenderWidget->setYRotation(0); m_rigWeightRenderWidget->setZRotation(0); - m_missingMarksInfoLabel = new InfoLabel; - m_missingMarksInfoLabel->hide(); - - m_errorMarksInfoLabel = new InfoLabel; - m_errorMarksInfoLabel->hide(); + m_infoLabel = new InfoLabel; + m_infoLabel->hide(); QVBoxLayout *layout = new QVBoxLayout; layout->addLayout(formLayout); layout->addWidget(m_rigWeightRenderWidget); - layout->addWidget(m_missingMarksInfoLabel); - layout->addWidget(m_errorMarksInfoLabel); + layout->addWidget(m_infoLabel); layout->addStretch(); setLayout(layout); @@ -62,36 +51,21 @@ RigWidget::RigWidget(const Document *document, QWidget *parent) : void RigWidget::rigTypeChanged() { - //m_poseCheckButton->setVisible(RigType::None != m_document->rigType); m_rigTypeBox->setCurrentIndex((int)m_document->rigType); } void RigWidget::updateResultInfo() { - QStringList missingMarkNames; - for (const auto &markName: m_document->resultRigMissingMarkNames()) { - missingMarkNames.append(markName); - } - QString missingNamesString = missingMarkNames.join(tr(", ")); - if (missingNamesString.isEmpty()) { - m_missingMarksInfoLabel->hide(); + const auto &messages = m_document->resultRigMessages(); + if (messages.empty()) { + m_infoLabel->hide(); } else { - m_missingMarksInfoLabel->setText(tr("Missing marks: ") + missingNamesString); - m_missingMarksInfoLabel->setMaximumWidth(width() * 0.8); - m_missingMarksInfoLabel->show(); - } - - QStringList errorMarkNames; - for (const auto &markName: m_document->resultRigErrorMarkNames()) { - errorMarkNames.append(markName); - } - QString errorNamesString = errorMarkNames.join(tr(",")); - if (errorNamesString.isEmpty()) { - m_errorMarksInfoLabel->hide(); - } else { - m_errorMarksInfoLabel->setText(tr("Error marks: ") + errorNamesString); - m_errorMarksInfoLabel->setMaximumWidth(width() * 0.8); - m_errorMarksInfoLabel->show(); + QStringList messageList; + for (const auto &item: messages) + messageList.append(item.second); + m_infoLabel->setText(messageList.join("

")); + m_infoLabel->setMaximumWidth(width() * 0.90); + m_infoLabel->show(); } } diff --git a/src/rigwidget.h b/src/rigwidget.h index 4096daef..d5eee5e8 100644 --- a/src/rigwidget.h +++ b/src/rigwidget.h @@ -23,8 +23,7 @@ private: const Document *m_document = nullptr; QComboBox *m_rigTypeBox = nullptr; ModelWidget *m_rigWeightRenderWidget = nullptr; - InfoLabel *m_missingMarksInfoLabel = nullptr; - InfoLabel *m_errorMarksInfoLabel = nullptr; + InfoLabel *m_infoLabel = nullptr; }; #endif diff --git a/src/tetrapodposer.cpp b/src/tetrapodposer.cpp deleted file mode 100644 index f8c09204..00000000 --- a/src/tetrapodposer.cpp +++ /dev/null @@ -1,219 +0,0 @@ -#include -#include -#include "tetrapodposer.h" -#include "util.h" - -TetrapodPoser::TetrapodPoser(const std::vector &bones) : - Poser(bones) -{ -} - -void TetrapodPoser::resolveTranslation() -{ - 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; - } - } - resolveLimbRotation({QString("LeftUpperArm"), QString("LeftLowerArm"), QString("LeftHand")}); - resolveLimbRotation({QString("RightUpperArm"), QString("RightLowerArm"), QString("RightHand")}); - resolveLimbRotation({QString("LeftUpperLeg"), QString("LeftLowerLeg"), QString("LeftFoot")}); - resolveLimbRotation({QString("RightUpperLeg"), QString("RightLowerLeg"), QString("RightFoot")}); - resolveLimbRotation({QString("Spine"), QString("Chest"), QString("Neck"), QString("Head")}); -} - -std::pair TetrapodPoser::findQVector3DFromMap(const std::map &map, const QString &xName, const QString &yName, const QString &zName) -{ - auto findXResult = map.find(xName); - auto findYResult = map.find(yName); - auto findZResult = map.find(zName); - if (findXResult == map.end() && - findYResult == map.end() && - findZResult == map.end()) { - return {false, QVector3D()}; - } - return {true, { - valueOfKeyInMapOrEmpty(map, xName).toFloat(), - valueOfKeyInMapOrEmpty(map, yName).toFloat(), - valueOfKeyInMapOrEmpty(map, zName).toFloat() - }}; -} - -std::pair> TetrapodPoser::findBonePositionsFromParameters(const std::map &map) -{ - auto findBoneStartResult = findQVector3DFromMap(map, "fromX", "fromY", "fromZ"); - auto findBoneStopResult = findQVector3DFromMap(map, "toX", "toY", "toZ"); - - if (!findBoneStartResult.first || !findBoneStopResult.first) - return {false, {QVector3D(), QVector3D()}}; - - return {true, {findBoneStartResult.second, findBoneStopResult.second}}; -} - -void TetrapodPoser::resolveLimbRotation(const std::vector &limbBoneNames) -{ - // We match the poses by the distance and rotation plane - - if (limbBoneNames.size() < 3) { - qDebug() << "Cann't resolve limb bones with invalid joints:" << limbBoneNames.size(); - return; - } - const auto &beginBoneName = limbBoneNames[0]; - const auto &middleBoneName = limbBoneNames[1]; - const auto &endBoneName = limbBoneNames[2]; - - const auto &beginBoneParameters = parameters().find(beginBoneName); - if (beginBoneParameters == parameters().end()) { - qDebug() << beginBoneName << "'s parameters not found"; - return; - } - - auto matchBeginBonePositions = findBonePositionsFromParameters(beginBoneParameters->second); - if (!matchBeginBonePositions.first) { - qDebug() << beginBoneName << "'s positions not found"; - return; - } - - const auto &endBoneParameters = parameters().find(endBoneName); - if (endBoneParameters == parameters().end()) { - qDebug() << endBoneName << "'s parameters not found"; - return; - } - - auto matchEndBonePositions = findBonePositionsFromParameters(endBoneParameters->second); - if (!matchEndBonePositions.first) { - qDebug() << endBoneName << "'s positions not found"; - return; - } - - float matchLimbLength = (matchBeginBonePositions.second.first - matchBeginBonePositions.second.second).length() + - (matchBeginBonePositions.second.second - matchEndBonePositions.second.first).length(); - - auto matchDistanceBetweenBeginAndEndBones = (matchBeginBonePositions.second.first - matchEndBonePositions.second.first).length(); - auto matchRotatePlaneNormal = QVector3D::crossProduct((matchBeginBonePositions.second.second - matchBeginBonePositions.second.first).normalized(), (matchEndBonePositions.second.first - matchBeginBonePositions.second.second).normalized()); - - auto matchDirectionBetweenBeginAndEndPones = (matchEndBonePositions.second.first - matchBeginBonePositions.second.first).normalized(); - - auto matchEndBoneDirection = (matchEndBonePositions.second.second - matchEndBonePositions.second.first).normalized(); - - int beginBoneIndex = findBoneIndex(beginBoneName); - if (-1 == beginBoneIndex) { - qDebug() << beginBoneName << "not found in rigged bones"; - return; - } - const auto &beginBone = bones()[beginBoneIndex]; - - int middleBoneIndex = findBoneIndex(middleBoneName); - if (-1 == middleBoneIndex) { - qDebug() << middleBoneName << "not found in rigged bones"; - return; - } - - int endBoneIndex = findBoneIndex(endBoneName); - if (-1 == endBoneIndex) { - qDebug() << endBoneName << "not found in rigged bones"; - return; - } - const auto &endBone = bones()[endBoneIndex]; - - float targetBeginBoneLength = (beginBone.headPosition - beginBone.tailPosition).length(); - float targetMiddleBoneLength = (beginBone.tailPosition - endBone.headPosition).length(); - float targetLimbLength = targetBeginBoneLength + targetMiddleBoneLength; - - float targetDistanceBetweenBeginAndEndBones = matchDistanceBetweenBeginAndEndBones * (targetLimbLength / matchLimbLength); - QVector3D targetEndBoneStartPosition = beginBone.headPosition + matchDirectionBetweenBeginAndEndPones * targetDistanceBetweenBeginAndEndBones; - - float angleBetweenDistanceAndMiddleBones = 0; - { - const float &a = targetMiddleBoneLength; - const float &b = targetDistanceBetweenBeginAndEndBones; - const float &c = targetBeginBoneLength; - double cosC = (a*a + b*b - c*c) / (2.0*a*b); - angleBetweenDistanceAndMiddleBones = qRadiansToDegrees(acos(cosC)); - } - - QVector3D targetMiddleBoneStartPosition; - { - qDebug() << beginBoneName << "Angle:" << angleBetweenDistanceAndMiddleBones; - auto rotation = QQuaternion::fromAxisAndAngle(matchRotatePlaneNormal, angleBetweenDistanceAndMiddleBones); - targetMiddleBoneStartPosition = targetEndBoneStartPosition + rotation.rotatedVector(-matchDirectionBetweenBeginAndEndPones).normalized() * targetMiddleBoneLength; - } - - // Now the bones' positions have been resolved, we calculate the rotation - - auto oldBeginBoneDirection = (beginBone.tailPosition - beginBone.headPosition).normalized(); - auto newBeginBoneDirection = (targetMiddleBoneStartPosition - beginBone.headPosition).normalized(); - auto beginBoneRotation = QQuaternion::rotationTo(oldBeginBoneDirection, newBeginBoneDirection); - m_jointNodeTree.updateRotation(beginBoneIndex, beginBoneRotation); - - auto oldMiddleBoneDirection = (endBone.headPosition - beginBone.tailPosition).normalized(); - auto newMiddleBoneDirection = (targetEndBoneStartPosition - targetMiddleBoneStartPosition).normalized(); - oldMiddleBoneDirection = beginBoneRotation.rotatedVector(oldMiddleBoneDirection); - auto middleBoneRotation = QQuaternion::rotationTo(oldMiddleBoneDirection, newMiddleBoneDirection); - m_jointNodeTree.updateRotation(middleBoneIndex, middleBoneRotation); - - // Calculate the end effectors' rotation - auto oldEndBoneDirection = (endBone.tailPosition - endBone.headPosition).normalized(); - auto newEndBoneDirection = matchEndBoneDirection; - oldEndBoneDirection = beginBoneRotation.rotatedVector(oldEndBoneDirection); - oldEndBoneDirection = middleBoneRotation.rotatedVector(oldEndBoneDirection); - auto endBoneRotation = QQuaternion::rotationTo(oldEndBoneDirection, newEndBoneDirection); - m_jointNodeTree.updateRotation(endBoneIndex, endBoneRotation); - - if (limbBoneNames.size() > 3) { - std::vector rotations; - rotations.push_back(beginBoneRotation); - rotations.push_back(middleBoneRotation); - rotations.push_back(endBoneRotation); - for (size_t i = 3; i < limbBoneNames.size(); ++i) { - const auto &boneName = limbBoneNames[i]; - int boneIndex = findBoneIndex(boneName); - if (-1 == boneIndex) { - qDebug() << "Find bone failed:" << boneName; - continue; - } - const auto &bone = bones()[boneIndex]; - const auto &boneParameters = parameters().find(boneName); - if (boneParameters == parameters().end()) { - qDebug() << "Find bone parameters:" << boneName; - continue; - } - auto matchBonePositions = findBonePositionsFromParameters(boneParameters->second); - if (!matchBonePositions.first) { - qDebug() << "Find bone positions failed:" << boneName; - continue; - } - auto matchBoneDirection = (matchBonePositions.second.second - matchBonePositions.second.first).normalized(); - auto oldBoneDirection = (bone.tailPosition - bone.headPosition).normalized(); - auto newBoneDirection = matchBoneDirection; - for (const auto &rotation: rotations) { - oldBoneDirection = rotation.rotatedVector(oldBoneDirection); - } - auto boneRotation = QQuaternion::rotationTo(oldBoneDirection, newBoneDirection); - m_jointNodeTree.updateRotation(boneIndex, boneRotation); - rotations.push_back(boneRotation); - } - } -} - -void TetrapodPoser::commit() -{ - resolveTranslation(); - - Poser::commit(); -} - diff --git a/src/tetrapodrigger.cpp b/src/tetrapodrigger.cpp deleted file mode 100644 index bfbd0de9..00000000 --- a/src/tetrapodrigger.cpp +++ /dev/null @@ -1,689 +0,0 @@ -#include -#include -#include "tetrapodrigger.h" - -TetrapodRigger::TetrapodRigger(const std::vector &verticesPositions, - const std::set &inputTriangles) : - Rigger(verticesPositions, inputTriangles) -{ -} - -bool TetrapodRigger::isCutOffSplitter(BoneMark boneMark) -{ - return boneMark == BoneMark::Neck || - boneMark == BoneMark::Shoulder || - boneMark == BoneMark::Hip; -} - -bool TetrapodRigger::rig() -{ - if (!validate()) - return false; - - std::set bodyTriangles; - if (!calculateBodyTriangles(bodyTriangles)) - return false; - - auto neckIndicies = m_marksMap.find(std::make_pair(BoneMark::Neck, SkeletonSide::None)); - auto leftShoulderIndicies = m_marksMap.find(std::make_pair(BoneMark::Shoulder, SkeletonSide::Left)); - auto leftElbowIndicies = m_marksMap.find(std::make_pair(BoneMark::Elbow, SkeletonSide::Left)); - auto leftWristIndicies = m_marksMap.find(std::make_pair(BoneMark::Wrist, SkeletonSide::Left)); - auto rightShoulderIndicies = m_marksMap.find(std::make_pair(BoneMark::Shoulder, SkeletonSide::Right)); - auto rightElbowIndicies = m_marksMap.find(std::make_pair(BoneMark::Elbow, SkeletonSide::Right)); - auto rightWristIndicies = m_marksMap.find(std::make_pair(BoneMark::Wrist, SkeletonSide::Right)); - auto leftHipIndicies = m_marksMap.find(std::make_pair(BoneMark::Hip, SkeletonSide::Left)); - auto leftKneeIndicies = m_marksMap.find(std::make_pair(BoneMark::Knee, SkeletonSide::Left)); - auto leftAnkleIndicies = m_marksMap.find(std::make_pair(BoneMark::Ankle, SkeletonSide::Left)); - auto rightHipIndicies = m_marksMap.find(std::make_pair(BoneMark::Hip, SkeletonSide::Right)); - auto rightKneeIndicies = m_marksMap.find(std::make_pair(BoneMark::Knee, SkeletonSide::Right)); - auto rightAnkleIndicies = m_marksMap.find(std::make_pair(BoneMark::Ankle, SkeletonSide::Right)); - - // 1. Prepare all bones start and stop positions: - - QVector3D shouldersCenter = (m_marks[leftShoulderIndicies->second[0]].bonePosition + - m_marks[rightShoulderIndicies->second[0]].bonePosition) / 2; - QVector3D hipsCenter = (m_marks[leftHipIndicies->second[0]].bonePosition + - m_marks[rightHipIndicies->second[0]].bonePosition) / 2; - - QVector3D bonesOrigin = hipsCenter; - QVector3D chestBoneStartPosition = (shouldersCenter + hipsCenter) / 2; - QVector3D neckBoneStartPosition = shouldersCenter; - QVector3D headBoneStartPosition = m_marks[neckIndicies->second[0]].bonePosition; - QVector3D leftUpperArmBoneStartPosition = m_marks[leftShoulderIndicies->second[0]].bonePosition; - QVector3D leftLowerArmBoneStartPosition = m_marks[leftElbowIndicies->second[0]].bonePosition; - QVector3D leftHandBoneStartPosition = m_marks[leftWristIndicies->second[0]].bonePosition; - QVector3D rightUpperArmBoneStartPosition = m_marks[rightShoulderIndicies->second[0]].bonePosition; - QVector3D rightLowerArmBoneStartPosition = m_marks[rightElbowIndicies->second[0]].bonePosition; - QVector3D rightHandBoneStartPosition = m_marks[rightWristIndicies->second[0]].bonePosition; - QVector3D leftUpperLegBoneStartPosition = m_marks[leftHipIndicies->second[0]].bonePosition; - QVector3D leftLowerLegBoneStartPosition = m_marks[leftKneeIndicies->second[0]].bonePosition; - QVector3D leftFootBoneStartPosition = m_marks[leftAnkleIndicies->second[0]].bonePosition; - QVector3D rightUpperLegBoneStartPosition = m_marks[rightHipIndicies->second[0]].bonePosition; - QVector3D rightLowerLegBoneStartPosition = m_marks[rightKneeIndicies->second[0]].bonePosition; - QVector3D rightFootBoneStartPosition = m_marks[rightAnkleIndicies->second[0]].bonePosition; - - bool isMainBodyVerticalAligned = fabs(shouldersCenter.y() - hipsCenter.y()) > fabs(shouldersCenter.z() - hipsCenter.z()); - bool isLeftArmVerticalAligned = fabs(leftUpperArmBoneStartPosition.y() - leftHandBoneStartPosition.y()) > fabs(leftUpperArmBoneStartPosition.z() - leftHandBoneStartPosition.z()); - bool isRightArmVerticalAligned = fabs(rightUpperArmBoneStartPosition.y() - rightHandBoneStartPosition.y()) > fabs(rightUpperArmBoneStartPosition.z() - rightHandBoneStartPosition.z()); - - std::set headVertices; - addTrianglesToVertices(m_marks[neckIndicies->second[0]].smallGroup(), headVertices); - addTrianglesToVertices(m_marks[neckIndicies->second[0]].markTriangles, headVertices); - QVector3D headBoneStopPosition; - if (isMainBodyVerticalAligned) { - QVector3D maxY = findMaxY(headVertices); - headBoneStopPosition = QVector3D(headBoneStartPosition.x(), - maxY.y(), - maxY.z()); - } else { - QVector3D maxZ = findMaxZ(headVertices); - headBoneStopPosition = QVector3D(headBoneStartPosition.x(), - maxZ.y(), - maxZ.z()); - } - - std::set leftArmVertices; - addTrianglesToVertices(m_marks[leftShoulderIndicies->second[0]].smallGroup(), leftArmVertices); - QVector3D leftHandBoneStopPosition; - if (isLeftArmVerticalAligned) { - QVector3D minY = findMinY(leftArmVertices); - leftHandBoneStopPosition = QVector3D(leftHandBoneStartPosition.x(), - minY.y(), - minY.z()); - } else { - QVector3D maxX = findMaxZ(leftArmVertices); - leftHandBoneStopPosition = QVector3D(maxX.x(), - maxX.y(), - leftHandBoneStartPosition.z()); - } - addTrianglesToVertices(m_marks[leftShoulderIndicies->second[0]].markTriangles, leftArmVertices); - - std::set rightArmVertices; - addTrianglesToVertices(m_marks[rightShoulderIndicies->second[0]].smallGroup(), rightArmVertices); - QVector3D rightHandBoneStopPosition; - if (isRightArmVerticalAligned) { - QVector3D minY = findMinY(rightArmVertices); - rightHandBoneStopPosition = QVector3D(rightHandBoneStartPosition.x(), - minY.y(), - minY.z()); - } else { - QVector3D minX = findMinX(rightArmVertices); - rightHandBoneStopPosition = QVector3D(minX.x(), - minX.y(), - rightHandBoneStartPosition.z()); - } - addTrianglesToVertices(m_marks[rightShoulderIndicies->second[0]].markTriangles, rightArmVertices); - - std::set leftLegVertices; - QVector3D leftFootBoneStopPosition; - addTrianglesToVertices(m_marks[leftHipIndicies->second[0]].smallGroup(), leftLegVertices); - { - QVector3D maxZ = findMaxZ(leftLegVertices); - leftFootBoneStopPosition = QVector3D(leftFootBoneStartPosition.x(), - maxZ.y(), - maxZ.z()); - } - addTrianglesToVertices(m_marks[leftHipIndicies->second[0]].markTriangles, leftLegVertices); - - std::set rightLegVertices; - QVector3D rightFootBoneStopPosition; - addTrianglesToVertices(m_marks[rightHipIndicies->second[0]].smallGroup(), rightLegVertices); - { - QVector3D maxZ = findMaxZ(rightLegVertices); - rightFootBoneStopPosition = QVector3D(rightFootBoneStartPosition.x(), - maxZ.y(), - maxZ.z()); - } - addTrianglesToVertices(m_marks[rightHipIndicies->second[0]].markTriangles, rightLegVertices); - - // 2. Collect vertices for each bone: - - // 2.1 Collect vertices for neck bone: - std::set bodyVertices; - addTrianglesToVertices(bodyTriangles, bodyVertices); - addTrianglesToVertices(m_marks[leftShoulderIndicies->second[0]].markTriangles, bodyVertices); - addTrianglesToVertices(m_marks[rightShoulderIndicies->second[0]].markTriangles, bodyVertices); - - std::set bodyVerticesAfterShoulder; - std::set neckVertices; - { - std::set shoulderMarkVertices; - addTrianglesToVertices(m_marks[leftShoulderIndicies->second[0]].markTriangles, shoulderMarkVertices); - addTrianglesToVertices(m_marks[rightShoulderIndicies->second[0]].markTriangles, shoulderMarkVertices); - if (isMainBodyVerticalAligned) { - QVector3D maxY = findMaxY(shoulderMarkVertices); - splitVerticesByY(bodyVertices, maxY.y(), neckVertices, bodyVerticesAfterShoulder); - } else { - QVector3D maxZ = findMaxZ(shoulderMarkVertices); - splitVerticesByZ(bodyVertices, maxZ.z(), neckVertices, bodyVerticesAfterShoulder); - } - } - addTrianglesToVertices(m_marks[neckIndicies->second[0]].markTriangles, neckVertices); - - // 2.2 Collect vertices for chest bone: - - // Calculate neck's radius - float neckRadius = 0; - std::set neckMarkVertices; - addTrianglesToVertices(m_marks[neckIndicies->second[0]].markTriangles, neckMarkVertices); - { - QVector3D minX, minY, minZ, maxX, maxY, maxZ; - resolveBoundingBox(neckMarkVertices, minX, maxX, minY, maxY, minZ, maxZ); - neckRadius = fabs(minX.x() - maxX.x()); - } - - std::set bodyVerticesAfterChest; - std::set chestVertices; - if (isMainBodyVerticalAligned) { - splitVerticesByY(bodyVerticesAfterShoulder, chestBoneStartPosition.y() - neckRadius, chestVertices, bodyVerticesAfterChest); - } else { - splitVerticesByZ(bodyVerticesAfterShoulder, chestBoneStartPosition.z() - neckRadius, chestVertices, bodyVerticesAfterChest); - } - - // 2.3 Collect vertices for spine bone: - std::set bodyVerticesBeforeSpine; - std::set spineVertices; - if (isMainBodyVerticalAligned) { - splitVerticesByY(bodyVerticesAfterShoulder, chestBoneStartPosition.y() + neckRadius, bodyVerticesBeforeSpine, spineVertices); - } else { - splitVerticesByZ(bodyVerticesAfterShoulder, chestBoneStartPosition.z() + neckRadius, bodyVerticesBeforeSpine, spineVertices); - } - - // 3. Collect vertices for arms: - - // 3.1.1 Collect vertices for left upper arm: - std::set leftElbowMarkVertices; - addTrianglesToVertices(m_marks[leftElbowIndicies->second[0]].markTriangles, leftElbowMarkVertices); - std::set leftUpperArmVertices; - { - std::set leftArmVerticesAfterElbow; - if (isLeftArmVerticalAligned) { - QVector3D minY = findMinY(leftElbowMarkVertices); - splitVerticesByY(leftArmVertices, minY.y(), leftUpperArmVertices, leftArmVerticesAfterElbow); - } else { - QVector3D maxX = findMaxX(leftElbowMarkVertices); - splitVerticesByX(leftArmVertices, maxX.x(), leftArmVerticesAfterElbow, leftUpperArmVertices); - } - } - - // 3.1.2 Collect vertices for left lower arm: - std::set leftArmVerticesSinceElbow; - std::set leftLowerArmVertices; - { - std::set leftArmVerticesBeforeElbow; - if (isLeftArmVerticalAligned) { - splitVerticesByY(leftArmVertices, m_marks[leftElbowIndicies->second[0]].bonePosition.y(), leftArmVerticesBeforeElbow, leftArmVerticesSinceElbow); - } else { - splitVerticesByX(leftArmVertices, m_marks[leftElbowIndicies->second[0]].bonePosition.x(), leftArmVerticesSinceElbow, leftArmVerticesBeforeElbow); - } - } - std::set leftWristMarkVertices; - addTrianglesToVertices(m_marks[leftWristIndicies->second[0]].markTriangles, leftWristMarkVertices); - { - std::set leftArmVerticesAfterWrist; - if (isLeftArmVerticalAligned) { - QVector3D minY = findMinY(leftWristMarkVertices); - splitVerticesByY(leftArmVerticesSinceElbow, minY.y(), leftLowerArmVertices, leftArmVerticesAfterWrist); - } else { - QVector3D maxX = findMaxX(leftWristMarkVertices); - splitVerticesByX(leftArmVerticesSinceElbow, maxX.x(), leftArmVerticesAfterWrist, leftLowerArmVertices); - } - } - - // 3.1.3 Collect vertices for left hand: - std::set leftHandVertices; - { - std::set leftArmVerticesBeforeWrist; - if (isLeftArmVerticalAligned) { - splitVerticesByY(leftArmVerticesSinceElbow, m_marks[leftWristIndicies->second[0]].bonePosition.y(), leftArmVerticesBeforeWrist, leftHandVertices); - } else { - splitVerticesByX(leftArmVerticesSinceElbow, m_marks[leftWristIndicies->second[0]].bonePosition.x(), leftHandVertices, leftArmVerticesBeforeWrist); - } - } - - // 3.2.1 Collect vertices for right upper arm: - std::set rightElbowMarkVertices; - addTrianglesToVertices(m_marks[rightElbowIndicies->second[0]].markTriangles, rightElbowMarkVertices); - std::set rightUpperArmVertices; - { - std::set rightArmVerticesAfterElbow; - if (isRightArmVerticalAligned) { - QVector3D minY = findMinY(rightElbowMarkVertices); - splitVerticesByY(rightArmVertices, minY.y(), rightUpperArmVertices, rightArmVerticesAfterElbow); - } else { - QVector3D minX = findMinX(rightElbowMarkVertices); - splitVerticesByX(rightArmVertices, minX.x(), rightUpperArmVertices, rightArmVerticesAfterElbow); - } - } - - // 3.2.2 Collect vertices for right lower arm: - std::set rightArmVerticesSinceElbow; - std::set rightLowerArmVertices; - { - std::set rightArmVerticesBeforeElbow; - if (isRightArmVerticalAligned) { - splitVerticesByY(rightArmVertices, m_marks[rightElbowIndicies->second[0]].bonePosition.y(), rightArmVerticesBeforeElbow, rightArmVerticesSinceElbow); - } else { - splitVerticesByX(rightArmVertices, m_marks[rightElbowIndicies->second[0]].bonePosition.x(), rightArmVerticesBeforeElbow, rightArmVerticesSinceElbow); - } - } - std::set rightWristMarkVertices; - addTrianglesToVertices(m_marks[rightWristIndicies->second[0]].markTriangles, rightWristMarkVertices); - { - std::set rightArmVerticesAfterWrist; - if (isRightArmVerticalAligned) { - QVector3D minY = findMinY(rightWristMarkVertices); - splitVerticesByY(rightArmVerticesSinceElbow, minY.y(), rightLowerArmVertices, rightArmVerticesAfterWrist); - } else { - QVector3D minX = findMinX(rightWristMarkVertices); - splitVerticesByX(rightArmVerticesSinceElbow, minX.x(), rightLowerArmVertices, rightArmVerticesAfterWrist); - } - } - - // 3.2.3 Collect vertices for right hand: - std::set rightHandVertices; - { - std::set rightArmVerticesBeforeWrist; - if (isRightArmVerticalAligned) { - splitVerticesByY(rightArmVerticesSinceElbow, m_marks[rightWristIndicies->second[0]].bonePosition.y(), rightArmVerticesBeforeWrist, rightHandVertices); - } else { - splitVerticesByX(rightArmVerticesSinceElbow, m_marks[rightWristIndicies->second[0]].bonePosition.x(), rightArmVerticesBeforeWrist, rightHandVertices); - } - } - - // 4. Collect vertices for legs: - - // 4.1.1 Collect vertices for left upper leg: - std::set leftKneeMarkVertices; - addTrianglesToVertices(m_marks[leftKneeIndicies->second[0]].markTriangles, leftKneeMarkVertices); - std::set leftUpperLegVertices; - { - std::set leftLegVerticesAfterKnee; - QVector3D minY = findMinY(leftKneeMarkVertices); - splitVerticesByY(leftLegVertices, minY.y(), leftUpperLegVertices, leftLegVerticesAfterKnee); - } - - // 4.1.2 Collect vertices for left lower leg: - std::set leftLegVerticesSinceKnee; - std::set leftLowerLegVertices; - { - std::set leftLegVerticesBeforeKnee; - QVector3D maxY = findMaxY(leftKneeMarkVertices); - splitVerticesByY(leftLegVertices, maxY.y(), leftLegVerticesBeforeKnee, leftLegVerticesSinceKnee); - } - std::set leftAnkleMarkVertices; - addTrianglesToVertices(m_marks[leftAnkleIndicies->second[0]].markTriangles, leftAnkleMarkVertices); - { - std::set leftLegVerticesAfterAnkle; - QVector3D maxY = findMaxY(leftAnkleMarkVertices); - QVector3D minY = findMinY(leftAnkleMarkVertices); - splitVerticesByY(leftLegVerticesSinceKnee, (maxY.y() + minY.y()) / 2, leftLowerLegVertices, leftLegVerticesAfterAnkle); - } - - // 4.1.3 Collect vertices for left foot: - std::set leftFootVertices; - { - std::set leftLegVerticesBeforeAnkle; - QVector3D maxY = findMaxY(leftAnkleMarkVertices); - QVector3D minY = findMinY(leftAnkleMarkVertices); - splitVerticesByY(leftLegVerticesSinceKnee, (maxY.y() + minY.y()) / 2, leftLegVerticesBeforeAnkle, leftFootVertices); - } - - // 4.2.1 Collect vertices for right upper leg: - std::set rightKneeMarkVertices; - addTrianglesToVertices(m_marks[rightKneeIndicies->second[0]].markTriangles, rightKneeMarkVertices); - std::set rightUpperLegVertices; - { - std::set rightLegVerticesAfterKnee; - QVector3D minY = findMinY(rightKneeMarkVertices); - splitVerticesByY(rightLegVertices, minY.y(), rightUpperLegVertices, rightLegVerticesAfterKnee); - } - - // 4.2.2 Collect vertices for right lower leg: - std::set rightLegVerticesSinceKnee; - std::set rightLowerLegVertices; - { - std::set rightLegVerticesBeforeKnee; - QVector3D maxY = findMaxY(rightKneeMarkVertices); - splitVerticesByY(rightLegVertices, maxY.y(), rightLegVerticesBeforeKnee, rightLegVerticesSinceKnee); - } - std::set rightAnkleMarkVertices; - addTrianglesToVertices(m_marks[rightAnkleIndicies->second[0]].markTriangles, rightAnkleMarkVertices); - { - std::set rightLegVerticesAfterAnkle; - QVector3D maxY = findMaxY(rightAnkleMarkVertices); - QVector3D minY = findMinY(rightAnkleMarkVertices); - splitVerticesByY(rightLegVerticesSinceKnee, (maxY.y() + minY.y()) / 2, rightLowerLegVertices, rightLegVerticesAfterAnkle); - } - - // 4.2.3 Collect vertices for right foot: - std::set rightFootVertices; - { - std::set rightLegVerticesBeforeAnkle; - QVector3D maxY = findMaxY(rightAnkleMarkVertices); - QVector3D minY = findMinY(rightAnkleMarkVertices); - splitVerticesByY(rightLegVerticesSinceKnee, (maxY.y() + minY.y()) / 2, rightLegVerticesBeforeAnkle, rightFootVertices); - } - - // 5. Generate bones - std::map boneIndexMap; - - m_resultBones.push_back(RiggerBone()); - RiggerBone &bodyBone = m_resultBones.back(); - bodyBone.index = m_resultBones.size() - 1; - bodyBone.name = Rigger::rootBoneName; - bodyBone.headPosition = QVector3D(0, 0, 0); - bodyBone.tailPosition = bonesOrigin; - bodyBone.hasButton = true; - bodyBone.button = {7, 1}; - bodyBone.buttonParameterType = RiggerButtonParameterType::Translation; - boneIndexMap[bodyBone.name] = bodyBone.index; - - m_resultBones.push_back(RiggerBone()); - RiggerBone &leftHipBone = m_resultBones.back(); - leftHipBone.index = m_resultBones.size() - 1; - leftHipBone.name = "LeftHip"; - leftHipBone.headPosition = m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition; - leftHipBone.tailPosition = leftUpperLegBoneStartPosition; - boneIndexMap[leftHipBone.name] = leftHipBone.index; - m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(leftHipBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &leftUpperLegBone = m_resultBones.back(); - leftUpperLegBone.index = m_resultBones.size() - 1; - leftUpperLegBone.name = "LeftUpperLeg"; - leftUpperLegBone.headPosition = m_resultBones[boneIndexMap["LeftHip"]].tailPosition; - leftUpperLegBone.tailPosition = leftLowerLegBoneStartPosition; - leftUpperLegBone.color = BoneMarkToColor(BoneMark::Hip); - leftUpperLegBone.hasButton = true; - leftUpperLegBone.button = {4, 2}; - leftUpperLegBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll; - boneIndexMap[leftUpperLegBone.name] = leftUpperLegBone.index; - m_resultBones[boneIndexMap["LeftHip"]].children.push_back(leftUpperLegBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &leftLowerLegBone = m_resultBones.back(); - leftLowerLegBone.index = m_resultBones.size() - 1; - leftLowerLegBone.name = "LeftLowerLeg"; - leftLowerLegBone.headPosition = m_resultBones[boneIndexMap["LeftUpperLeg"]].tailPosition; - leftLowerLegBone.tailPosition = leftFootBoneStartPosition; - leftLowerLegBone.color = BoneMarkToColor(BoneMark::Knee); - leftLowerLegBone.hasButton = true; - leftLowerLegBone.button = {5, 2}; - leftLowerLegBone.buttonParameterType = RiggerButtonParameterType::Intersection; - boneIndexMap[leftLowerLegBone.name] = leftLowerLegBone.index; - m_resultBones[boneIndexMap["LeftUpperLeg"]].children.push_back(leftLowerLegBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &leftFootBone = m_resultBones.back(); - leftFootBone.index = m_resultBones.size() - 1; - leftFootBone.name = "LeftFoot"; - leftFootBone.headPosition = m_resultBones[boneIndexMap["LeftLowerLeg"]].tailPosition; - leftFootBone.tailPosition = leftFootBoneStopPosition; - leftFootBone.color = BoneMarkToColor(BoneMark::Ankle); - leftFootBone.hasButton = true; - leftFootBone.button = {6, 2}; - leftFootBone.buttonParameterType = RiggerButtonParameterType::Intersection; - boneIndexMap[leftFootBone.name] = leftFootBone.index; - m_resultBones[boneIndexMap["LeftLowerLeg"]].children.push_back(leftFootBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &rightHipBone = m_resultBones.back(); - rightHipBone.index = m_resultBones.size() - 1; - rightHipBone.name = "RightHip"; - rightHipBone.headPosition = m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition; - rightHipBone.tailPosition = rightUpperLegBoneStartPosition; - boneIndexMap[rightHipBone.name] = rightHipBone.index; - m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(rightHipBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &rightUpperLegBone = m_resultBones.back(); - rightUpperLegBone.index = m_resultBones.size() - 1; - rightUpperLegBone.name = "RightUpperLeg"; - rightUpperLegBone.headPosition = m_resultBones[boneIndexMap["RightHip"]].tailPosition; - rightUpperLegBone.tailPosition = rightLowerLegBoneStartPosition; - rightUpperLegBone.color = BoneMarkToColor(BoneMark::Hip); - rightUpperLegBone.hasButton = true; - rightUpperLegBone.button = {4, 0}; - rightUpperLegBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll; - boneIndexMap[rightUpperLegBone.name] = rightUpperLegBone.index; - m_resultBones[boneIndexMap["RightHip"]].children.push_back(rightUpperLegBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &rightLowerLegBone = m_resultBones.back(); - rightLowerLegBone.index = m_resultBones.size() - 1; - rightLowerLegBone.name = "RightLowerLeg"; - rightLowerLegBone.headPosition = m_resultBones[boneIndexMap["RightUpperLeg"]].tailPosition; - rightLowerLegBone.tailPosition = rightFootBoneStartPosition; - rightLowerLegBone.color = BoneMarkToColor(BoneMark::Knee); - rightLowerLegBone.hasButton = true; - rightLowerLegBone.button = {5, 0}; - rightLowerLegBone.buttonParameterType = RiggerButtonParameterType::Intersection; - boneIndexMap[rightLowerLegBone.name] = rightLowerLegBone.index; - m_resultBones[boneIndexMap["RightUpperLeg"]].children.push_back(rightLowerLegBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &rightFootBone = m_resultBones.back(); - rightFootBone.index = m_resultBones.size() - 1; - rightFootBone.name = "RightFoot"; - rightFootBone.headPosition = m_resultBones[boneIndexMap["RightLowerLeg"]].tailPosition; - rightFootBone.tailPosition = rightFootBoneStopPosition; - rightFootBone.color = BoneMarkToColor(BoneMark::Ankle); - rightFootBone.hasButton = true; - rightFootBone.button = {6, 0}; - rightFootBone.buttonParameterType = RiggerButtonParameterType::Intersection; - boneIndexMap[rightFootBone.name] = rightFootBone.index; - m_resultBones[boneIndexMap["RightLowerLeg"]].children.push_back(rightFootBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &spineBone = m_resultBones.back(); - spineBone.index = m_resultBones.size() - 1; - spineBone.name = "Spine"; - spineBone.headPosition = m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition; - spineBone.tailPosition = chestBoneStartPosition; - spineBone.color = Qt::white; - spineBone.hasButton = true; - spineBone.button = {3, 1}; - spineBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll; - boneIndexMap[spineBone.name] = spineBone.index; - m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(spineBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &chestBone = m_resultBones.back(); - chestBone.index = m_resultBones.size() - 1; - chestBone.name = "Chest"; - chestBone.headPosition = m_resultBones[boneIndexMap["Spine"]].tailPosition; - chestBone.tailPosition = neckBoneStartPosition; - chestBone.color = QColor(0x57, 0x43, 0x98); - chestBone.hasButton = true; - chestBone.button = {2, 1}; - chestBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll; - boneIndexMap[chestBone.name] = chestBone.index; - m_resultBones[boneIndexMap["Spine"]].children.push_back(chestBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &leftShoulderBone = m_resultBones.back(); - leftShoulderBone.index = m_resultBones.size() - 1; - leftShoulderBone.name = "LeftShoulder"; - leftShoulderBone.headPosition = m_resultBones[boneIndexMap["Chest"]].tailPosition; - leftShoulderBone.tailPosition = leftUpperArmBoneStartPosition; - boneIndexMap[leftShoulderBone.name] = leftShoulderBone.index; - m_resultBones[boneIndexMap["Chest"]].children.push_back(leftShoulderBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &leftUpperArmBone = m_resultBones.back(); - leftUpperArmBone.index = m_resultBones.size() - 1; - leftUpperArmBone.name = "LeftUpperArm"; - leftUpperArmBone.headPosition = m_resultBones[boneIndexMap["LeftShoulder"]].tailPosition; - leftUpperArmBone.tailPosition = leftLowerArmBoneStartPosition; - leftUpperArmBone.color = BoneMarkToColor(BoneMark::Shoulder); - leftUpperArmBone.hasButton = true; - leftUpperArmBone.button = {1, 2}; - leftUpperArmBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll; - boneIndexMap[leftUpperArmBone.name] = leftUpperArmBone.index; - m_resultBones[boneIndexMap["LeftShoulder"]].children.push_back(leftUpperArmBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &leftLowerArmBone = m_resultBones.back(); - leftLowerArmBone.index = m_resultBones.size() - 1; - leftLowerArmBone.name = "LeftLowerArm"; - leftLowerArmBone.headPosition = m_resultBones[boneIndexMap["LeftUpperArm"]].tailPosition; - leftLowerArmBone.tailPosition = leftHandBoneStartPosition; - leftLowerArmBone.color = BoneMarkToColor(BoneMark::Elbow); - leftLowerArmBone.hasButton = true; - leftLowerArmBone.button = {2, 2}; - leftLowerArmBone.buttonParameterType = RiggerButtonParameterType::Intersection; - boneIndexMap[leftLowerArmBone.name] = leftLowerArmBone.index; - m_resultBones[boneIndexMap["LeftUpperArm"]].children.push_back(leftLowerArmBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &leftHandBone = m_resultBones.back(); - leftHandBone.index = m_resultBones.size() - 1; - leftHandBone.name = "LeftHand"; - leftHandBone.headPosition = m_resultBones[boneIndexMap["LeftLowerArm"]].tailPosition; - leftHandBone.tailPosition = leftHandBoneStopPosition; - leftHandBone.color = BoneMarkToColor(BoneMark::Wrist); - leftHandBone.hasButton = true; - leftHandBone.button = {3, 2}; - leftHandBone.buttonParameterType = RiggerButtonParameterType::Intersection; - boneIndexMap[leftHandBone.name] = leftHandBone.index; - m_resultBones[boneIndexMap["LeftLowerArm"]].children.push_back(leftHandBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &rightShoulderBone = m_resultBones.back(); - rightShoulderBone.index = m_resultBones.size() - 1; - rightShoulderBone.name = "RightShoulder"; - rightShoulderBone.headPosition = m_resultBones[boneIndexMap["Chest"]].tailPosition; - rightShoulderBone.tailPosition = rightUpperArmBoneStartPosition; - boneIndexMap[rightShoulderBone.name] = rightShoulderBone.index; - m_resultBones[boneIndexMap["Chest"]].children.push_back(rightShoulderBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &rightUpperArmBone = m_resultBones.back(); - rightUpperArmBone.index = m_resultBones.size() - 1; - rightUpperArmBone.name = "RightUpperArm"; - rightUpperArmBone.headPosition = m_resultBones[boneIndexMap["RightShoulder"]].tailPosition; - rightUpperArmBone.tailPosition = rightLowerArmBoneStartPosition; - rightUpperArmBone.color = BoneMarkToColor(BoneMark::Shoulder); - rightUpperArmBone.hasButton = true; - rightUpperArmBone.button = {1, 0}; - rightUpperArmBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll; - boneIndexMap[rightUpperArmBone.name] = rightUpperArmBone.index; - m_resultBones[boneIndexMap["RightShoulder"]].children.push_back(rightUpperArmBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &rightLowerArmBone = m_resultBones.back(); - rightLowerArmBone.index = m_resultBones.size() - 1; - rightLowerArmBone.name = "RightLowerArm"; - rightLowerArmBone.headPosition = m_resultBones[boneIndexMap["RightUpperArm"]].tailPosition; - rightLowerArmBone.tailPosition = rightHandBoneStartPosition; - rightLowerArmBone.color = BoneMarkToColor(BoneMark::Elbow); - rightLowerArmBone.hasButton = true; - rightLowerArmBone.button = {2, 0}; - rightLowerArmBone.buttonParameterType = RiggerButtonParameterType::Intersection; - boneIndexMap[rightLowerArmBone.name] = rightLowerArmBone.index; - m_resultBones[boneIndexMap["RightUpperArm"]].children.push_back(rightLowerArmBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &rightHandBone = m_resultBones.back(); - rightHandBone.index = m_resultBones.size() - 1; - rightHandBone.name = "RightHand"; - rightHandBone.headPosition = m_resultBones[boneIndexMap["RightLowerArm"]].tailPosition; - rightHandBone.tailPosition = rightHandBoneStopPosition; - rightHandBone.color = BoneMarkToColor(BoneMark::Wrist); - rightHandBone.hasButton = true; - rightHandBone.button = {3, 0}; - rightHandBone.buttonParameterType = RiggerButtonParameterType::Intersection; - boneIndexMap[rightHandBone.name] = rightHandBone.index; - m_resultBones[boneIndexMap["RightLowerArm"]].children.push_back(rightHandBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &neckBone = m_resultBones.back(); - neckBone.index = m_resultBones.size() - 1; - neckBone.name = "Neck"; - neckBone.headPosition = m_resultBones[boneIndexMap["Chest"]].tailPosition; - neckBone.tailPosition = headBoneStartPosition; - neckBone.color = BoneMarkToColor(BoneMark::Neck); - neckBone.hasButton = true; - neckBone.button = {1, 1}; - neckBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll; - boneIndexMap[neckBone.name] = neckBone.index; - m_resultBones[boneIndexMap["Chest"]].children.push_back(neckBone.index); - - m_resultBones.push_back(RiggerBone()); - RiggerBone &headBone = m_resultBones.back(); - headBone.index = m_resultBones.size() - 1; - headBone.name = "Head"; - headBone.headPosition = m_resultBones[boneIndexMap["Neck"]].tailPosition; - headBone.tailPosition = headBoneStopPosition; - headBone.color = QColor(0xfb, 0xef, 0x8b); - headBone.hasButton = true; - headBone.button = {0, 1}; - headBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll; - boneIndexMap[headBone.name] = headBone.index; - m_resultBones[boneIndexMap["Neck"]].children.push_back(headBone.index); - - // 6. Calculate weights for vertices - addVerticesToWeights(headVertices, boneIndexMap["Head"]); - addVerticesToWeights(neckVertices, boneIndexMap["Neck"]); - addVerticesToWeights(chestVertices, boneIndexMap["Chest"]); - addVerticesToWeights(spineVertices, boneIndexMap["Spine"]); - addVerticesToWeights(leftUpperArmVertices, boneIndexMap["LeftUpperArm"]); - addVerticesToWeights(leftLowerArmVertices, boneIndexMap["LeftLowerArm"]); - addVerticesToWeights(leftHandVertices, boneIndexMap["LeftHand"]); - addVerticesToWeights(rightUpperArmVertices, boneIndexMap["RightUpperArm"]); - addVerticesToWeights(rightLowerArmVertices, boneIndexMap["RightLowerArm"]); - addVerticesToWeights(rightHandVertices, boneIndexMap["RightHand"]); - addVerticesToWeights(leftUpperLegVertices, boneIndexMap["LeftUpperLeg"]); - addVerticesToWeights(leftLowerLegVertices, boneIndexMap["LeftLowerLeg"]); - addVerticesToWeights(leftFootVertices, boneIndexMap["LeftFoot"]); - addVerticesToWeights(rightUpperLegVertices, boneIndexMap["RightUpperLeg"]); - addVerticesToWeights(rightLowerLegVertices, boneIndexMap["RightLowerLeg"]); - addVerticesToWeights(rightFootVertices, boneIndexMap["RightFoot"]); - - for (auto &weights: m_resultWeights) { - weights.second.finalizeWeights(); - } - - return true; -} - -bool TetrapodRigger::validate() -{ - bool foundError = false; - - std::vector> mustPresentedMarks; - - mustPresentedMarks.push_back(std::make_pair(BoneMark::Neck, SkeletonSide::None)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Shoulder, SkeletonSide::Left)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Elbow, SkeletonSide::Left)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Wrist, SkeletonSide::Left)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Shoulder, SkeletonSide::Right)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Elbow, SkeletonSide::Right)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Wrist, SkeletonSide::Right)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Hip, SkeletonSide::Left)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Knee, SkeletonSide::Left)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Ankle, SkeletonSide::Left)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Hip, SkeletonSide::Right)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Knee, SkeletonSide::Right)); - mustPresentedMarks.push_back(std::make_pair(BoneMark::Ankle, SkeletonSide::Right)); - - for (const auto &pair: mustPresentedMarks) { - if (m_marksMap.find(pair) == m_marksMap.end()) { - foundError = true; - QString markDispName = SkeletonSideToDispName(pair.second) + " " + BoneMarkToDispName(pair.first); - m_missingMarkNames.push_back(markDispName); - m_messages.push_back(std::make_pair(QtCriticalMsg, - tr("Couldn't find valid \"%1\" mark").arg(markDispName))); - } - } - - if (foundError) - return false; - - if (!m_errorMarkNames.empty() || !m_missingMarkNames.empty()) - return false; - - return true; -} - diff --git a/src/tetrapodrigger.h b/src/tetrapodrigger.h deleted file mode 100644 index 6e259003..00000000 --- a/src/tetrapodrigger.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef DUST3D_TETRAPOD_RIGGER_H -#define DUST3D_TETRAPOD_RIGGER_H -#include -#include "rigger.h" -#include "meshsplitter.h" - -class TetrapodRigger : public Rigger -{ - Q_OBJECT -public: - TetrapodRigger(const std::vector &verticesPositions, - const std::set &inputTriangles); -protected: - bool validate() override; - bool isCutOffSplitter(BoneMark boneMark) override; - bool rig() override; -}; - -#endif