Add new rig type: Generic

The difference between tetrapod and generic is that, with tetrapod, all the bones need to be generated are know before mark, so if some marks are missing, there would be a message prompted; with generic, you can mark what ever marks as you wish, and the algorithm is try to create a spine and the limbs growing on the spine, that means, the generated skeleton would be some kind of creature.
master
Jeremy Hu 2018-11-01 23:24:06 +08:00
parent ca07b3ef6f
commit 1c17e25b69
34 changed files with 1691 additions and 795 deletions

View File

@ -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

View File

@ -1,5 +1,6 @@
#include <QObject>
#include "bonemark.h"
#include "theme.h"
IMPL_BoneMarkToColor
IMPL_BoneMarkToString

View File

@ -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: \

View File

@ -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) {

View File

@ -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);

44
src/genericposer.cpp Normal file
View File

@ -0,0 +1,44 @@
#include <unordered_map>
#include "genericposer.h"
GenericPoser::GenericPoser(const std::vector<RiggerBone> &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();
}

14
src/genericposer.h Normal file
View File

@ -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<RiggerBone> &bones);
public:
void commit() override;
};
#endif

502
src/genericrigger.cpp Normal file
View File

@ -0,0 +1,502 @@
#include <QDebug>
#include <cmath>
#include <queue>
#include <unordered_set>
#include "genericrigger.h"
GenericRigger::GenericRigger(const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &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<int> &jointMarkIndicies)
{
const auto &mark = m_marks[markIndex];
jointMarkIndicies.push_back(markIndex);
std::map<MeshSplitterTriangle, int> 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<std::pair<int, int>, 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<const MeshSplitterTriangle *> 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<const MeshSplitterTriangle *> 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<int, std::set<int>> pairs;
std::set<std::pair<int, int>> processedEdges;
for (const auto &edge: edgeToTriangleMap) {
if (processedEdges.find(edge.first) != processedEdges.end())
continue;
std::pair<int, int> 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<int> 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<MeshSplitterTriangle> bodyTriangles;
if (!calculateBodyTriangles(bodyTriangles))
return false;
std::set<int> 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<int> s_empty;
const std::vector<int> *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<int> limbMarkIndicies;
};
std::vector<SpineNode> 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<float>::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<int> 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<QString, int> 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<QColor> twoColorsForSpine = {QColor(0x57, 0x43, 0x98), Qt::white};
const std::vector<QColor> 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<int> spineBoneVertices;
QVector3D tailPosition;
int buttonRow = spineNodes.size() - spineGenerateOrder;
if (spineNodeIndex + 1 < (int)spineNodes.size()) {
std::set<int> frontOrCoincidentVertices;
std::set<int> 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<int> 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<int> 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<QVector3D> extremCoords(6, jointMark.bonePosition);
resolveBoundingBox(remainingLimbVertices, extremCoords[0], extremCoords[1], extremCoords[2], extremCoords[3], extremCoords[4], extremCoords[5]);
float maxDistance2 = std::numeric_limits<float>::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<int> frontOrCoincidentVertices;
std::set<int> 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<int>::max();
int maxColumn = std::numeric_limits<int>::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);
}

26
src/genericrigger.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef DUST3D_GENERIC_RIGGER_H
#define DUST3D_GENERIC_RIGGER_H
#include <QVector3D>
#include <vector>
#include <set>
#include "rigger.h"
#include "meshsplitter.h"
class GenericRigger: public Rigger
{
Q_OBJECT
public:
GenericRigger(const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &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<int> &jointMarkIndicies);
void normalizeButtonColumns();
QString namingLimb(int spineOrder, SkeletonSide side, int limbOrder, int jointOrder);
};
#endif

View File

@ -278,6 +278,7 @@ void *MeshGenerator::combinePartMesh(QString partId)
cacheBmeshNodes.clear();
cacheBmeshVertices.clear();
cacheBmeshQuads.clear();
std::map<int, std::vector<int>> 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);

View File

