From 1c17e25b69a4e5954e976d181c74c66e5925c121 Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Thu, 1 Nov 2018 23:24:06 +0800 Subject: [PATCH] Add new rig type: Generic The difference between tetrapod and generic is that, with tetrapod, all the bones need to be generated are know before mark, so if some marks are missing, there would be a message prompted; with generic, you can mark what ever marks as you wish, and the algorithm is try to create a spine and the limbs growing on the spine, that means, the generated skeleton would be some kind of creature. --- dust3d.pro | 15 + src/bonemark.cpp | 1 + src/bonemark.h | 18 + src/document.cpp | 6 +- src/fbxfile.cpp | 18 - src/genericposer.cpp | 44 +++ src/genericposer.h | 14 + src/genericrigger.cpp | 502 +++++++++++++++++++++++++ src/genericrigger.h | 26 ++ src/meshgenerator.cpp | 13 +- src/meshsplitter.cpp | 41 +- src/meshsplitter.h | 5 +- src/motioneditwidget.cpp | 2 +- src/motionsgenerator.cpp | 10 +- src/motionsgenerator.h | 8 +- src/outcome.h | 2 + src/poseeditwidget.cpp | 122 +++--- src/poseeditwidget.h | 10 +- src/posepreviewsgenerator.cpp | 8 +- src/posepreviewsgenerator.h | 5 +- src/poserconstruct.cpp | 12 + src/poserconstruct.h | 9 + src/riggenerator.cpp | 129 +++++-- src/riggenerator.h | 4 +- src/rigger.cpp | 659 ++------------------------------ src/rigger.h | 46 ++- src/riggerconstruct.cpp | 13 + src/riggerconstruct.h | 10 + src/rigtype.h | 7 + src/skeletongraphicswidget.h | 19 +- src/tetrapodposer.cpp | 1 - src/tetrapodposer.h | 2 +- src/tetrapodrigger.cpp | 686 ++++++++++++++++++++++++++++++++++ src/tetrapodrigger.h | 19 + 34 files changed, 1691 insertions(+), 795 deletions(-) create mode 100644 src/genericposer.cpp create mode 100644 src/genericposer.h create mode 100644 src/genericrigger.cpp create mode 100644 src/genericrigger.h create mode 100644 src/poserconstruct.cpp create mode 100644 src/poserconstruct.h create mode 100644 src/riggerconstruct.cpp create mode 100644 src/riggerconstruct.h create mode 100644 src/tetrapodrigger.cpp create mode 100644 src/tetrapodrigger.h diff --git a/dust3d.pro b/dust3d.pro index ac87c42e..a4ecb247 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -276,6 +276,21 @@ HEADERS += src/uvunwrap.h SOURCES += src/triangletangentresolve.cpp HEADERS += src/triangletangentresolve.h +SOURCES += src/tetrapodrigger.cpp +HEADERS += src/tetrapodrigger.h + +SOURCES += src/genericrigger.cpp +HEADERS += src/genericrigger.h + +SOURCES += src/genericposer.cpp +HEADERS += src/genericposer.h + +SOURCES += src/riggerconstruct.cpp +HEADERS += src/riggerconstruct.h + +SOURCES += src/poserconstruct.cpp +HEADERS += src/poserconstruct.h + SOURCES += src/main.cpp HEADERS += src/version.h diff --git a/src/bonemark.cpp b/src/bonemark.cpp index 5cae2573..5f4870a6 100644 --- a/src/bonemark.cpp +++ b/src/bonemark.cpp @@ -1,5 +1,6 @@ #include #include "bonemark.h" +#include "theme.h" IMPL_BoneMarkToColor IMPL_BoneMarkToString diff --git a/src/bonemark.h b/src/bonemark.h index c5d211e2..8cd60bcf 100644 --- a/src/bonemark.h +++ b/src/bonemark.h @@ -13,6 +13,8 @@ enum class BoneMark Hip, Knee, Ankle, + Limb, + Joint, Count }; #define BoneMarkHasSide(mark) ((mark) != BoneMark::Neck) @@ -35,6 +37,10 @@ QColor BoneMarkToColor(BoneMark mark) \ return QColor(0x0b, 0x24, 0xfb); \ case BoneMark::Ankle: \ return QColor(0xfc, 0x28, 0xfc); \ + case BoneMark::Limb: \ + return QColor(0xf7, 0xf7, 0xf7); \ + case BoneMark::Joint: \ + return QColor(0xfd, 0xa9, 0xaa); \ case BoneMark::None: \ return Qt::transparent; \ default: \ @@ -60,6 +66,10 @@ const char *BoneMarkToString(BoneMark mark) \ return "Knee"; \ case BoneMark::Ankle: \ return "Ankle"; \ + case BoneMark::Limb: \ + return "Limb"; \ + case BoneMark::Joint: \ + return "Joint"; \ case BoneMark::None: \ return "None"; \ default: \ @@ -85,6 +95,10 @@ BoneMark BoneMarkFromString(const char *markString) \ return BoneMark::Knee; \ if (mark == "Ankle") \ return BoneMark::Ankle; \ + if (mark == "Limb") \ + return BoneMark::Limb; \ + if (mark == "Joint") \ + return BoneMark::Joint; \ return BoneMark::None; \ } QString BoneMarkToDispName(BoneMark mark); @@ -106,6 +120,10 @@ QString BoneMarkToDispName(BoneMark mark) \ return QObject::tr("Knee"); \ case BoneMark::Ankle: \ return QObject::tr("Ankle"); \ + case BoneMark::Limb: \ + return QObject::tr("Limb (Generic)"); \ + case BoneMark::Joint: \ + return QObject::tr("Joint (Generic)"); \ case BoneMark::None: \ return QObject::tr("None"); \ default: \ diff --git a/src/document.cpp b/src/document.cpp index d6175e47..63c91578 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -2593,7 +2593,7 @@ void Document::generateRig() qDebug() << "Rig generating.."; QThread *thread = new QThread; - m_rigGenerator = new RigGenerator(*m_postProcessedOutcome); + m_rigGenerator = new RigGenerator(rigType, *m_postProcessedOutcome); m_rigGenerator->moveToThread(thread); connect(thread, &QThread::started, m_rigGenerator, &RigGenerator::process); connect(m_rigGenerator, &RigGenerator::finished, this, &Document::rigReady); @@ -2714,7 +2714,7 @@ void Document::generateMotions() return; } - m_motionsGenerator = new MotionsGenerator(rigBones, rigWeights, currentRiggedOutcome()); + m_motionsGenerator = new MotionsGenerator(rigType, rigBones, rigWeights, currentRiggedOutcome()); bool hasDirtyMotion = false; for (const auto &pose: poseMap) { m_motionsGenerator->addPoseToLibrary(pose.first, pose.second.parameters); @@ -2779,7 +2779,7 @@ void Document::generatePosePreviews() return; } - m_posePreviewsGenerator = new PosePreviewsGenerator(rigBones, + m_posePreviewsGenerator = new PosePreviewsGenerator(rigType, rigBones, rigWeights, *m_riggedOutcome); bool hasDirtyPose = false; for (auto &poseIt: poseMap) { diff --git a/src/fbxfile.cpp b/src/fbxfile.cpp index 36194d71..da473460 100644 --- a/src/fbxfile.cpp +++ b/src/fbxfile.cpp @@ -2465,24 +2465,6 @@ FbxFileWriter::FbxFileWriter(Outcome &outcome, double roll = 0; quaternionToFbxEulerAngles(rotation, &pitch, &yaw, &roll); - qDebug() << "curve:" << boneNodes[jointIndex].name << "frame:" << frame << "pitch:" << pitch << "yaw:" << yaw << "roll:" << roll; - - { - double qpitch = 0; - double qyaw = 0; - double qroll = 0; - quaternionToEulerAngles(rotation, &qpitch, &qyaw, &qroll); - if (!qFuzzyCompare(pitch, qpitch)) { - qDebug() << "pitch qt:" << qpitch << "this:" << pitch; - } - if (!qFuzzyCompare(yaw, qyaw)) { - qDebug() << "yaw qt:" << qyaw << "this:" << yaw; - } - if (!qFuzzyCompare(roll, qroll)) { - qDebug() << "roll qt:" << qroll << "this:" << roll; - } - } - values[0].push_back(pitch); values[1].push_back(yaw); values[2].push_back(roll); diff --git a/src/genericposer.cpp b/src/genericposer.cpp new file mode 100644 index 00000000..e7c3658d --- /dev/null +++ b/src/genericposer.cpp @@ -0,0 +1,44 @@ +#include +#include "genericposer.h" + +GenericPoser::GenericPoser(const std::vector &bones) : + Poser(bones) +{ +} + +void GenericPoser::commit() +{ + for (const auto &item: parameters()) { + int boneIndex = findBoneIndex(item.first); + if (-1 == boneIndex) { + continue; + } + auto findPitchResult = item.second.find("pitch"); + auto findYawResult = item.second.find("yaw"); + auto findRollResult = item.second.find("roll"); + if (findPitchResult != item.second.end() || + findYawResult != item.second.end() || + findRollResult != item.second.end()) { + float yawAngle = valueOfKeyInMapOrEmpty(item.second, "yaw").toFloat(); + if (item.first.startsWith("Left")) { + yawAngle = -yawAngle; + } + QQuaternion rotation = eulerAnglesToQuaternion(valueOfKeyInMapOrEmpty(item.second, "pitch").toFloat(), + yawAngle, + valueOfKeyInMapOrEmpty(item.second, "roll").toFloat()); + m_jointNodeTree.updateRotation(boneIndex, rotation); + continue; + } + auto findIntersectionResult = item.second.find("intersection"); + if (findIntersectionResult != item.second.end()) { + float intersectionAngle = valueOfKeyInMapOrEmpty(item.second, "intersection").toFloat(); + const RiggerBone &bone = bones()[boneIndex]; + QVector3D axis = bone.baseNormal; + QQuaternion rotation = QQuaternion::fromAxisAndAngle(axis, intersectionAngle); + m_jointNodeTree.updateRotation(boneIndex, rotation); + continue; + } + } + + Poser::commit(); +} diff --git a/src/genericposer.h b/src/genericposer.h new file mode 100644 index 00000000..ba936cc0 --- /dev/null +++ b/src/genericposer.h @@ -0,0 +1,14 @@ +#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.cpp b/src/genericrigger.cpp new file mode 100644 index 00000000..50e7e81f --- /dev/null +++ b/src/genericrigger.cpp @@ -0,0 +1,502 @@ +#include +#include +#include +#include +#include "genericrigger.h" + +GenericRigger::GenericRigger(const std::vector &verticesPositions, + const std::set &inputTriangles) : + Rigger(verticesPositions, inputTriangles) +{ +} + +bool GenericRigger::validate() +{ + return true; +} + +bool GenericRigger::isCutOffSplitter(BoneMark boneMark) +{ + return BoneMark::Limb == boneMark; +} + +BoneMark GenericRigger::translateBoneMark(BoneMark boneMark) +{ + if (boneMark == BoneMark::Neck || + boneMark == BoneMark::Shoulder || + boneMark == BoneMark::Hip || + boneMark == BoneMark::Limb) + return BoneMark::Limb; + return BoneMark::Joint; +} + +void GenericRigger::collectJointsForLimb(int markIndex, std::vector &jointMarkIndicies) +{ + const auto &mark = m_marks[markIndex]; + + jointMarkIndicies.push_back(markIndex); + + std::map triangleToMarkMap; + for (size_t i = 0; i < m_marks.size(); ++i) { + const auto &item = m_marks[i]; + if (item.boneMark == BoneMark::Joint || (int)i == markIndex) { + for (const auto &triangle: item.markTriangles) + triangleToMarkMap.insert({triangle, i}); + //qDebug() << "Mapped" << item.markTriangles.size() << "triangles for" << BoneMarkToString(item.boneMark) << SkeletonSideToDispName(item.boneSide); + } + } + + if (triangleToMarkMap.size() <= 1) { + qDebug() << "Collect joints for limb failed because of lack marks"; + return; + } + + const auto &group = mark.smallGroup(); + if (group.empty()) { + qDebug() << "Collect joints for limb failed because of lack verticies"; + return; + } + + // Build the edge to triangle map; + std::map, const MeshSplitterTriangle *> edgeToTriangleMap; + for (const auto &triangle: group) { + for (int i = 0; i < 3; ++i) { + int j = (i + 1) % 3; + edgeToTriangleMap.insert({{triangle.indicies[i], triangle.indicies[j]}, &triangle}); + } + } + + // Find startup triangles + std::queue waitTriangles; + for (const auto &triangle: mark.markTriangles) { + for (int i = 0; i < 3; i++) { + int j = (i + 1) % 3; + auto findOppositeTriangleResult = edgeToTriangleMap.find({triangle.indicies[j], triangle.indicies[i]}); + if (findOppositeTriangleResult == edgeToTriangleMap.end()) + continue; + const MeshSplitterTriangle *startupTriangle = findOppositeTriangleResult->second; + triangleToMarkMap.insert({*startupTriangle, markIndex}); + waitTriangles.push(startupTriangle); + } + } + + if (waitTriangles.empty()) { + qDebug() << "Couldn't find a triangle to start"; + return; + } + + // Traverse all the triangles and fill the triangle to mark map + std::unordered_set processedTriangles; + while (!waitTriangles.empty()) { + const MeshSplitterTriangle *triangle = waitTriangles.front(); + waitTriangles.pop(); + if (processedTriangles.find(triangle) != processedTriangles.end()) + continue; + processedTriangles.insert(triangle); + int sourceMark = -1; + auto findTriangleSourceMarkResult = triangleToMarkMap.find(*triangle); + if (findTriangleSourceMarkResult != triangleToMarkMap.end()) { + sourceMark = findTriangleSourceMarkResult->second; + } + for (int i = 0; i < 3; i++) { + int j = (i + 1) % 3; + auto findOppositeTriangleResult = edgeToTriangleMap.find({triangle->indicies[j], triangle->indicies[i]}); + if (findOppositeTriangleResult == edgeToTriangleMap.end()) + continue; + const MeshSplitterTriangle *neighborTriangle = findOppositeTriangleResult->second; + auto findTriangleSourceMarkResult = triangleToMarkMap.find(*neighborTriangle); + if (findTriangleSourceMarkResult == triangleToMarkMap.end()) { + if (-1 != sourceMark) + triangleToMarkMap.insert({*neighborTriangle, sourceMark}); + } + waitTriangles.push(neighborTriangle); + } + } + + std::map> pairs; + std::set> processedEdges; + for (const auto &edge: edgeToTriangleMap) { + if (processedEdges.find(edge.first) != processedEdges.end()) + continue; + std::pair oppositeEdge = {edge.first.second, edge.first.first}; + processedEdges.insert(edge.first); + processedEdges.insert(oppositeEdge); + auto findOppositeTriangleResult = edgeToTriangleMap.find(oppositeEdge); + if (findOppositeTriangleResult == edgeToTriangleMap.end()) + continue; + const MeshSplitterTriangle *oppositeTriangle = findOppositeTriangleResult->second; + auto findFirstTriangleMarkResult = triangleToMarkMap.find(*edge.second); + if (findFirstTriangleMarkResult == triangleToMarkMap.end()) + continue; + auto findSecondTriangleMarkResult = triangleToMarkMap.find(*oppositeTriangle); + if (findSecondTriangleMarkResult == triangleToMarkMap.end()) + continue; + if (findFirstTriangleMarkResult->second != findSecondTriangleMarkResult->second) { + pairs[findFirstTriangleMarkResult->second].insert(findSecondTriangleMarkResult->second); + pairs[findSecondTriangleMarkResult->second].insert(findFirstTriangleMarkResult->second); + } + } + + std::set visited; + auto findPairResult = pairs.find(markIndex); + visited.insert(markIndex); + while (findPairResult != pairs.end()) { + int linkTo = -1; + for (const auto &item: findPairResult->second) { + if (visited.find(item) != visited.end()) { + continue; + } + linkTo = item; + //qDebug() << "Link" << findPairResult->first << "to" << linkTo; + visited.insert(linkTo); + jointMarkIndicies.push_back(linkTo); + break; + } + if (-1 == linkTo) + break; + findPairResult = pairs.find(linkTo); + } +} + +bool GenericRigger::rig() +{ + if (!validate()) + return false; + + std::set bodyTriangles; + if (!calculateBodyTriangles(bodyTriangles)) + return false; + + std::set bodyVerticies; + bool isMainBodyVerticalAligned = false; + addTrianglesToVertices(bodyTriangles, bodyVerticies); + { + QVector3D xMin, xMax, yMin, yMax, zMin, zMax; + resolveBoundingBox(bodyVerticies, xMin, xMax, yMin, yMax, zMin, zMax); + 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)); + auto leftLimbIndicies = m_marksMap.find(std::make_pair(BoneMark::Limb, SkeletonSide::Left)); + auto rightLimbIndicies = m_marksMap.find(std::make_pair(BoneMark::Limb, SkeletonSide::Right)); + + // Generate spine for main body + auto sortLimbIndiciesInSpineOrder = [=](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); + 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 + }; + enum Column + { + Left = 0, + Noside, + Right + }; + struct SpineNode + { + float coord; + QVector3D position; + std::set limbMarkIndicies; + }; + std::vector spineNodes; + 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();) { + 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])]; + const auto &coord = isMainBodyVerticalAligned ? mark.bonePosition.y() : + mark.bonePosition.z(); + if (coord < choosenCoord) { + choosenCoord = coord; + choosenColumn = side; + } + } + } + if (-1 == choosenColumn) { + 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; + 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])]; + 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; + ++sideIndicies[side]; + } + } + } + if (countOfLimbs <= 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(); + spineNode.coord = choosenCoord; + spineNode.limbMarkIndicies = limbMarkIndicies; + spineNode.position = sumOfLimbPositions / countOfLimbs; + } + + if (spineNodes.empty()) { + qDebug() << "Couldn't find limbs to create a spine"; + return false; + } + + std::map boneIndexMap; + + m_resultBones.push_back(RiggerBone()); + RiggerBone &bodyBone = m_resultBones.back(); + bodyBone.index = m_resultBones.size() - 1; + bodyBone.name = "Body"; + bodyBone.headPosition = QVector3D(0, 0, 0); + 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)}; + int spineGenerateOrder = 1; + 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(); + 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; + // 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; + tailPosition = spineNodes[spineNodeIndex + 1].position; + } else { + spineBoneVertices = remainingSpineVerticies; + if (isMainBodyVerticalAligned) { + tailPosition = findMaxY(spineBoneVertices); + } else { + tailPosition = findMaxZ(spineBoneVertices); + } + } + + QVector3D spineBoneHeadPosition = averagePosition(spineBoneVertices); + if (isMainBodyVerticalAligned) { + spineBoneHeadPosition.setY(spineNode.coord); + } else { + spineBoneHeadPosition.setZ(spineNode.coord); + } + + m_resultBones.push_back(RiggerBone()); + RiggerBone &spineBone = m_resultBones.back(); + spineBone.index = m_resultBones.size() - 1; + spineBone.name = "Spine" + QString::number(spineGenerateOrder); + 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; + + qDebug() << spineBone.name << "head:" << spineBone.headPosition << "tail:" << spineBone.tailPosition; + + if (1 == spineGenerateOrder) { + m_resultBones[boneIndexMap["Body"]].tailPosition = spineBone.headPosition; + m_resultBones[boneIndexMap["Body"]].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); + } + + int limbGenerateOrder = 1; + for (const auto &limbMarkIndex: spineNode.limbMarkIndicies) { + const auto &limbMark = m_marks[limbMarkIndex]; + + 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.headPosition = spineBoneHeadPosition; + boneIndexMap[ribBone.name] = ribBone.index; + if (1 == spineGenerateOrder) { + m_resultBones[boneIndexMap["Body"]].children.push_back(ribBone.index); + } else { + m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder)]].children.push_back(ribBone.index); + } + + std::vector jointMarkIndicies; + collectJointsForLimb(limbMarkIndex, jointMarkIndicies); + + //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; + }; + 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); + } else { + QString parentLimbBoneName = namingLimb(spineGenerateOrder, side, limbGenerateOrder, 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) { + const auto jointMark = m_marks[jointMarkIndex]; + int buttonColumn = boneColumn(); + 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; + 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); + } 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; + } + + boneIndexMap[limbBone.name] = limbBone.index; + addToParentBone(limbBone.headPosition, jointMark.boneSide, limbBone.index); + + lastPosition = jointMark.bonePosition; + + ++jointGenerateOrder; + } + + ++limbGenerateOrder; + } + + ++spineGenerateOrder; + } + + normalizeButtonColumns(); + + // Finalize weights + for (auto &weights: m_resultWeights) { + weights.second.finalizeWeights(); + } + + return true; +} + +void GenericRigger::normalizeButtonColumns() +{ + int minColumn = std::numeric_limits::max(); + int 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 = std::max(std::abs(minColumn), std::abs(maxColumn)); + for (auto &bone: m_resultBones) { + if (!bone.hasButton) + continue; + bone.button.second += columnNumOfOneSide; + } +} + +QString GenericRigger::namingLimb(int spineOrder, SkeletonSide side, int limbOrder, int jointOrder) +{ + return SkeletonSideToDispName(side) + "Limb" + QString::number(spineOrder) + "x" + QString::number(jointOrder); +} diff --git a/src/genericrigger.h b/src/genericrigger.h new file mode 100644 index 00000000..02c0e4d0 --- /dev/null +++ b/src/genericrigger.h @@ -0,0 +1,26 @@ +#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/meshgenerator.cpp b/src/meshgenerator.cpp index 9bd45ed0..c0b88418 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -278,6 +278,7 @@ void *MeshGenerator::combinePartMesh(QString partId) cacheBmeshNodes.clear(); cacheBmeshVertices.clear(); cacheBmeshQuads.clear(); + std::map> bmeshNodeIdToDataMap; for (const auto &nodeId: m_partNodeIds[partId]) { auto findNode = m_snapshot->nodes.find(nodeId); if (findNode == m_snapshot->nodes.end()) { @@ -305,13 +306,15 @@ void *MeshGenerator::combinePartMesh(QString partId) bmeshNode.color = partColor; bmeshNode.materialId = materialId; bmeshNode.boneMark = boneMark; - //if (SkeletonBoneMark::None != boneMark) - // bmeshNode.color = BoneMarkToColor(boneMark); + bmeshNode.mirroredByPartId = mirroredPartId; + bmeshNodeIdToDataMap[bmeshNodeId].push_back(cacheBmeshNodes.size()); cacheBmeshNodes.push_back(bmeshNode); if (xMirrored) { bmeshNode.partId = mirroredPartId; bmeshNode.mirrorFromPartId = QUuid(partId); + bmeshNode.mirroredByPartId = QUuid(); bmeshNode.origin.setX(-x); + bmeshNodeIdToDataMap[bmeshNodeId].push_back(cacheBmeshNodes.size()); cacheBmeshNodes.push_back(bmeshNode); } } @@ -346,6 +349,12 @@ void *MeshGenerator::combinePartMesh(QString partId) void *resultMesh = nullptr; if (!bmeshToNodeIdMap.empty()) { meshId = meshlite_bmesh_generate_mesh(m_meshliteContext, bmeshId); + for (const auto &item: bmeshNodeIdToDataMap) { + float baseNormal[3] = {0, 0, 0}; + meshlite_bmesh_get_node_base_norm(m_meshliteContext, bmeshId, item.first, baseNormal); + for (auto &subItem: item.second) + cacheBmeshNodes[subItem].baseNormal = QVector3D(baseNormal[0], baseNormal[1], baseNormal[2]); + } loadVertexSources(m_meshliteContext, meshId, partIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices, cacheBmeshQuads); if (wrapped) resultMesh = convertToCombinableConvexHullMesh(m_meshliteContext, meshId); diff --git a/src/meshsplitter.cpp b/src/meshsplitter.cpp index 6aaabd49..d4514426 100644 --- a/src/meshsplitter.cpp +++ b/src/meshsplitter.cpp @@ -4,9 +4,10 @@ #include "meshsplitter.h" bool MeshSplitter::split(const std::set &input, - const std::set &splitter, + std::set &splitter, std::set &firstGroup, - std::set &secondGroup) + std::set &secondGroup, + bool expandSplitter) { firstGroup.clear(); secondGroup.clear(); @@ -20,19 +21,35 @@ bool MeshSplitter::split(const std::set &input, } } - /* - size_t noClosingEdges = 0; - for (const auto &triangle: input) { - for (int i = 0; i < 3; i++) { - int next = (i + 1) % 3; - if (edgeToTriangleMap.find(std::make_pair(triangle.indicies[next], triangle.indicies[i])) == edgeToTriangleMap.end()) { - qDebug() << "Edge is not closing:" << triangle.indicies[next] << triangle.indicies[i]; - noClosingEdges++; + // Expand the splitter if needed + if (expandSplitter) { + std::vector expandedTriangles; + for (const auto &triangle: splitter) { + for (int i = 0; i < 3; i++) { + int next = (i + 1) % 3; + auto oppositeEdge = std::make_pair(triangle.indicies[next], triangle.indicies[i]); + auto oppositeTriangle = edgeToTriangleMap.find(oppositeEdge); + if (oppositeTriangle == edgeToTriangleMap.end()) { + qDebug() << "Find opposite edge failed:" << oppositeEdge.first << oppositeEdge.second; + continue; + } + if (splitter.find(oppositeTriangle->second) == splitter.end()) { + expandedTriangles.push_back(oppositeTriangle->second); + } } } + size_t addTriangles = 0; + for (const auto &triangle: expandedTriangles) { + auto insertResult = splitter.insert(triangle); + if (insertResult.second) + ++addTriangles; + } + if (0 == addTriangles) { + qDebug() << "Expanded without new triangles added"; + } else { + qDebug() << "Expanded with new triangles added:" << addTriangles; + } } - qDebug() << "noClosingEdges:" << noClosingEdges; - */ // Find one triangle wich is direct neighbor of one splitter MeshSplitterTriangle startTriangle; diff --git a/src/meshsplitter.h b/src/meshsplitter.h index 900bae56..81a2791d 100644 --- a/src/meshsplitter.h +++ b/src/meshsplitter.h @@ -18,9 +18,10 @@ class MeshSplitter { public: static bool split(const std::set &input, - const std::set &splitter, + std::set &splitter, std::set &firstGroup, - std::set &secondGroup); + std::set &secondGroup, + bool expandSplitter=false); }; #endif diff --git a/src/motioneditwidget.cpp b/src/motioneditwidget.cpp index 1c2d47e9..274c47ab 100644 --- a/src/motioneditwidget.cpp +++ b/src/motioneditwidget.cpp @@ -247,7 +247,7 @@ void MotionEditWidget::generatePreviews() return; } - m_previewsGenerator = new MotionsGenerator(rigBones, rigWeights, + m_previewsGenerator = new MotionsGenerator(m_document->rigType, rigBones, rigWeights, m_document->currentRiggedOutcome()); for (const auto &pose: m_document->poseMap) m_previewsGenerator->addPoseToLibrary(pose.first, pose.second.parameters); diff --git a/src/motionsgenerator.cpp b/src/motionsgenerator.cpp index ad56553d..04d1ca57 100644 --- a/src/motionsgenerator.cpp +++ b/src/motionsgenerator.cpp @@ -2,12 +2,14 @@ #include #include #include "motionsgenerator.h" -#include "tetrapodposer.h" #include "posemeshcreator.h" +#include "poserconstruct.h" -MotionsGenerator::MotionsGenerator(const std::vector *rigBones, +MotionsGenerator::MotionsGenerator(RigType rigType, + const std::vector *rigBones, const std::map *rigWeights, const Outcome &outcome) : + m_rigType(rigType), m_rigBones(*rigBones), m_rigWeights(*rigWeights), m_outcome(outcome) @@ -243,7 +245,9 @@ std::vector> MotionsGenerator::takeResultJointNo void MotionsGenerator::generate() { - m_poser = new TetrapodPoser(m_rigBones); + m_poser = newPoser(m_rigType, m_rigBones); + if (nullptr == m_poser) + return; for (const auto &motionId: m_requiredMotionIds) { std::set visited; diff --git a/src/motionsgenerator.h b/src/motionsgenerator.h index ce9f37b8..e7c38988 100644 --- a/src/motionsgenerator.h +++ b/src/motionsgenerator.h @@ -8,13 +8,14 @@ #include "rigger.h" #include "jointnodetree.h" #include "document.h" -#include "tetrapodposer.h" +#include "poser.h" class MotionsGenerator : public QObject { Q_OBJECT public: - MotionsGenerator(const std::vector *rigBones, + MotionsGenerator(RigType rigType, + const std::vector *rigBones, const std::map *rigWeights, const Outcome &outcome); ~MotionsGenerator(); @@ -42,6 +43,7 @@ private: void generatePreviewsForOutcomes(const std::vector> &outcomes, std::vector> &previews); float calculateMotionDuration(const QUuid &motionId, std::set &visited); + RigType m_rigType = RigType::None; std::vector m_rigBones; std::map m_rigWeights; Outcome m_outcome; @@ -52,7 +54,7 @@ private: std::map>> m_resultPreviewMeshs; std::map>> m_resultJointNodeTrees; std::map m_poseJointNodeTreeMap; - TetrapodPoser *m_poser = nullptr; + Poser *m_poser = nullptr; int m_fps = 30; }; diff --git a/src/outcome.h b/src/outcome.h index 9cd6e4a2..87e44483 100644 --- a/src/outcome.h +++ b/src/outcome.h @@ -19,7 +19,9 @@ struct OutcomeNode QColor color; QUuid materialId; QUuid mirrorFromPartId; + QUuid mirroredByPartId; BoneMark boneMark; + QVector3D baseNormal; }; class Outcome diff --git a/src/poseeditwidget.cpp b/src/poseeditwidget.cpp index 0b390107..e3241bec 100644 --- a/src/poseeditwidget.cpp +++ b/src/poseeditwidget.cpp @@ -10,6 +10,7 @@ #include "poseeditwidget.h" #include "floatnumberwidget.h" #include "version.h" +#include "poserconstruct.h" PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : QDialog(parent), @@ -28,78 +29,14 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : m_previewWidget->updateMesh(m_posePreviewManager->takeResultPreviewMesh()); }); - std::map> buttons; - buttons["Head"] = std::make_tuple(new QPushButton(tr("Head")), PopupWidgetType::PitchYawRoll); - buttons["Neck"] = std::make_tuple(new QPushButton(tr("Neck")), PopupWidgetType::PitchYawRoll); - buttons["RightUpperArm"] = std::make_tuple(new QPushButton(tr("UpperArm")), PopupWidgetType::PitchYawRoll); - buttons["RightLowerArm"] = std::make_tuple(new QPushButton(tr("LowerArm")), PopupWidgetType::Intersection); - buttons["RightHand"] = std::make_tuple(new QPushButton(tr("Hand")), PopupWidgetType::Intersection); - buttons["LeftUpperArm"] = std::make_tuple(new QPushButton(tr("UpperArm")), PopupWidgetType::PitchYawRoll); - buttons["LeftLowerArm"] = std::make_tuple(new QPushButton(tr("LowerArm")), PopupWidgetType::Intersection); - buttons["LeftHand"] = std::make_tuple(new QPushButton(tr("Hand")), PopupWidgetType::Intersection); - buttons["Chest"] = std::make_tuple(new QPushButton(tr("Chest")), PopupWidgetType::PitchYawRoll); - buttons["Spine"] = std::make_tuple(new QPushButton(tr("Spine")), PopupWidgetType::PitchYawRoll); - buttons["RightUpperLeg"] = std::make_tuple(new QPushButton(tr("UpperLeg")), PopupWidgetType::PitchYawRoll); - buttons["RightLowerLeg"] = std::make_tuple(new QPushButton(tr("LowerLeg")), PopupWidgetType::Intersection); - buttons["RightFoot"] = std::make_tuple(new QPushButton(tr("Foot")), PopupWidgetType::Intersection); - buttons["LeftUpperLeg"] = std::make_tuple(new QPushButton(tr("UpperLeg")), PopupWidgetType::PitchYawRoll); - buttons["LeftLowerLeg"] = std::make_tuple(new QPushButton(tr("LowerLeg")), PopupWidgetType::Intersection); - buttons["LeftFoot"] = std::make_tuple(new QPushButton(tr("Foot")), PopupWidgetType::Intersection); - - QGridLayout *marksContainerLayout = new QGridLayout; - marksContainerLayout->setContentsMargins(0, 0, 0, 0); - marksContainerLayout->setSpacing(0); - marksContainerLayout->addWidget(std::get<0>(buttons["Head"]), 0, 1); - marksContainerLayout->addWidget(std::get<0>(buttons["Neck"]), 1, 1); - marksContainerLayout->addWidget(std::get<0>(buttons["RightUpperArm"]), 1, 0); - marksContainerLayout->addWidget(std::get<0>(buttons["RightLowerArm"]), 2, 0); - marksContainerLayout->addWidget(std::get<0>(buttons["RightHand"]), 3, 0); - marksContainerLayout->addWidget(std::get<0>(buttons["LeftUpperArm"]), 1, 2); - marksContainerLayout->addWidget(std::get<0>(buttons["LeftLowerArm"]), 2, 2); - marksContainerLayout->addWidget(std::get<0>(buttons["LeftHand"]), 3, 2); - marksContainerLayout->addWidget(std::get<0>(buttons["Chest"]), 2, 1); - marksContainerLayout->addWidget(std::get<0>(buttons["Spine"]), 3, 1); - - QGridLayout *lowerMarksContainerLayout = new QGridLayout; - lowerMarksContainerLayout->setContentsMargins(0, 0, 0, 0); - lowerMarksContainerLayout->setSpacing(0); - lowerMarksContainerLayout->addWidget(std::get<0>(buttons["RightUpperLeg"]), 0, 1); - lowerMarksContainerLayout->addWidget(std::get<0>(buttons["RightLowerLeg"]), 1, 1); - lowerMarksContainerLayout->addWidget(std::get<0>(buttons["RightFoot"]), 2, 0); - lowerMarksContainerLayout->addWidget(std::get<0>(buttons["LeftUpperLeg"]), 0, 2); - lowerMarksContainerLayout->addWidget(std::get<0>(buttons["LeftLowerLeg"]), 1, 2); - lowerMarksContainerLayout->addWidget(std::get<0>(buttons["LeftFoot"]), 2, 3); - - QFont buttonFont; - buttonFont.setWeight(QFont::Light); - buttonFont.setPixelSize(9); - buttonFont.setBold(false); - for (const auto &item: buttons) { - QString boneName = item.first; - QPushButton *buttonWidget = std::get<0>(item.second); - PopupWidgetType widgetType = std::get<1>(item.second); - buttonWidget->setFont(buttonFont); - buttonWidget->setMaximumWidth(55); - connect(buttonWidget, &QPushButton::clicked, [this, boneName, widgetType]() { - emit showPopupAngleDialog(boneName, widgetType, mapFromGlobal(QCursor::pos())); - }); - } - m_previewWidget = new ModelWidget(this); m_previewWidget->setMinimumSize(128, 128); m_previewWidget->resize(384, 384); m_previewWidget->move(-64, -64+22); - QVBoxLayout *markButtonsLayout = new QVBoxLayout; - markButtonsLayout->addStretch(); - markButtonsLayout->addLayout(marksContainerLayout); - markButtonsLayout->addLayout(lowerMarksContainerLayout); - markButtonsLayout->addStretch(); - QHBoxLayout *paramtersLayout = new QHBoxLayout; - paramtersLayout->setContentsMargins(256, 0, 0, 0); + paramtersLayout->setContentsMargins(0, 480, 0, 0); paramtersLayout->addStretch(); - paramtersLayout->addLayout(markButtonsLayout); paramtersLayout->addSpacing(20); m_nameEdit = new QLineEdit; @@ -135,10 +72,56 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : connect(this, &PoseEditWidget::renamePose, m_document, &Document::renamePose); connect(this, &PoseEditWidget::setPoseParameters, m_document, &Document::setPoseParameters); + updateButtons(); updatePreview(); updateTitle(); } +void PoseEditWidget::updateButtons() +{ + delete m_buttonsContainer; + m_buttonsContainer = new QWidget(this); + m_buttonsContainer->resize(600, 500); + m_buttonsContainer->move(256, 0); + m_buttonsContainer->show(); + + QGridLayout *marksContainerLayout = new QGridLayout; + marksContainerLayout->setContentsMargins(0, 0, 0, 0); + marksContainerLayout->setSpacing(2); + + QFont buttonFont; + buttonFont.setWeight(QFont::Light); + buttonFont.setPixelSize(7); + buttonFont.setBold(false); + + std::map> buttons; + const std::vector *rigBones = m_document->resultRigBones(); + if (nullptr != rigBones && !rigBones->empty()) { + for (const auto &bone: *rigBones) { + if (!bone.hasButton) + continue; + QPushButton *buttonWidget = new QPushButton(bone.name); + PopupWidgetType widgetType = bone.buttonParameterType; + buttonWidget->setFont(buttonFont); + buttonWidget->setMaximumWidth(100); + QString boneName = bone.name; + connect(buttonWidget, &QPushButton::clicked, [this, boneName, widgetType]() { + emit showPopupAngleDialog(boneName, widgetType, mapFromGlobal(QCursor::pos())); + }); + marksContainerLayout->addWidget(buttonWidget, bone.button.first, bone.button.second); + } + } + + marksContainerLayout->setSizeConstraint(QLayout::SizeConstraint::SetMinimumSize); + + QVBoxLayout *mainLayouer = new QVBoxLayout; + mainLayouer->addStretch(); + mainLayouer->addLayout(marksContainerLayout); + mainLayouer->addStretch(); + + m_buttonsContainer->setLayout(mainLayouer); +} + void PoseEditWidget::reject() { close(); @@ -171,7 +154,7 @@ void PoseEditWidget::closeEvent(QCloseEvent *event) QSize PoseEditWidget::sizeHint() const { - return QSize(0, 350); + return QSize(1024, 768); } PoseEditWidget::~PoseEditWidget() @@ -198,7 +181,10 @@ void PoseEditWidget::updatePreview() return; } - TetrapodPoser *poser = new TetrapodPoser(*rigBones); + Poser *poser = newPoser(m_document->rigType, *rigBones); + if (nullptr == poser) + return; + poser->parameters() = m_parameters; poser->commit(); m_posePreviewManager->postUpdate(*poser, m_document->currentRiggedOutcome(), *rigWeights); @@ -290,7 +276,7 @@ void PoseEditWidget::showPopupAngleDialog(QString boneName, PopupWidgetType popu rollLayout->addWidget(rollEraser); rollLayout->addWidget(rollWidget); layout->addLayout(rollLayout); - } else { + } else if (PopupWidgetType::Intersection == popupWidgetType) { FloatNumberWidget *intersectionWidget = new FloatNumberWidget; intersectionWidget->setItemName(tr("Intersection")); intersectionWidget->setRange(-180, 180); diff --git a/src/poseeditwidget.h b/src/poseeditwidget.h index 069d3556..6e022f51 100644 --- a/src/poseeditwidget.h +++ b/src/poseeditwidget.h @@ -5,15 +5,11 @@ #include #include #include "posepreviewmanager.h" -#include "tetrapodposer.h" #include "document.h" #include "modelwidget.h" +#include "rigger.h" -enum class PopupWidgetType -{ - PitchYawRoll, - Intersection -}; +typedef RiggerButtonParameterType PopupWidgetType; class PoseEditWidget : public QDialog { @@ -28,6 +24,7 @@ public: PoseEditWidget(const Document *document, QWidget *parent=nullptr); ~PoseEditWidget(); public slots: + void updateButtons(); void updatePreview(); void showPopupAngleDialog(QString boneName, PopupWidgetType popupWidgetType, QPoint pos); void setEditPoseId(QUuid poseId); @@ -51,6 +48,7 @@ private: QUuid m_poseId; bool m_unsaved = false; QLineEdit *m_nameEdit = nullptr; + QWidget *m_buttonsContainer = nullptr; }; #endif diff --git a/src/posepreviewsgenerator.cpp b/src/posepreviewsgenerator.cpp index b1577ba9..22a5b165 100644 --- a/src/posepreviewsgenerator.cpp +++ b/src/posepreviewsgenerator.cpp @@ -1,12 +1,14 @@ #include #include #include "posepreviewsgenerator.h" -#include "tetrapodposer.h" #include "posemeshcreator.h" +#include "poserconstruct.h" -PosePreviewsGenerator::PosePreviewsGenerator(const std::vector *rigBones, +PosePreviewsGenerator::PosePreviewsGenerator(RigType rigType, + const std::vector *rigBones, const std::map *rigWeights, const Outcome &outcome) : + m_rigType(rigType), m_rigBones(*rigBones), m_rigWeights(*rigWeights), m_outcome(new Outcome(outcome)) @@ -43,7 +45,7 @@ void PosePreviewsGenerator::process() QElapsedTimer countTimeConsumed; countTimeConsumed.start(); - TetrapodPoser *poser = new TetrapodPoser(m_rigBones); + Poser *poser = newPoser(m_rigType, m_rigBones); for (const auto &pose: m_poses) { poser->parameters() = pose.second; poser->commit(); diff --git a/src/posepreviewsgenerator.h b/src/posepreviewsgenerator.h index 45563722..b011f304 100644 --- a/src/posepreviewsgenerator.h +++ b/src/posepreviewsgenerator.h @@ -7,12 +7,14 @@ #include "meshloader.h" #include "rigger.h" #include "outcome.h" +#include "rigtype.h" class PosePreviewsGenerator : public QObject { Q_OBJECT public: - PosePreviewsGenerator(const std::vector *rigBones, + PosePreviewsGenerator(RigType rigType, + const std::vector *rigBones, const std::map *rigWeights, const Outcome &outcome); ~PosePreviewsGenerator(); @@ -24,6 +26,7 @@ signals: public slots: void process(); private: + RigType m_rigType = RigType::None; std::vector m_rigBones; std::map m_rigWeights; Outcome *m_outcome = nullptr; diff --git a/src/poserconstruct.cpp b/src/poserconstruct.cpp new file mode 100644 index 00000000..59f55ac8 --- /dev/null +++ b/src/poserconstruct.cpp @@ -0,0 +1,12 @@ +#include "poserconstruct.h" +#include "tetrapodposer.h" +#include "genericposer.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); + return nullptr; +} \ No newline at end of file diff --git a/src/poserconstruct.h b/src/poserconstruct.h new file mode 100644 index 00000000..d8671b34 --- /dev/null +++ b/src/poserconstruct.h @@ -0,0 +1,9 @@ +#ifndef DUST3D_POSER_CONSTRUCT_H +#define DUST3D_POSER_CONSTRUCT_H +#include "rigtype.h" +#include "poser.h" +#include "rigger.h" + +Poser *newPoser(RigType rigType, const std::vector &bones); + +#endif diff --git a/src/riggenerator.cpp b/src/riggenerator.cpp index 028fb8b7..e675e367 100644 --- a/src/riggenerator.cpp +++ b/src/riggenerator.cpp @@ -1,10 +1,12 @@ #include #include #include +#include #include "riggenerator.h" -#include "rigger.h" +#include "riggerconstruct.h" -RigGenerator::RigGenerator(const Outcome &outcome) : +RigGenerator::RigGenerator(RigType rigType, const Outcome &outcome) : + m_rigType(rigType), m_outcome(new Outcome(outcome)) { } @@ -71,59 +73,113 @@ void RigGenerator::generate() const auto &triangleSourceNodes = *m_outcome->triangleSourceNodes(); const std::vector> *triangleVertexNormals = m_outcome->triangleVertexNormals(); - - std::map, const OutcomeNode *> nodeMap; - for (const auto &item: m_outcome->nodes) { - nodeMap.insert({{item.partId, item.nodeId}, &item}); - } + const std::vector *triangleTangents = m_outcome->triangleTangents(); for (const auto &vertex: m_outcome->vertices) { inputVerticesPositions.push_back(vertex); } - std::map, std::tuple>> marksMap; + + std::map, std::tuple, float, QVector3D>> markedNodes; + for (const auto &bmeshNode: m_outcome->nodes) { + if (bmeshNode.boneMark == BoneMark::None) + continue; + SkeletonSide boneSide = SkeletonSide::None; + if (BoneMarkHasSide(bmeshNode.boneMark) && + std::abs(bmeshNode.origin.x()) > 0.01 ) { + boneSide = bmeshNode.origin.x() > 0 ? SkeletonSide::Left : SkeletonSide::Right; + } + //qDebug() << "Add bone mark:" << BoneMarkToString(bmeshNode.boneMark) << "side:" << SkeletonSideToDispName(boneSide); + markedNodes[std::make_pair(bmeshNode.partId, bmeshNode.nodeId)] = std::make_tuple(bmeshNode.boneMark, boneSide, bmeshNode.origin, std::set(), bmeshNode.radius, bmeshNode.baseNormal); + } + for (size_t triangleIndex = 0; triangleIndex < m_outcome->triangles.size(); triangleIndex++) { const auto &sourceTriangle = m_outcome->triangles[triangleIndex]; MeshSplitterTriangle newTriangle; for (int i = 0; i < 3; i++) newTriangle.indicies[i] = sourceTriangle[i]; - auto findBmeshNodeResult = nodeMap.find(triangleSourceNodes[triangleIndex]); - if (findBmeshNodeResult != nodeMap.end()) { - const auto &bmeshNode = *findBmeshNodeResult->second; - if (bmeshNode.boneMark != BoneMark::None) { - SkeletonSide boneSide = SkeletonSide::None; - if (BoneMarkHasSide(bmeshNode.boneMark)) { - boneSide = bmeshNode.origin.x() > 0 ? SkeletonSide::Left : SkeletonSide::Right; - } - auto &marks = marksMap[std::make_pair(bmeshNode.boneMark, boneSide)]; - std::get<0>(marks) += bmeshNode.origin; - std::get<1>(marks) += 1; - std::get<2>(marks).insert(newTriangle); - } + auto findMarkedNodeResult = markedNodes.find(triangleSourceNodes[triangleIndex]); + if (findMarkedNodeResult != markedNodes.end()) { + auto &markedNode = findMarkedNodeResult->second; + std::get<3>(markedNode).insert(newTriangle); } inputTriangles.insert(newTriangle); } - m_autoRigger = new Rigger(inputVerticesPositions, inputTriangles); - for (const auto &marks: marksMap) { - m_autoRigger->addMarkGroup(marks.first.first, marks.first.second, - std::get<0>(marks.second) / std::get<1>(marks.second), - std::get<2>(marks.second)); + + std::vector, float, QVector3D>> markedNodesList; + for (const auto &markedNode: markedNodes) { + markedNodesList.push_back(markedNode.second); + } + + // Combine the overlapped marks + std::vector, float, QVector3D>> combinedMarkedNodesList; + std::set processedNodes; + for (size_t i = 0; i < markedNodesList.size(); ++i) { + if (processedNodes.find(i) != processedNodes.end()) + continue; + const auto &first = markedNodesList[i]; + std::tuple, float, QVector3D> newNodes; + size_t combinedNum = 1; + newNodes = first; + for (size_t j = i + 1; j < markedNodesList.size(); ++j) { + const auto &second = markedNodesList[j]; + if (std::get<0>(first) == std::get<0>(second) && + std::get<1>(first) == std::get<1>(second)) { + if ((std::get<2>(first) - std::get<2>(second)).lengthSquared() < + std::pow((std::get<4>(first) + std::get<4>(second)), 2)) { + processedNodes.insert(j); + + std::get<2>(newNodes) += std::get<2>(second); + for (const auto &triangle: std::get<3>(second)) + std::get<3>(newNodes).insert(triangle); + std::get<4>(newNodes) += std::get<4>(second); + std::get<5>(newNodes) += std::get<5>(second); + ++combinedNum; + } + } + } + if (combinedNum > 1) { + std::get<2>(newNodes) /= combinedNum; + std::get<4>(newNodes) /= combinedNum; + std::get<5>(newNodes).normalize(); + + qDebug() << "Combined" << combinedNum << "on mark:" << BoneMarkToString(std::get<0>(newNodes)) << "side:" << SkeletonSideToDispName(std::get<1>(newNodes)); + } + combinedMarkedNodesList.push_back(newNodes); + } + + m_autoRigger = newRigger(m_rigType, inputVerticesPositions, inputTriangles); + if (nullptr == m_autoRigger) { + qDebug() << "Unsupported rig type:" << RigTypeToString(m_rigType); + } else { + for (const auto &markedNode: combinedMarkedNodesList) { + const auto &triangles = std::get<3>(markedNode); + if (triangles.empty()) + continue; + m_autoRigger->addMarkGroup(std::get<0>(markedNode), std::get<1>(markedNode), + std::get<2>(markedNode), + std::get<4>(markedNode), + std::get<5>(markedNode), + std::get<3>(markedNode)); + } + m_isSucceed = m_autoRigger->rig(); } - m_isSucceed = m_autoRigger->rig(); if (m_isSucceed) { qDebug() << "Rig succeed"; } else { qDebug() << "Rig failed"; - 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_missingMarkNames = m_autoRigger->missingMarkNames(); + m_errorMarkNames = m_autoRigger->errorMarkNames(); + for (const auto &message: m_autoRigger->messages()) { + qDebug() << "errorType:" << message.first << "Message:" << message.second; + } } } // Blend vertices colors according to bone weights - std::vector inputVerticesColors(m_outcome->vertices.size()); + std::vector inputVerticesColors(m_outcome->vertices.size(), Qt::black); if (m_isSucceed) { const auto &resultWeights = m_autoRigger->resultWeights(); const auto &resultBones = m_autoRigger->resultBones(); @@ -157,8 +213,12 @@ void RigGenerator::generate() Vertex *triangleVertices = new Vertex[m_outcome->triangles.size() * 3]; int triangleVerticesNum = 0; const QVector3D defaultUv = QVector3D(0, 0, 0); + const QVector3D defaultTangents = QVector3D(0, 0, 0); for (size_t triangleIndex = 0; triangleIndex < m_outcome->triangles.size(); triangleIndex++) { const auto &sourceTriangle = m_outcome->triangles[triangleIndex]; + const auto *sourceTangent = &defaultTangents; + if (nullptr != triangleTangents) + sourceTangent = &(*triangleTangents)[triangleIndex]; for (int i = 0; i < 3; i++) { Vertex ¤tVertex = triangleVertices[triangleVerticesNum++]; const auto &sourcePosition = inputVerticesPositions[sourceTriangle[i]]; @@ -177,6 +237,11 @@ void RigGenerator::generate() currentVertex.normX = sourceNormal->x(); currentVertex.normY = sourceNormal->y(); currentVertex.normZ = sourceNormal->z(); + currentVertex.metalness = MeshLoader::m_defaultMetalness; + currentVertex.roughness = MeshLoader::m_defaultRoughness; + currentVertex.tangentX = sourceTangent->x(); + currentVertex.tangentY = sourceTangent->y(); + currentVertex.tangentZ = sourceTangent->z(); } } m_resultMesh = new MeshLoader(triangleVertices, triangleVerticesNum); diff --git a/src/riggenerator.h b/src/riggenerator.h index d370e08d..be542d70 100644 --- a/src/riggenerator.h +++ b/src/riggenerator.h @@ -6,12 +6,13 @@ #include "outcome.h" #include "meshloader.h" #include "rigger.h" +#include "rigtype.h" class RigGenerator : public QObject { Q_OBJECT public: - RigGenerator(const Outcome &outcome); + RigGenerator(RigType rigType, const Outcome &outcome); ~RigGenerator(); MeshLoader *takeResultMesh(); std::vector *takeResultBones(); @@ -26,6 +27,7 @@ signals: public slots: void process(); private: + RigType m_rigType = RigType::None; Outcome *m_outcome = nullptr; MeshLoader *m_resultMesh = nullptr; Rigger *m_autoRigger = nullptr; diff --git a/src/rigger.cpp b/src/rigger.cpp index 9fc02c22..00b8bdb3 100644 --- a/src/rigger.cpp +++ b/src/rigger.cpp @@ -5,6 +5,8 @@ #include "skeletonside.h" #include "rigger.h" +size_t Rigger::m_maxCutOffSplitterExpandRound = 3; + Rigger::Rigger(const std::vector &verticesPositions, const std::set &inputTriangles) : m_verticesPositions(verticesPositions), @@ -12,11 +14,9 @@ Rigger::Rigger(const std::vector &verticesPositions, { } -bool Rigger::isCutOffSplitter(BoneMark boneMark) +BoneMark Rigger::translateBoneMark(BoneMark boneMark) { - return boneMark == BoneMark::Neck || - boneMark == BoneMark::Shoulder || - boneMark == BoneMark::Hip; + return boneMark; } bool Rigger::calculateBodyTriangles(std::set &bodyTriangles) @@ -42,23 +42,25 @@ bool Rigger::calculateBodyTriangles(std::set &bodyTriangle return true; } -bool Rigger::addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bonePosition, +bool Rigger::addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bonePosition, float nodeRadius, QVector3D baseNormal, const std::set &markTriangles) { m_marks.push_back(RiggerMark()); RiggerMark &mark = m_marks.back(); - mark.boneMark = boneMark; + mark.boneMark = translateBoneMark(boneMark); mark.boneSide = boneSide; mark.bonePosition = bonePosition; + mark.nodeRadius = nodeRadius; + mark.baseNormal = baseNormal; mark.markTriangles = markTriangles; if (isCutOffSplitter(mark.boneMark)) { - if (!mark.split(m_inputTriangles)) { + 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(mark.boneMark)))); + tr("Mark \"%1 %2\" couldn't cut off the mesh").arg(SkeletonSideToDispName(mark.boneSide)).arg(BoneMarkToString(boneMark)))); return false; } } @@ -82,45 +84,6 @@ void Rigger::addTrianglesToVertices(const std::set &triang } } -bool Rigger::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; -} - void Rigger::resolveBoundingBox(const std::set &vertices, QVector3D &xMin, QVector3D &xMax, QVector3D &yMin, QVector3D &yMax, QVector3D &zMin, QVector3D &zMax) { bool leftFirstTime = true; @@ -203,6 +166,19 @@ QVector3D Rigger::findMaxZ(const std::set &vertices) return maxZ; } +QVector3D Rigger::averagePosition(const std::set &vertices) +{ + if (vertices.empty()) + return QVector3D(); + + QVector3D sum; + for (const auto &index: vertices) { + const auto &position = m_verticesPositions[index]; + sum += position; + } + return sum / vertices.size(); +} + void Rigger::splitVerticesByY(const std::set &vertices, float y, std::set &greaterEqualThanVertices, std::set &lessThanVertices) { for (const auto &index: vertices) { @@ -236,6 +212,19 @@ void Rigger::splitVerticesByZ(const std::set &vertices, float z, std::set &vertices, const QVector3D &pointOnPlane, const QVector3D &planeNormal, std::set &frontOrCoincidentVertices, std::set &backVertices) +{ + for (const auto &index: vertices) { + const auto &position = m_verticesPositions[index]; + auto line = position - pointOnPlane; + auto dot = QVector3D::dotProduct(line, planeNormal); + if (dot >= 0) + frontOrCoincidentVertices.insert(index); + else + backVertices.insert(index); + } +} + const std::vector &Rigger::resultBones() { return m_resultBones; @@ -256,582 +245,6 @@ void Rigger::addVerticesToWeights(const std::set &vertices, int boneIndex) } } -bool Rigger::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 minY = findMinY(leftAnkleMarkVertices); - splitVerticesByY(leftLegVerticesSinceKnee, minY.y(), leftLowerLegVertices, leftLegVerticesAfterAnkle); - } - - // 4.1.3 Collect vertices for left foot: - std::set leftFootVertices; - { - std::set leftLegVerticesBeforeAnkle; - splitVerticesByY(leftLegVerticesSinceKnee, m_marks[leftAnkleIndicies->second[0]].bonePosition.y(), 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 minY = findMinY(rightAnkleMarkVertices); - splitVerticesByY(rightLegVerticesSinceKnee, minY.y(), rightLowerLegVertices, rightLegVerticesAfterAnkle); - } - - // 4.2.3 Collect vertices for right foot: - std::set rightFootVertices; - { - std::set rightLegVerticesBeforeAnkle; - splitVerticesByY(rightLegVerticesSinceKnee, m_marks[rightAnkleIndicies->second[0]].bonePosition.y(), 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 = "Body"; - bodyBone.headPosition = QVector3D(0, 0, 0); - bodyBone.tailPosition = bonesOrigin; - 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["Body"]].tailPosition; - leftHipBone.tailPosition = leftUpperLegBoneStartPosition; - boneIndexMap[leftHipBone.name] = leftHipBone.index; - m_resultBones[boneIndexMap["Body"]].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); - 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); - 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); - 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["Body"]].tailPosition; - rightHipBone.tailPosition = rightUpperLegBoneStartPosition; - boneIndexMap[rightHipBone.name] = rightHipBone.index; - m_resultBones[boneIndexMap["Body"]].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); - 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); - 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); - 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["Body"]].tailPosition; - spineBone.tailPosition = chestBoneStartPosition; - spineBone.color = Qt::white; - boneIndexMap[spineBone.name] = spineBone.index; - m_resultBones[boneIndexMap["Body"]].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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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; -} - const std::vector &Rigger::missingMarkNames() { return m_missingMarkNames; diff --git a/src/rigger.h b/src/rigger.h index 1ba30b74..678def28 100644 --- a/src/rigger.h +++ b/src/rigger.h @@ -5,17 +5,27 @@ #include #include #include +#include #include "meshsplitter.h" #include "bonemark.h" #include "rigtype.h" #include "skeletonside.h" +enum class RiggerButtonParameterType +{ + None = 0, + PitchYawRoll, + Intersection +}; + class RiggerMark { public: BoneMark boneMark; SkeletonSide boneSide; QVector3D bonePosition; + float nodeRadius = 0; + QVector3D baseNormal; std::set markTriangles; const std::set &bigGroup() const { @@ -29,9 +39,17 @@ public: m_secondGroup : m_firstGroup; } - bool split(const std::set &input) + bool split(const std::set &input, int expandRound=0) { - return MeshSplitter::split(input, markTriangles, m_firstGroup, m_secondGroup); + int totalRound = 1 + expandRound; + for (int round = 0; round < totalRound; ++round) { + m_firstGroup.clear(); + m_secondGroup.clear(); + bool splitResult = MeshSplitter::split(input, markTriangles, m_firstGroup, m_secondGroup, round > 0); + if (splitResult) + return true; + } + return false; } private: std::set m_firstGroup; @@ -42,10 +60,14 @@ class RiggerBone { public: QString name; - int index; + int index = -1; QVector3D headPosition; QVector3D tailPosition; QColor color; + bool hasButton = false; + RiggerButtonParameterType buttonParameterType = RiggerButtonParameterType::None; + std::pair button = {0, 0}; + QVector3D baseNormal; std::vector children; }; @@ -56,6 +78,9 @@ public: float boneWeights[4] = {0, 0, 0, 0}; void addBone(int boneIndex, float distance) { + if (m_boneRawIndicies.find(boneIndex) != m_boneRawIndicies.end()) + return; + m_boneRawIndicies.insert(boneIndex); if (qFuzzyIsNull(distance)) distance = 0.0001; m_boneRawWeights.push_back(std::make_pair(boneIndex, 1.0 / distance)); @@ -82,6 +107,7 @@ public: } } private: + std::set m_boneRawIndicies; std::vector> m_boneRawWeights; }; @@ -90,19 +116,20 @@ class Rigger : public QObject public: Rigger(const std::vector &verticesPositions, const std::set &inputTriangles); - bool addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bonePosition, + bool addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bonePosition, float nodeRadius, QVector3D baseNormal, const std::set &markTriangles); const std::vector> &messages(); - bool rig(); const std::vector &resultBones(); const std::map &resultWeights(); const std::vector &missingMarkNames(); const std::vector &errorMarkNames(); -private: - bool validate(); + virtual bool rig() = 0; +protected: + virtual bool validate() = 0; + virtual bool isCutOffSplitter(BoneMark boneMark) = 0; + virtual BoneMark translateBoneMark(BoneMark boneMark); void addTrianglesToVertices(const std::set &triangles, std::set &vertices); bool calculateBodyTriangles(std::set &bodyTriangles); - bool isCutOffSplitter(BoneMark boneMark); void resolveBoundingBox(const std::set &vertices, QVector3D &xMin, QVector3D &xMax, QVector3D &yMin, QVector3D &yMax, QVector3D &zMin, QVector3D &zMax); QVector3D findMinX(const std::set &vertices); QVector3D findMaxX(const std::set &vertices); @@ -110,9 +137,11 @@ private: QVector3D findMaxY(const std::set &vertices); QVector3D findMinZ(const std::set &vertices); QVector3D findMaxZ(const std::set &vertices); + QVector3D averagePosition(const std::set &vertices); void splitVerticesByY(const std::set &vertices, float y, std::set &greaterEqualThanVertices, std::set &lessThanVertices); void splitVerticesByX(const std::set &vertices, float x, std::set &greaterEqualThanVertices, std::set &lessThanVertices); void splitVerticesByZ(const std::set &vertices, float z, std::set &greaterEqualThanVertices, std::set &lessThanVertices); + void splitVerticesByPlane(const std::set &vertices, const QVector3D &pointOnPlane, const QVector3D &planeNormal, std::set &frontOrCoincidentVertices, std::set &backVertices); void addVerticesToWeights(const std::set &vertices, int boneIndex); std::vector> m_messages; std::vector m_verticesPositions; @@ -123,6 +152,7 @@ private: std::map m_resultWeights; std::vector m_missingMarkNames; std::vector m_errorMarkNames; + static size_t m_maxCutOffSplitterExpandRound; }; #endif diff --git a/src/riggerconstruct.cpp b/src/riggerconstruct.cpp new file mode 100644 index 00000000..01f218b3 --- /dev/null +++ b/src/riggerconstruct.cpp @@ -0,0 +1,13 @@ +#include "riggerconstruct.h" +#include "tetrapodrigger.h" +#include "genericrigger.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); + return nullptr; +} \ No newline at end of file diff --git a/src/riggerconstruct.h b/src/riggerconstruct.h new file mode 100644 index 00000000..39f53a2c --- /dev/null +++ b/src/riggerconstruct.h @@ -0,0 +1,10 @@ +#ifndef DUST3D_RIGGER_CONSTRUCT_H +#define DUST3D_RIGGER_CONSTRUCT_H +#include "rigtype.h" +#include "rigger.h" +#include "poser.h" + +Rigger *newRigger(RigType rigType, const std::vector &verticesPositions, + const std::set &inputTriangles); + +#endif diff --git a/src/rigtype.h b/src/rigtype.h index 619a5668..693e44d9 100644 --- a/src/rigtype.h +++ b/src/rigtype.h @@ -5,6 +5,7 @@ enum class RigType { None = 0, + Generic, Tetrapod, Count }; @@ -13,6 +14,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; \ return RigType::None; \ @@ -22,6 +25,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::None: \ @@ -35,6 +40,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::None: \ diff --git a/src/skeletongraphicswidget.h b/src/skeletongraphicswidget.h index 9daf4ada..21619421 100644 --- a/src/skeletongraphicswidget.h +++ b/src/skeletongraphicswidget.h @@ -147,22 +147,27 @@ public: } QColor penColor = color; - penColor.setAlphaF(m_checked ? Theme::checkedAlpha : Theme::normalAlpha); + penColor.setAlphaF((m_checked || m_hovered) ? Theme::checkedAlpha : Theme::normalAlpha); QPen pen(penColor); pen.setWidth(0); setPen(pen); + QColor brushColor; + Qt::BrushStyle style; if (m_markColor == Qt::transparent) { - QColor brushColor = color; + brushColor = color; brushColor.setAlphaF((m_checked || m_hovered) ? Theme::fillAlpha : 0); - QBrush brush(brushColor); - setBrush(brush); + style = Qt::SolidPattern; } else { - QColor brushColor = m_markColor; + brushColor = m_markColor; brushColor.setAlphaF((m_checked || m_hovered) ? Theme::fillAlpha * 4 : Theme::fillAlpha * 1.5); - QBrush brush(brushColor); - setBrush(brush); + style = Qt::Dense4Pattern; } + if (m_checked) + brushColor.setAlphaF(brushColor.alphaF() * 1.2); + QBrush brush(brushColor); + brush.setStyle(style); + setBrush(brush); } void setOrigin(QPointF point) { diff --git a/src/tetrapodposer.cpp b/src/tetrapodposer.cpp index 3cd07138..df5369e2 100644 --- a/src/tetrapodposer.cpp +++ b/src/tetrapodposer.cpp @@ -38,7 +38,6 @@ void TetrapodPoser::commit() QVector3D referenceDirection = bone.name == "RightHand" ? QVector3D(1, 0, 0) : QVector3D(-1, 0, 0); auto angleWithX = (int)angleBetweenVectors(handDirection, -referenceDirection); auto angleWithZ = (int)angleBetweenVectors(handDirection, QVector3D(0, 0, -1)); - qDebug() << "angleWithX:" << angleWithX << "angleWithZ:" << angleWithZ; QVector3D rotateAxis = angleWithX < angleWithZ ? QVector3D::crossProduct(handDirection, referenceDirection).normalized() : QVector3D::crossProduct(handDirection, QVector3D(0, 0, -1)).normalized(); diff --git a/src/tetrapodposer.h b/src/tetrapodposer.h index 88c46d68..18268ada 100644 --- a/src/tetrapodposer.h +++ b/src/tetrapodposer.h @@ -8,7 +8,7 @@ class TetrapodPoser : public Poser public: TetrapodPoser(const std::vector &bones); public: - void commit(); + void commit() override; }; #endif diff --git a/src/tetrapodrigger.cpp b/src/tetrapodrigger.cpp new file mode 100644 index 00000000..f84eac62 --- /dev/null +++ b/src/tetrapodrigger.cpp @@ -0,0 +1,686 @@ +#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 = "Body"; + bodyBone.headPosition = QVector3D(0, 0, 0); + bodyBone.tailPosition = bonesOrigin; + 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["Body"]].tailPosition; + leftHipBone.tailPosition = leftUpperLegBoneStartPosition; + boneIndexMap[leftHipBone.name] = leftHipBone.index; + m_resultBones[boneIndexMap["Body"]].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["Body"]].tailPosition; + rightHipBone.tailPosition = rightUpperLegBoneStartPosition; + boneIndexMap[rightHipBone.name] = rightHipBone.index; + m_resultBones[boneIndexMap["Body"]].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["Body"]].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["Body"]].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 new file mode 100644 index 00000000..6e259003 --- /dev/null +++ b/src/tetrapodrigger.h @@ -0,0 +1,19 @@ +#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