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