@ -4,9 +4,10 @@
#include "meshsplitter.h"
bool MeshSplitter::split(const std::set<MeshSplitterTriangle> &input,
const std::set<MeshSplitterTriangle> &splitter,
std::set<MeshSplitterTriangle> &splitter,
std::set<MeshSplitterTriangle> &firstGroup,
std::set<MeshSplitterTriangle> &secondGroup)
std::set<MeshSplitterTriangle> &secondGroup,
bool expandSplitter)
{
firstGroup.clear();
secondGroup.clear();
@ -20,19 +21,35 @@ bool MeshSplitter::split(const std::set<MeshSplitterTriangle> &input,
}
}
/*
size_t noClosingEdges = 0;
for (const auto &triangle: input) {
// Expand the splitter if needed
if (expandSplitter) {
std::vector<MeshSplitterTriangle> expandedTriangles;
for (const auto &triangle: splitter) {
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++;
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);
}
}
}
qDebug() << "noClosingEdges:" << noClosingEdges;
*/
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;
}
}
// Find one triangle wich is direct neighbor of one splitter
MeshSplitterTriangle startTriangle;

View File

@ -18,9 +18,10 @@ class MeshSplitter
{
public:
static bool split(const std::set<MeshSplitterTriangle> &input,
const std::set<MeshSplitterTriangle> &splitter,
std::set<MeshSplitterTriangle> &splitter,
std::set<MeshSplitterTriangle> &firstGroup,
std::set<MeshSplitterTriangle> &secondGroup);
std::set<MeshSplitterTriangle> &secondGroup,
bool expandSplitter=false);
};
#endif

View File

@ -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);

View File

@ -2,12 +2,14 @@
#include <QElapsedTimer>
#include <cmath>
#include "motionsgenerator.h"
#include "tetrapodposer.h"
#include "posemeshcreator.h"
#include "poserconstruct.h"
MotionsGenerator::MotionsGenerator(const std::vector<RiggerBone> *rigBones,
MotionsGenerator::MotionsGenerator(RigType rigType,
const std::vector<RiggerBone> *rigBones,
const std::map<int, RiggerVertexWeights> *rigWeights,
const Outcome &outcome) :
m_rigType(rigType),
m_rigBones(*rigBones),
m_rigWeights(*rigWeights),
m_outcome(outcome)
@ -243,7 +245,9 @@ std::vector<std::pair<float, JointNodeTree>> 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<QUuid> visited;

View File

@ -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<RiggerBone> *rigBones,
MotionsGenerator(RigType rigType,
const std::vector<RiggerBone> *rigBones,
const std::map<int, RiggerVertexWeights> *rigWeights,
const Outcome &outcome);
~MotionsGenerator();
@ -42,6 +43,7 @@ private:
void generatePreviewsForOutcomes(const std::vector<std::pair<float, JointNodeTree>> &outcomes, std::vector<std::pair<float, MeshLoader *>> &previews);
float calculateMotionDuration(const QUuid &motionId, std::set<QUuid> &visited);
RigType m_rigType = RigType::None;
std::vector<RiggerBone> m_rigBones;
std::map<int, RiggerVertexWeights> m_rigWeights;
Outcome m_outcome;
@ -52,7 +54,7 @@ private:
std::map<QUuid, std::vector<std::pair<float, MeshLoader *>>> m_resultPreviewMeshs;
std::map<QUuid, std::vector<std::pair<float, JointNodeTree>>> m_resultJointNodeTrees;
std::map<QUuid, JointNodeTree> m_poseJointNodeTreeMap;
TetrapodPoser *m_poser = nullptr;
Poser *m_poser = nullptr;
int m_fps = 30;
};

View File

@ -19,7 +19,9 @@ struct OutcomeNode
QColor color;
QUuid materialId;
QUuid mirrorFromPartId;
QUuid mirroredByPartId;
BoneMark boneMark;
QVector3D baseNormal;
};
class Outcome

View File

@ -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<QString, std::tuple<QPushButton *, PopupWidgetType>> 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<QString, std::tuple<QPushButton *, PopupWidgetType>> buttons;
const std::vector<RiggerBone> *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);

View File

@ -5,15 +5,11 @@
#include <QCloseEvent>
#include <QLineEdit>
#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

View File

