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
parent
ca07b3ef6f
commit
1c17e25b69
15
dust3d.pro
15
dust3d.pro
|
@ -276,6 +276,21 @@ HEADERS += src/uvunwrap.h
|
|||
SOURCES += src/triangletangentresolve.cpp
|
||||
HEADERS += src/triangletangentresolve.h
|
||||
|
||||
SOURCES += src/tetrapodrigger.cpp
|
||||
HEADERS += src/tetrapodrigger.h
|
||||
|
||||
SOURCES += src/genericrigger.cpp
|
||||
HEADERS += src/genericrigger.h
|
||||
|
||||
SOURCES += src/genericposer.cpp
|
||||
HEADERS += src/genericposer.h
|
||||
|
||||
SOURCES += src/riggerconstruct.cpp
|
||||
HEADERS += src/riggerconstruct.h
|
||||
|
||||
SOURCES += src/poserconstruct.cpp
|
||||
HEADERS += src/poserconstruct.h
|
||||
|
||||
SOURCES += src/main.cpp
|
||||
|
||||
HEADERS += src/version.h
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <QObject>
|
||||
#include "bonemark.h"
|
||||
#include "theme.h"
|
||||
|
||||
IMPL_BoneMarkToColor
|
||||
IMPL_BoneMarkToString
|
||||
|
|
|
@ -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: \
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@ struct OutcomeNode
|
|||
QColor color;
|
||||
QUuid materialId;
|
||||
QUuid mirrorFromPartId;
|
||||
QUuid mirroredByPartId;
|
||||
BoneMark boneMark;
|
||||
QVector3D baseNormal;
|
||||
};
|
||||
|
||||
class Outcome
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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 ¤tVertex = triangleVertices[triangleVerticesNum++];
|
||||
const auto &sourcePosition = inputVerticesPositions[sourceTriangle[i]];
|
||||
|
@ -177,6 +237,11 @@ void RigGenerator::generate()
|
|||
currentVertex.normX = sourceNormal->x();
|
||||
currentVertex.normY = sourceNormal->y();
|
||||
currentVertex.normZ = sourceNormal->z();
|
||||
currentVertex.metalness = MeshLoader::m_defaultMetalness;
|
||||
currentVertex.roughness = MeshLoader::m_defaultRoughness;
|
||||
currentVertex.tangentX = sourceTangent->x();
|
||||
currentVertex.tangentY = sourceTangent->y();
|
||||
currentVertex.tangentZ = sourceTangent->z();
|
||||
}
|
||||
}
|
||||
m_resultMesh = new MeshLoader(triangleVertices, triangleVerticesNum);
|
||||
|
|
|
@ -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;
|
||||
|
|
659
src/rigger.cpp
659
src/rigger.cpp
|
@ -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;
|
||||
|
|
46
src/rigger.h
46
src/rigger.h
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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: \
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -8,7 +8,7 @@ class TetrapodPoser : public Poser
|
|||
public:
|
||||
TetrapodPoser(const std::vector<RiggerBone> &bones);
|
||||
public:
|
||||
void commit();
|
||||
void commit() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
Loading…
Reference in New Issue