@ -1,12 +1,14 @@
#include <QGuiApplication>
#include <QElapsedTimer>
#include "posepreviewsgenerator.h"
#include "tetrapodposer.h"
#include "posemeshcreator.h"
#include "poserconstruct.h"
PosePreviewsGenerator::PosePreviewsGenerator(const std::vector<RiggerBone> *rigBones,
PosePreviewsGenerator::PosePreviewsGenerator(RigType rigType,
const std::vector<RiggerBone> *rigBones,
const std::map<int, RiggerVertexWeights> *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();

View File

@ -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<RiggerBone> *rigBones,
PosePreviewsGenerator(RigType rigType,
const std::vector<RiggerBone> *rigBones,
const std::map<int, RiggerVertexWeights> *rigWeights,
const Outcome &outcome);
~PosePreviewsGenerator();
@ -24,6 +26,7 @@ signals:
public slots:
void process();
private:
RigType m_rigType = RigType::None;
std::vector<RiggerBone> m_rigBones;
std::map<int, RiggerVertexWeights> m_rigWeights;
Outcome *m_outcome = nullptr;

12
src/poserconstruct.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "poserconstruct.h"
#include "tetrapodposer.h"
#include "genericposer.h"
Poser *newPoser(RigType rigType, const std::vector<RiggerBone> &bones)
{
if (rigType == RigType::Tetrapod)
return new TetrapodPoser(bones);
else if (rigType == RigType::Generic)
return new GenericPoser(bones);
return nullptr;
}

9
src/poserconstruct.h Normal file
View File

@ -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<RiggerBone> &bones);
#endif

View File

@ -1,10 +1,12 @@
#include <QGuiApplication>
#include <QDebug>
#include <QElapsedTimer>
#include <cmath>
#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<std::vector<QVector3D>> *triangleVertexNormals = m_outcome->triangleVertexNormals();
std::map<std::pair<QUuid, QUuid>, const OutcomeNode *> nodeMap;
for (const auto &item: m_outcome->nodes) {
nodeMap.insert({{item.partId, item.nodeId}, &item});
}
const std::vector<QVector3D> *triangleTangents = m_outcome->triangleTangents();
for (const auto &vertex: m_outcome->vertices) {
inputVerticesPositions.push_back(vertex);
}
std::map<std::pair<BoneMark, SkeletonSide>, std::tuple<QVector3D, int, std::set<MeshSplitterTriangle>>> marksMap;
std::map<std::pair<QUuid, QUuid>, std::tuple<BoneMark, SkeletonSide, QVector3D, std::set<MeshSplitterTriangle>, 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<MeshSplitterTriangle>(), 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<std::tuple<BoneMark, SkeletonSide, QVector3D, std::set<MeshSplitterTriangle>, float, QVector3D>> markedNodesList;
for (const auto &markedNode: markedNodes) {
markedNodesList.push_back(markedNode.second);
}
// Combine the overlapped marks
std::vector<std::tuple<BoneMark, SkeletonSide, QVector3D, std::set<MeshSplitterTriangle>, float, QVector3D>> combinedMarkedNodesList;
std::set<size_t> processedNodes;
for (size_t i = 0; i < markedNodesList.size(); ++i) {
if (processedNodes.find(i) != processedNodes.end())
continue;
const auto &first = markedNodesList[i];
std::tuple<BoneMark, SkeletonSide, QVector3D, std::set<MeshSplitterTriangle>, 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();
}
if (m_isSucceed) {
qDebug() << "Rig succeed";
} else {
qDebug() << "Rig failed";
if (nullptr != m_autoRigger) {
m_missingMarkNames = m_autoRigger->missingMarkNames();
m_errorMarkNames = m_autoRigger->errorMarkNames();
for (const auto &message: m_autoRigger->messages()) {
qDebug() << "errorType:" << message.first << "Message:" << message.second;
}
}
}
// Blend vertices colors according to bone weights
std::vector<QColor> inputVerticesColors(m_outcome->vertices.size());
std::vector<QColor> 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 &currentVertex = 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);

View File

@ -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<RiggerBone> *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;

View File

@ -5,6 +5,8 @@
#include "skeletonside.h"
#include "rigger.h"
size_t Rigger::m_maxCutOffSplitterExpandRound = 3;
Rigger::Rigger(const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &inputTriangles) :
m_verticesPositions(verticesPositions),
@ -12,11 +14,9 @@ Rigger::Rigger(const std::vector<QVector3D> &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<MeshSplitterTriangle> &bodyTriangles)
@ -42,23 +42,25 @@ bool Rigger::calculateBodyTriangles(std::set<MeshSplitterTriangle> &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<MeshSplitterTriangle> &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<MeshSplitterTriangle> &triang
}
}
bool Rigger::validate()
{
bool foundError = false;
std::vector<std::pair<BoneMark, SkeletonSide>> 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<int> &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<int> &vertices)
return maxZ;
}
QVector3D Rigger::averagePosition(const std::set<int> &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<int> &vertices, float y, std::set<int> &greaterEqualThanVertices, std::set<int> &lessThanVertices)
{
for (const auto &index: vertices) {
@ -236,6 +212,19 @@ void Rigger::splitVerticesByZ(const std::set<int> &vertices, float z, std::set<i
}
}
void Rigger::splitVerticesByPlane(const std::set<int> &vertices, const QVector3D &pointOnPlane, const QVector3D &planeNormal, std::set<int> &frontOrCoincidentVertices, std::set<int> &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<RiggerBone> &Rigger::resultBones()
{
return m_resultBones;
@ -256,582 +245,6 @@ void Rigger::addVerticesToWeights(const std::set<int> &vertices, int boneIndex)
}
}
bool Rigger::rig()
{
if (!validate())
return false;
std::set<MeshSplitterTriangle> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> bodyVertices;
addTrianglesToVertices(bodyTriangles, bodyVertices);
addTrianglesToVertices(m_marks[leftShoulderIndicies->second[0]].markTriangles, bodyVertices);
addTrianglesToVertices(m_marks[rightShoulderIndicies->second[0]].markTriangles, bodyVertices);
std::set<int> bodyVerticesAfterShoulder;
std::set<int> neckVertices;
{
std::set<int> 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<int> 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<int> bodyVerticesAfterChest;
std::set<int> 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<int> bodyVerticesBeforeSpine;
std::set<int> 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<int> leftElbowMarkVertices;
addTrianglesToVertices(m_marks[leftElbowIndicies->second[0]].markTriangles, leftElbowMarkVertices);
std::set<int> leftUpperArmVertices;
{
std::set<int> 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<int> leftArmVerticesSinceElbow;
std::set<int> leftLowerArmVertices;
{
std::set<int> 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<int> leftWristMarkVertices;
addTrianglesToVertices(m_marks[leftWristIndicies->second[0]].markTriangles, leftWristMarkVertices);
{
std::set<int> 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<int> leftHandVertices;
{
std::set<int> 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<int> rightElbowMarkVertices;
addTrianglesToVertices(m_marks[rightElbowIndicies->second[0]].markTriangles, rightElbowMarkVertices);
std::set<int> rightUpperArmVertices;
{
std::set<int> 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<int> rightArmVerticesSinceElbow;
std::set<int> rightLowerArmVertices;
{
std::set<int> 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<int> rightWristMarkVertices;
addTrianglesToVertices(m_marks[rightWristIndicies->second[0]].markTriangles, rightWristMarkVertices);
{
std::set<int> 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<int> rightHandVertices;
{
std::set<int> 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<int> leftKneeMarkVertices;
addTrianglesToVertices(m_marks[leftKneeIndicies->second[0]].markTriangles, leftKneeMarkVertices);
std::set<int> leftUpperLegVertices;
{
std::set<int> leftLegVerticesAfterKnee;
QVector3D minY = findMinY(leftKneeMarkVertices);
splitVerticesByY(leftLegVertices, minY.y(), leftUpperLegVertices, leftLegVerticesAfterKnee);
}
// 4.1.2 Collect vertices for left lower leg:
std::set<int> leftLegVerticesSinceKnee;
std::set<int> leftLowerLegVertices;
{
std::set<int> leftLegVerticesBeforeKnee;
QVector3D maxY = findMaxY(leftKneeMarkVertices);
splitVerticesByY(leftLegVertices, maxY.y(), leftLegVerticesBeforeKnee, leftLegVerticesSinceKnee);
}
std::set<int> leftAnkleMarkVertices;
addTrianglesToVertices(m_marks[leftAnkleIndicies->second[0]].markTriangles, leftAnkleMarkVertices);
{
std::set<int> leftLegVerticesAfterAnkle;
QVector3D minY = findMinY(leftAnkleMarkVertices);
splitVerticesByY(leftLegVerticesSinceKnee, minY.y(), leftLowerLegVertices, leftLegVerticesAfterAnkle);
}
// 4.1.3 Collect vertices for left foot:
std::set<int> leftFootVertices;
{
std::set<int> leftLegVerticesBeforeAnkle;
splitVerticesByY(leftLegVerticesSinceKnee, m_marks[leftAnkleIndicies->second[0]].bonePosition.y(), leftLegVerticesBeforeAnkle, leftFootVertices);
}
// 4.2.1 Collect vertices for right upper leg:
std::set<int> rightKneeMarkVertices;
addTrianglesToVertices(m_marks[rightKneeIndicies->second[0]].markTriangles, rightKneeMarkVertices);
std::set<int> rightUpperLegVertices;
{
std::set<int> rightLegVerticesAfterKnee;
QVector3D minY = findMinY(rightKneeMarkVertices);
splitVerticesByY(rightLegVertices, minY.y(), rightUpperLegVertices, rightLegVerticesAfterKnee);
}
// 4.2.2 Collect vertices for right lower leg:
std::set<int> rightLegVerticesSinceKnee;
std::set<int> rightLowerLegVertices;
{
std::set<int> rightLegVerticesBeforeKnee;
QVector3D maxY = findMaxY(rightKneeMarkVertices);
splitVerticesByY(rightLegVertices, maxY.y(), rightLegVerticesBeforeKnee, rightLegVerticesSinceKnee);
}
std::set<int> rightAnkleMarkVertices;
addTrianglesToVertices(m_marks[rightAnkleIndicies->second[0]].markTriangles, rightAnkleMarkVertices);
{
std::set<int> rightLegVerticesAfterAnkle;
QVector3D minY = findMinY(rightAnkleMarkVertices);
splitVerticesByY(rightLegVerticesSinceKnee, minY.y(), rightLowerLegVertices, rightLegVerticesAfterAnkle);
}
// 4.2.3 Collect vertices for right foot:
std::set<int> rightFootVertices;
{
std::set<int> rightLegVerticesBeforeAnkle;
splitVerticesByY(rightLegVerticesSinceKnee, m_marks[rightAnkleIndicies->second[0]].bonePosition.y(), rightLegVerticesBeforeAnkle, rightFootVertices);
}
// 5. Generate bones
std::map<QString, int> 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<QString> &Rigger::missingMarkNames()
{
return m_missingMarkNames;

View File

@ -5,17 +5,27 @@
#include <QObject>
#include <QColor>
#include <QDebug>
#include <QVector2D>
#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<MeshSplitterTriangle> markTriangles;
const std::set<MeshSplitterTriangle> &bigGroup() const
{
@ -29,9 +39,17 @@ public:
m_secondGroup :
m_firstGroup;
}
bool split(const std::set<MeshSplitterTriangle> &input)
bool split(const std::set<MeshSplitterTriangle> &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<MeshSplitterTriangle> 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<int, int> button = {0, 0};
QVector3D baseNormal;
std::vector<int> 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<int> m_boneRawIndicies;
std::vector<std::pair<int, float>> m_boneRawWeights;
};
@ -90,19 +116,20 @@ class Rigger : public QObject
public:
Rigger(const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &inputTriangles);
bool addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bonePosition,
bool addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bonePosition, float nodeRadius, QVector3D baseNormal,
const std::set<MeshSplitterTriangle> &markTriangles);
const std::vector<std::pair<QtMsgType, QString>> &messages();
bool rig();
const std::vector<RiggerBone> &resultBones();
const std::map<int, RiggerVertexWeights> &resultWeights();
const std::vector<QString> &missingMarkNames();
const std::vector<QString> &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<MeshSplitterTriangle> &triangles, std::set<int> &vertices);
bool calculateBodyTriangles(std::set<MeshSplitterTriangle> &bodyTriangles);
bool isCutOffSplitter(BoneMark boneMark);
void resolveBoundingBox(const std::set<int> &vertices, QVector3D &xMin, QVector3D &xMax, QVector3D &yMin, QVector3D &yMax, QVector3D &zMin, QVector3D &zMax);
QVector3D findMinX(const std::set<int> &vertices);
QVector3D findMaxX(const std::set<int> &vertices);
@ -110,9 +137,11 @@ private:
QVector3D findMaxY(const std::set<int> &vertices);
QVector3D findMinZ(const std::set<int> &vertices);
QVector3D findMaxZ(const std::set<int> &vertices);
QVector3D averagePosition(const std::set<int> &vertices);
void splitVerticesByY(const std::set<int> &vertices, float y, std::set<int> &greaterEqualThanVertices, std::set<int> &lessThanVertices);
void splitVerticesByX(const std::set<int> &vertices, float x, std::set<int> &greaterEqualThanVertices, std::set<int> &lessThanVertices);
void splitVerticesByZ(const std::set<int> &vertices, float z, std::set<int> &greaterEqualThanVertices, std::set<int> &lessThanVertices);
void splitVerticesByPlane(const std::set<int> &vertices, const QVector3D &pointOnPlane, const QVector3D &planeNormal, std::set<int> &frontOrCoincidentVertices, std::set<int> &backVertices);
void addVerticesToWeights(const std::set<int> &vertices, int boneIndex);
std::vector<std::pair<QtMsgType, QString>> m_messages;
std::vector<QVector3D> m_verticesPositions;
@ -123,6 +152,7 @@ private:
std::map<int, RiggerVertexWeights> m_resultWeights;
std::vector<QString> m_missingMarkNames;
std::vector<QString> m_errorMarkNames;
static size_t m_maxCutOffSplitterExpandRound;
};
#endif

13
src/riggerconstruct.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "riggerconstruct.h"
#include "tetrapodrigger.h"
#include "genericrigger.h"
Rigger *newRigger(RigType rigType, const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &inputTriangles)
{
if (rigType == RigType::Tetrapod)
return new TetrapodRigger(verticesPositions, inputTriangles);
else if (rigType == RigType::Generic)
return new GenericRigger(verticesPositions, inputTriangles);
return nullptr;
}

10
src/riggerconstruct.h Normal file
View File

@ -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<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &inputTriangles);
#endif

View File

@ -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: \

View File

@ -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)
{

View File

@ -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();

View File

@ -8,7 +8,7 @@ class TetrapodPoser : public Poser
public:
TetrapodPoser(const std::vector<RiggerBone> &bones);
public:
void commit();
void commit() override;
};
#endif

686
src/tetrapodrigger.cpp Normal file
View File

@ -0,0 +1,686 @@
#include <QDebug>
#include <cmath>
#include "tetrapodrigger.h"
TetrapodRigger::TetrapodRigger(const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &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<MeshSplitterTriangle> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> bodyVertices;
addTrianglesToVertices(bodyTriangles, bodyVertices);
addTrianglesToVertices(m_marks[leftShoulderIndicies->second[0]].markTriangles, bodyVertices);
addTrianglesToVertices(m_marks[rightShoulderIndicies->second[0]].markTriangles, bodyVertices);
std::set<int> bodyVerticesAfterShoulder;
std::set<int> neckVertices;
{
std::set<int> 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<int> 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<int> bodyVerticesAfterChest;
std::set<int> 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<int> bodyVerticesBeforeSpine;
std::set<int> 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<int> leftElbowMarkVertices;
addTrianglesToVertices(m_marks[leftElbowIndicies->second[0]].markTriangles, leftElbowMarkVertices);
std::set<int> leftUpperArmVertices;
{
std::set<int> 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<int> leftArmVerticesSinceElbow;
std::set<int> leftLowerArmVertices;
{
std::set<int> 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<int> leftWristMarkVertices;
addTrianglesToVertices(m_marks[leftWristIndicies->second[0]].markTriangles, leftWristMarkVertices);
{
std::set<int> 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<int> leftHandVertices;
{
std::set<int> 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<int> rightElbowMarkVertices;
addTrianglesToVertices(m_marks[rightElbowIndicies->second[0]].markTriangles, rightElbowMarkVertices);
std::set<int> rightUpperArmVertices;
{
std::set<int> 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<int> rightArmVerticesSinceElbow;
std::set<int> rightLowerArmVertices;
{
std::set<int> 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<int> rightWristMarkVertices;
addTrianglesToVertices(m_marks[rightWristIndicies->second[0]].markTriangles, rightWristMarkVertices);
{
std::set<int> 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<int> rightHandVertices;
{
std::set<int> 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<int> leftKneeMarkVertices;
addTrianglesToVertices(m_marks[leftKneeIndicies->second[0]].markTriangles, leftKneeMarkVertices);
std::set<int> leftUpperLegVertices;
{
std::set<int> leftLegVerticesAfterKnee;
QVector3D minY = findMinY(leftKneeMarkVertices);
splitVerticesByY(leftLegVertices, minY.y(), leftUpperLegVertices, leftLegVerticesAfterKnee);
}
// 4.1.2 Collect vertices for left lower leg:
std::set<int> leftLegVerticesSinceKnee;
std::set<int> leftLowerLegVertices;
{
std::set<int> leftLegVerticesBeforeKnee;
QVector3D maxY = findMaxY(leftKneeMarkVertices);
splitVerticesByY(leftLegVertices, maxY.y(), leftLegVerticesBeforeKnee, leftLegVerticesSinceKnee);
}
std::set<int> leftAnkleMarkVertices;
addTrianglesToVertices(m_marks[leftAnkleIndicies->second[0]].markTriangles, leftAnkleMarkVertices);
{
std::set<int> 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<int> leftFootVertices;
{
std::set<int> 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<int> rightKneeMarkVertices;
addTrianglesToVertices(m_marks[rightKneeIndicies->second[0]].markTriangles, rightKneeMarkVertices);
std::set<int> rightUpperLegVertices;
{
std::set<int> rightLegVerticesAfterKnee;
QVector3D minY = findMinY(rightKneeMarkVertices);
splitVerticesByY(rightLegVertices, minY.y(), rightUpperLegVertices, rightLegVerticesAfterKnee);
}
// 4.2.2 Collect vertices for right lower leg:
std::set<int> rightLegVerticesSinceKnee;
std::set<int> rightLowerLegVertices;
{
std::set<int> rightLegVerticesBeforeKnee;
QVector3D maxY = findMaxY(rightKneeMarkVertices);
splitVerticesByY(rightLegVertices, maxY.y(), rightLegVerticesBeforeKnee, rightLegVerticesSinceKnee);
}
std::set<int> rightAnkleMarkVertices;
addTrianglesToVertices(m_marks[rightAnkleIndicies->second[0]].markTriangles, rightAnkleMarkVertices);
{
std::set<int> 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<int> rightFootVertices;
{
std::set<int> rightLegVerticesBeforeAnkle;
QVector3D maxY = findMaxY(rightAnkleMarkVertices);
QVector3D minY = findMinY(rightAnkleMarkVertices);
splitVerticesByY(rightLegVerticesSinceKnee, (maxY.y() + minY.y()) / 2, rightLegVerticesBeforeAnkle, rightFootVertices);
}
// 5. Generate bones
std::map<QString, int> 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<std::pair<BoneMark, SkeletonSide>> 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;
}

19
src/tetrapodrigger.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef DUST3D_TETRAPOD_RIGGER_H
#define DUST3D_TETRAPOD_RIGGER_H
#include <QVector3D>
#include "rigger.h"
#include "meshsplitter.h"
class TetrapodRigger : public Rigger
{
Q_OBJECT
public:
TetrapodRigger(const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &inputTriangles);
protected:
bool validate() override;
bool isCutOffSplitter(BoneMark boneMark) override;
bool rig() override;
};
#endif