Unify rig type of generic and tetrapod as animal

Marks also reduced to four types: Neck, Limb, Tail and Joint.
master
Jeremy Hu 2018-11-07 00:26:19 +08:00
parent d72738feb1
commit 2c8975b1cd
27 changed files with 633 additions and 1390 deletions

View File

@ -168,9 +168,6 @@ HEADERS += src/skinnedmeshcreator.h
SOURCES += src/jointnodetree.cpp SOURCES += src/jointnodetree.cpp
HEADERS += src/jointnodetree.h HEADERS += src/jointnodetree.h
SOURCES += src/tetrapodposer.cpp
HEADERS += src/tetrapodposer.h
SOURCES += src/poser.cpp SOURCES += src/poser.cpp
HEADERS += src/poser.h HEADERS += src/poser.h
@ -276,14 +273,11 @@ HEADERS += src/uvunwrap.h
SOURCES += src/triangletangentresolve.cpp SOURCES += src/triangletangentresolve.cpp
HEADERS += src/triangletangentresolve.h HEADERS += src/triangletangentresolve.h
SOURCES += src/tetrapodrigger.cpp SOURCES += src/animalrigger.cpp
HEADERS += src/tetrapodrigger.h HEADERS += src/animalrigger.h
SOURCES += src/genericrigger.cpp SOURCES += src/animalposer.cpp
HEADERS += src/genericrigger.h HEADERS += src/animalposer.h
SOURCES += src/genericposer.cpp
HEADERS += src/genericposer.h
SOURCES += src/riggerconstruct.cpp SOURCES += src/riggerconstruct.cpp
HEADERS += src/riggerconstruct.h HEADERS += src/riggerconstruct.h

232
src/animalposer.cpp Normal file
View File

@ -0,0 +1,232 @@
#include <cmath>
#include <QtMath>
#include <QRegularExpression>
#include "animalposer.h"
#include "util.h"
AnimalPoser::AnimalPoser(const std::vector<RiggerBone> &bones) :
Poser(bones)
{
}
void AnimalPoser::resolveTranslation()
{
for (const auto &item: parameters()) {
int boneIndex = findBoneIndex(item.first);
if (-1 == boneIndex) {
continue;
}
auto findTranslateXResult = item.second.find("translateX");
auto findTranslateYResult = item.second.find("translateY");
auto findTranslateZResult = item.second.find("translateZ");
if (findTranslateXResult != item.second.end() ||
findTranslateYResult != item.second.end() ||
findTranslateZResult != item.second.end()) {
float x = valueOfKeyInMapOrEmpty(item.second, "translateX").toFloat();
float y = valueOfKeyInMapOrEmpty(item.second, "translateY").toFloat();
float z = valueOfKeyInMapOrEmpty(item.second, "translateZ").toFloat();
QVector3D translation = {x, y, z};
m_jointNodeTree.addTranslation(boneIndex, translation);
continue;
}
}
QRegularExpression reJoints("^([a-zA-Z]+\\d*)_Joint\\d+$");
QRegularExpression reSpine("^([a-zA-Z]+)\\d*$");
std::map<QString, std::vector<QString>> chains;
for (const auto &item: parameters()) {
QRegularExpressionMatch match = reJoints.match(item.first);
if (match.hasMatch()) {
QString name = match.captured(1);
chains[name].push_back(item.first);
qDebug() << "chains[" << name << "]:" << item.first;
} else {
match = reSpine.match(item.first);
if (match.hasMatch()) {
QString name = match.captured(1);
chains[name].push_back(item.first);
qDebug() << "chains[" << name << "]:" << item.first;
} else if (item.first.startsWith("Virtual_")) {
qDebug() << "Ignore connector:" << item.first;
} else {
qDebug() << "Unrecognized bone name:" << item.first;
}
}
}
for (auto &chain: chains) {
std::sort(chain.second.begin(), chain.second.end(), [](const QString &first, const QString &second) {
return first < second;
});
qDebug() << "Chain:";
for (const auto &chainJoint: chain.second) {
qDebug() << chainJoint;
}
resolveChainRotation(chain.second);
}
}
std::pair<bool, QVector3D> AnimalPoser::findQVector3DFromMap(const std::map<QString, QString> &map, const QString &xName, const QString &yName, const QString &zName)
{
auto findXResult = map.find(xName);
auto findYResult = map.find(yName);
auto findZResult = map.find(zName);
if (findXResult == map.end() &&
findYResult == map.end() &&
findZResult == map.end()) {
return {false, QVector3D()};
}
return {true, {
valueOfKeyInMapOrEmpty(map, xName).toFloat(),
valueOfKeyInMapOrEmpty(map, yName).toFloat(),
valueOfKeyInMapOrEmpty(map, zName).toFloat()
}};
}
std::pair<bool, std::pair<QVector3D, QVector3D>> AnimalPoser::findBonePositionsFromParameters(const std::map<QString, QString> &map)
{
auto findBoneStartResult = findQVector3DFromMap(map, "fromX", "fromY", "fromZ");
auto findBoneStopResult = findQVector3DFromMap(map, "toX", "toY", "toZ");
if (!findBoneStartResult.first || !findBoneStopResult.first)
return {false, {QVector3D(), QVector3D()}};
return {true, {findBoneStartResult.second, findBoneStopResult.second}};
}
void AnimalPoser::resolveChainRotation(const std::vector<QString> &limbBoneNames)
{
std::vector<QQuaternion> rotationsForEndEffector;
size_t endEffectorStart = 0;
// We match the poses by the distance and rotation plane
if (limbBoneNames.size() >= 2) {
endEffectorStart = 2;
const auto &beginBoneName = limbBoneNames[0];
const auto &middleBoneName = limbBoneNames[1];
const auto &beginBoneParameters = parameters().find(beginBoneName);
if (beginBoneParameters == parameters().end()) {
qDebug() << beginBoneName << "'s parameters not found";
return;
}
auto matchBeginBonePositions = findBonePositionsFromParameters(beginBoneParameters->second);
if (!matchBeginBonePositions.first) {
qDebug() << beginBoneName << "'s positions not found";
return;
}
const auto &middleBoneParameters = parameters().find(middleBoneName);
if (middleBoneParameters == parameters().end()) {
qDebug() << middleBoneName << "'s parameters not found";
return;
}
auto matchMiddleBonePositions = findBonePositionsFromParameters(middleBoneParameters->second);
if (!matchMiddleBonePositions.first) {
qDebug() << middleBoneName << "'s positions not found";
return;
}
float matchLimbLength = (matchBeginBonePositions.second.first - matchBeginBonePositions.second.second).length() +
(matchMiddleBonePositions.second.first - matchMiddleBonePositions.second.second).length();
auto matchDistanceBetweenBeginAndEndBones = (matchBeginBonePositions.second.first - matchMiddleBonePositions.second.second).length();
auto matchRotatePlaneNormal = QVector3D::crossProduct((matchBeginBonePositions.second.second - matchBeginBonePositions.second.first).normalized(), (matchMiddleBonePositions.second.second - matchBeginBonePositions.second.second).normalized());
auto matchDirectionBetweenBeginAndEndPones = (matchMiddleBonePositions.second.second - matchBeginBonePositions.second.first).normalized();
int beginBoneIndex = findBoneIndex(beginBoneName);
if (-1 == beginBoneIndex) {
qDebug() << beginBoneName << "not found in rigged bones";
return;
}
const auto &beginBone = bones()[beginBoneIndex];
int middleBoneIndex = findBoneIndex(middleBoneName);
if (-1 == middleBoneIndex) {
qDebug() << middleBoneName << "not found in rigged bones";
return;
}
const auto &middleBone = bones()[beginBoneIndex];
float targetBeginBoneLength = (beginBone.headPosition - beginBone.tailPosition).length();
float targetMiddleBoneLength = (middleBone.headPosition - middleBone.tailPosition).length();
float targetLimbLength = targetBeginBoneLength + targetMiddleBoneLength;
float targetDistanceBetweenBeginAndEndBones = matchDistanceBetweenBeginAndEndBones * (targetLimbLength / matchLimbLength);
QVector3D targetEndBoneStartPosition = beginBone.headPosition + matchDirectionBetweenBeginAndEndPones * targetDistanceBetweenBeginAndEndBones;
float angleBetweenDistanceAndMiddleBones = 0;
{
const float &a = targetMiddleBoneLength;
const float &b = targetDistanceBetweenBeginAndEndBones;
const float &c = targetBeginBoneLength;
double cosC = (a*a + b*b - c*c) / (2.0*a*b);
angleBetweenDistanceAndMiddleBones = qRadiansToDegrees(acos(cosC));
}
QVector3D targetMiddleBoneStartPosition;
{
qDebug() << beginBoneName << "Angle:" << angleBetweenDistanceAndMiddleBones;
auto rotation = QQuaternion::fromAxisAndAngle(matchRotatePlaneNormal, angleBetweenDistanceAndMiddleBones);
targetMiddleBoneStartPosition = targetEndBoneStartPosition + rotation.rotatedVector(-matchDirectionBetweenBeginAndEndPones).normalized() * targetMiddleBoneLength;
}
// Now the bones' positions have been resolved, we calculate the rotation
auto oldBeginBoneDirection = (beginBone.tailPosition - beginBone.headPosition).normalized();
auto newBeginBoneDirection = (targetMiddleBoneStartPosition - beginBone.headPosition).normalized();
auto beginBoneRotation = QQuaternion::rotationTo(oldBeginBoneDirection, newBeginBoneDirection);
m_jointNodeTree.updateRotation(beginBoneIndex, beginBoneRotation);
auto oldMiddleBoneDirection = (middleBone.tailPosition - middleBone.headPosition).normalized();
auto newMiddleBoneDirection = (targetEndBoneStartPosition - targetMiddleBoneStartPosition).normalized();
oldMiddleBoneDirection = beginBoneRotation.rotatedVector(oldMiddleBoneDirection);
auto middleBoneRotation = QQuaternion::rotationTo(oldMiddleBoneDirection, newMiddleBoneDirection);
m_jointNodeTree.updateRotation(middleBoneIndex, middleBoneRotation);
rotationsForEndEffector.push_back(beginBoneRotation);
rotationsForEndEffector.push_back(middleBoneRotation);
}
// Calculate the end effectors' rotation
if (limbBoneNames.size() > endEffectorStart) {
for (size_t i = endEffectorStart; i < limbBoneNames.size(); ++i) {
const auto &boneName = limbBoneNames[i];
int boneIndex = findBoneIndex(boneName);
if (-1 == boneIndex) {
qDebug() << "Find bone failed:" << boneName;
continue;
}
const auto &bone = bones()[boneIndex];
const auto &boneParameters = parameters().find(boneName);
if (boneParameters == parameters().end()) {
qDebug() << "Find bone parameters:" << boneName;
continue;
}
auto matchBonePositions = findBonePositionsFromParameters(boneParameters->second);
if (!matchBonePositions.first) {
qDebug() << "Find bone positions failed:" << boneName;
continue;
}
auto matchBoneDirection = (matchBonePositions.second.second - matchBonePositions.second.first).normalized();
auto oldBoneDirection = (bone.tailPosition - bone.headPosition).normalized();
auto newBoneDirection = matchBoneDirection;
for (const auto &rotation: rotationsForEndEffector) {
oldBoneDirection = rotation.rotatedVector(oldBoneDirection);
}
auto boneRotation = QQuaternion::rotationTo(oldBoneDirection, newBoneDirection);
m_jointNodeTree.updateRotation(boneIndex, boneRotation);
rotationsForEndEffector.push_back(boneRotation);
}
}
}
void AnimalPoser::commit()
{
resolveTranslation();
Poser::commit();
}

View File

@ -1,18 +1,18 @@
#ifndef DUST3D_TETRAPOD_POSER_H #ifndef DUST3D_ANIMAL_POSER_H
#define DUST3D_TETRAPOD_POSER_H #define DUST3D_ANIMAL_POSER_H
#include <vector> #include <vector>
#include "poser.h" #include "poser.h"
class TetrapodPoser : public Poser class AnimalPoser : public Poser
{ {
Q_OBJECT Q_OBJECT
public: public:
TetrapodPoser(const std::vector<RiggerBone> &bones); AnimalPoser(const std::vector<RiggerBone> &bones);
void commit() override; void commit() override;
private: private:
void resolveTranslation(); void resolveTranslation();
void resolveLimbRotation(const std::vector<QString> &limbBoneNames); void resolveChainRotation(const std::vector<QString> &limbBoneNames);
std::pair<bool, QVector3D> findQVector3DFromMap(const std::map<QString, QString> &map, const QString &xName, const QString &yName, const QString &zName); std::pair<bool, QVector3D> findQVector3DFromMap(const std::map<QString, QString> &map, const QString &xName, const QString &yName, const QString &zName);
std::pair<bool, std::pair<QVector3D, QVector3D>> findBonePositionsFromParameters(const std::map<QString, QString> &map); std::pair<bool, std::pair<QVector3D, QVector3D>> findBonePositionsFromParameters(const std::map<QString, QString> &map);
}; };

View File

@ -2,35 +2,36 @@
#include <cmath> #include <cmath>
#include <queue> #include <queue>
#include <unordered_set> #include <unordered_set>
#include "genericrigger.h" #include "animalrigger.h"
GenericRigger::GenericRigger(const std::vector<QVector3D> &verticesPositions, AnimalRigger::AnimalRigger(const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &inputTriangles) : const std::set<MeshSplitterTriangle> &inputTriangles) :
Rigger(verticesPositions, inputTriangles) Rigger(verticesPositions, inputTriangles)
{ {
} }
bool GenericRigger::validate() bool AnimalRigger::validate()
{ {
if (m_marksMap.empty()) {
m_messages.push_back(std::make_pair(QtCriticalMsg,
tr("Please tell me where is the neck, limbs and joints by mark the nodes from context menu")));
return false;
}
return true; return true;
} }
bool GenericRigger::isCutOffSplitter(BoneMark boneMark) bool AnimalRigger::isCutOffSplitter(BoneMark boneMark)
{ {
return BoneMark::Limb == boneMark; return BoneMark::Joint != boneMark;
} }
BoneMark GenericRigger::translateBoneMark(BoneMark boneMark) BoneMark AnimalRigger::translateBoneMark(BoneMark boneMark)
{ {
if (boneMark == BoneMark::Neck || return boneMark;
boneMark == BoneMark::Shoulder ||
boneMark == BoneMark::Hip ||
boneMark == BoneMark::Limb)
return BoneMark::Limb;
return BoneMark::Joint;
} }
void GenericRigger::collectJointsForLimb(int markIndex, std::vector<int> &jointMarkIndicies) bool AnimalRigger::collectJontsForChain(int markIndex, std::vector<int> &jointMarkIndicies)
{ {
const auto &mark = m_marks[markIndex]; const auto &mark = m_marks[markIndex];
@ -51,13 +52,13 @@ void GenericRigger::collectJointsForLimb(int markIndex, std::vector<int> &jointM
if (triangleToMarkMap.size() <= 1) { if (triangleToMarkMap.size() <= 1) {
qDebug() << "Collect joints for limb failed because of lack marks"; qDebug() << "Collect joints for limb failed because of lack marks";
return; return true;
} }
const auto &group = mark.smallGroup(); const auto &group = mark.smallGroup();
if (group.empty()) { if (group.empty()) {
qDebug() << "Collect joints for limb failed because of lack verticies"; qDebug() << "Collect joints for limb failed because of lack verticies";
return; return false;
} }
// Build the edge to triangle map; // Build the edge to triangle map;
@ -85,7 +86,7 @@ void GenericRigger::collectJointsForLimb(int markIndex, std::vector<int> &jointM
if (waitTriangles.empty()) { if (waitTriangles.empty()) {
qDebug() << "Couldn't find a triangle to start"; qDebug() << "Couldn't find a triangle to start";
return; return false;
} }
// Traverse all the triangles and fill the triangle to mark map // Traverse all the triangles and fill the triangle to mark map
@ -163,7 +164,7 @@ void GenericRigger::collectJointsForLimb(int markIndex, std::vector<int> &jointM
} }
if (-1 == nearestMarkIndex) { if (-1 == nearestMarkIndex) {
qDebug() << "Find nearest joint failed"; qDebug() << "Find nearest joint failed";
return; return false;
} }
jointMarkIndicies.push_back(nearestMarkIndex); jointMarkIndicies.push_back(nearestMarkIndex);
visited.insert(nearestMarkIndex); visited.insert(nearestMarkIndex);
@ -185,9 +186,11 @@ void GenericRigger::collectJointsForLimb(int markIndex, std::vector<int> &jointM
break; break;
findPairResult = pairs.find(linkTo); findPairResult = pairs.find(linkTo);
} }
return true;
} }
bool GenericRigger::rig() bool AnimalRigger::rig()
{ {
if (!validate()) if (!validate())
return false; return false;
@ -205,27 +208,44 @@ bool GenericRigger::rig()
isMainBodyVerticalAligned = fabs(yMax.y() - yMin.y()) > fabs(zMax.z() - zMin.z()); isMainBodyVerticalAligned = fabs(yMax.y() - yMin.y()) > fabs(zMax.z() - zMin.z());
} }
// Collect all limbs // Collect all branchs
auto nosideLimbIndicies = m_marksMap.find(std::make_pair(BoneMark::Limb, SkeletonSide::None)); auto neckIndicies = m_marksMap.find(std::make_pair(BoneMark::Neck, SkeletonSide::None));
auto tailIndicies = m_marksMap.find(std::make_pair(BoneMark::Tail, SkeletonSide::None));
auto leftLimbIndicies = m_marksMap.find(std::make_pair(BoneMark::Limb, SkeletonSide::Left)); auto leftLimbIndicies = m_marksMap.find(std::make_pair(BoneMark::Limb, SkeletonSide::Left));
auto rightLimbIndicies = m_marksMap.find(std::make_pair(BoneMark::Limb, SkeletonSide::Right)); auto rightLimbIndicies = m_marksMap.find(std::make_pair(BoneMark::Limb, SkeletonSide::Right));
std::vector<int> nosideMarkIndicies;
std::vector<int> leftMarkIndicies;
std::vector<int> righMarkIndicies;
if (neckIndicies != m_marksMap.end()) {
for (const auto &index: neckIndicies->second)
nosideMarkIndicies.push_back(index);
}
if (tailIndicies != m_marksMap.end()) {
for (const auto &index: tailIndicies->second)
nosideMarkIndicies.push_back(index);
}
if (leftLimbIndicies != m_marksMap.end()) {
for (const auto &index: leftLimbIndicies->second)
leftMarkIndicies.push_back(index);
}
if (rightLimbIndicies != m_marksMap.end()) {
for (const auto &index: rightLimbIndicies->second)
righMarkIndicies.push_back(index);
}
// Generate spine for main body // Generate spine for main body
auto sortLimbIndiciesInSpineOrder = [=](const int &first, const int &second) { auto sortMarkIndiciesInSpineOrder = [=](const int &first, const int &second) {
return isMainBodyVerticalAligned ? return isMainBodyVerticalAligned ?
(m_marks[first].bonePosition.y() < m_marks[second].bonePosition.y()) : (m_marks[first].bonePosition.y() < m_marks[second].bonePosition.y()) :
(m_marks[first].bonePosition.z() < m_marks[second].bonePosition.z()); (m_marks[first].bonePosition.z() < m_marks[second].bonePosition.z());
}; };
if (nosideLimbIndicies != m_marksMap.end()) std::sort(nosideMarkIndicies.begin(), nosideMarkIndicies.end(), sortMarkIndiciesInSpineOrder);
std::sort(nosideLimbIndicies->second.begin(), nosideLimbIndicies->second.end(), sortLimbIndiciesInSpineOrder); std::sort(leftMarkIndicies.begin(), leftMarkIndicies.end(), sortMarkIndiciesInSpineOrder);
if (leftLimbIndicies != m_marksMap.end()) std::sort(righMarkIndicies.begin(), righMarkIndicies.end(), sortMarkIndiciesInSpineOrder);
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 static std::vector<int> s_empty;
const std::vector<int> *limbColumns[3] = {leftLimbIndicies != m_marksMap.end() ? &leftLimbIndicies->second : &s_empty, const std::vector<int> *chainColumns[3] = {&leftMarkIndicies,
nosideLimbIndicies != m_marksMap.end() ? &nosideLimbIndicies->second : &s_empty, &nosideMarkIndicies,
rightLimbIndicies != m_marksMap.end() ? &rightLimbIndicies->second : &s_empty &righMarkIndicies
}; };
enum Column enum Column
{ {
@ -237,18 +257,18 @@ bool GenericRigger::rig()
{ {
float coord; float coord;
QVector3D position; QVector3D position;
std::set<int> limbMarkIndicies; std::set<int> chainMarkIndicies;
}; };
std::vector<SpineNode> spineNodes; std::vector<SpineNode> rawSpineNodes;
for (size_t sideIndicies[3] = {0, 0, 0}; for (size_t sideIndicies[3] = {0, 0, 0};
sideIndicies[Column::Noside] < limbColumns[Column::Noside]->size() || sideIndicies[Column::Noside] < chainColumns[Column::Noside]->size() ||
sideIndicies[Column::Left] < limbColumns[Column::Left]->size() || sideIndicies[Column::Left] < chainColumns[Column::Left]->size() ||
sideIndicies[Column::Right] < limbColumns[Column::Right]->size();) { sideIndicies[Column::Right] < chainColumns[Column::Right]->size();) {
float choosenCoord = std::numeric_limits<float>::max(); float choosenCoord = std::numeric_limits<float>::max();
int choosenColumn = -1; int choosenColumn = -1;
for (size_t side = Column::Left; side <= Column::Right; ++side) { for (size_t side = Column::Left; side <= Column::Right; ++side) {
if (sideIndicies[side] < limbColumns[side]->size()) { if (sideIndicies[side] < chainColumns[side]->size()) {
const auto &mark = m_marks[limbColumns[side]->at(sideIndicies[side])]; const auto &mark = m_marks[chainColumns[side]->at(sideIndicies[side])];
const auto &coord = isMainBodyVerticalAligned ? mark.bonePosition.y() : const auto &coord = isMainBodyVerticalAligned ? mark.bonePosition.y() :
mark.bonePosition.z(); mark.bonePosition.z();
if (coord < choosenCoord) { if (coord < choosenCoord) {
@ -261,42 +281,69 @@ bool GenericRigger::rig()
qDebug() << "Should not come here, coord corrupted"; qDebug() << "Should not come here, coord corrupted";
break; break;
} }
// Find all the limbs before or near this choosenCoord // Find all the chains before or near this choosenCoord
QVector3D sumOfLimbPositions; QVector3D sumOfChainPositions;
int countOfLimbs = 0; int countOfChains = 0;
std::set<int> limbMarkIndicies; std::set<int> chainMarkIndicies;
for (size_t side = Column::Left; side <= Column::Right; ++side) { for (size_t side = Column::Left; side <= Column::Right; ++side) {
if (sideIndicies[side] < limbColumns[side]->size()) { if (sideIndicies[side] < chainColumns[side]->size()) {
const auto &mark = m_marks[limbColumns[side]->at(sideIndicies[side])]; const auto &mark = m_marks[chainColumns[side]->at(sideIndicies[side])];
const auto &coord = isMainBodyVerticalAligned ? mark.bonePosition.y() : const auto &coord = isMainBodyVerticalAligned ? mark.bonePosition.y() :
mark.bonePosition.z(); mark.bonePosition.z();
if (coord <= choosenCoord + 0.001) { if (coord <= choosenCoord + 0.001) {
limbMarkIndicies.insert(limbColumns[side]->at(sideIndicies[side])); chainMarkIndicies.insert(chainColumns[side]->at(sideIndicies[side]));
sumOfLimbPositions += mark.bonePosition; sumOfChainPositions += mark.bonePosition;
++countOfLimbs; ++countOfChains;
++sideIndicies[side]; ++sideIndicies[side];
} }
} }
} }
if (countOfLimbs <= 0) { if (countOfChains <= 0) {
qDebug() << "Should not come here, there must be at least one limb"; qDebug() << "Should not come here, there must be at least one limb";
break; break;
} }
//qDebug() << "Create new spine node from" << countOfLimbs << "limbs current coord:" << choosenCoord; rawSpineNodes.push_back(SpineNode());
SpineNode &spineNode = rawSpineNodes.back();
spineNodes.push_back(SpineNode());
SpineNode &spineNode = spineNodes.back();
spineNode.coord = choosenCoord; spineNode.coord = choosenCoord;
spineNode.limbMarkIndicies = limbMarkIndicies; spineNode.chainMarkIndicies = chainMarkIndicies;
spineNode.position = sumOfLimbPositions / countOfLimbs; spineNode.position = sumOfChainPositions / countOfChains;
} }
if (spineNodes.empty()) { if (rawSpineNodes.empty()) {
qDebug() << "Couldn't find limbs to create a spine"; qDebug() << "Couldn't find limbs to create a spine";
return false; return false;
} }
// Reassemble spine nodes, each spine will be cut off as two
std::vector<SpineNode> spineNodes;
for (size_t i = 0; i < rawSpineNodes.size(); ++i) {
const auto &raw = rawSpineNodes[i];
spineNodes.push_back(raw);
if (i + 1 < rawSpineNodes.size()) {
SpineNode intermediate;
const auto &nextRaw = rawSpineNodes[i + 1];
intermediate.coord = (raw.coord + nextRaw.coord) / 2;
intermediate.position = (raw.position + nextRaw.position) / 2;
spineNodes.push_back(intermediate);
}
}
// Move the chain mark indicies to the new generated intermediate spine
for (size_t i = 2; i < spineNodes.size(); i += 2) {
auto &spineNode = spineNodes[i];
std::vector<int> needMoveIndicies;
for (const auto &markIndex: spineNode.chainMarkIndicies) {
const auto &chain = m_marks[markIndex];
if (chain.boneSide != SkeletonSide::None)
needMoveIndicies.push_back(markIndex);
}
auto &previousSpineNode = spineNodes[i - 1];
for (const auto &markIndex: needMoveIndicies) {
previousSpineNode.chainMarkIndicies.insert(markIndex);
spineNode.chainMarkIndicies.erase(markIndex);
}
}
std::map<QString, int> boneIndexMap; std::map<QString, int> boneIndexMap;
m_resultBones.push_back(RiggerBone()); m_resultBones.push_back(RiggerBone());
@ -304,47 +351,54 @@ bool GenericRigger::rig()
bodyBone.index = m_resultBones.size() - 1; bodyBone.index = m_resultBones.size() - 1;
bodyBone.name = Rigger::rootBoneName; bodyBone.name = Rigger::rootBoneName;
bodyBone.headPosition = QVector3D(0, 0, 0); bodyBone.headPosition = QVector3D(0, 0, 0);
bodyBone.hasButton = true;
bodyBone.button = {spineNodes.size(), 0};
bodyBone.buttonParameterType = RiggerButtonParameterType::Translation;
boneIndexMap[bodyBone.name] = bodyBone.index; boneIndexMap[bodyBone.name] = bodyBone.index;
auto remainingSpineVerticies = bodyVerticies; auto remainingSpineVerticies = bodyVerticies;
const std::vector<QColor> twoColorsForSpine = {QColor(0x57, 0x43, 0x98), Qt::white}; const std::vector<QColor> twoColorsForSpine = {BoneMarkToColor(BoneMark::Neck), BoneMarkToColor(BoneMark::Tail)};
const std::vector<QColor> twoColorsForLimb = {BoneMarkToColor(BoneMark::Joint), BoneMarkToColor(BoneMark::Hip)}; const std::vector<QColor> twoColorsForLimb = {BoneMarkToColor(BoneMark::Joint), BoneMarkToColor(BoneMark::Limb)};
int spineGenerateOrder = 1; int spineGenerateOrder = 1;
std::map<std::pair<QString, SkeletonSide>, int> chainOrderMapBySide;
for (int spineNodeIndex = 0; spineNodeIndex < (int)spineNodes.size(); ++spineNodeIndex) { for (int spineNodeIndex = 0; spineNodeIndex < (int)spineNodes.size(); ++spineNodeIndex) {
const auto &spineNode = spineNodes[spineNodeIndex]; const auto &spineNode = spineNodes[spineNodeIndex];
std::set<int> spineBoneVertices; std::set<int> spineBoneVertices;
QVector3D tailPosition; QVector3D tailPosition;
int buttonRow = spineNodes.size() - spineGenerateOrder;
if (spineNodeIndex + 1 < (int)spineNodes.size()) { if (spineNodeIndex + 1 < (int)spineNodes.size()) {
std::set<int> frontOrCoincidentVertices;
std::set<int> backVertices;
float distance = (spineNodes[spineNodeIndex + 1].position - spineNode.position).length(); float distance = (spineNodes[spineNodeIndex + 1].position - spineNode.position).length();
auto planeNormal = (spineNodes[spineNodeIndex + 1].position - spineNode.position).normalized(); QVector3D currentSpineDirection = (spineNodes[spineNodeIndex + 1].position - spineNode.position).normalized();
QVector3D previousSpineDirection = currentSpineDirection;
if (spineNodeIndex - 1 >= 0) {
previousSpineDirection = (spineNodes[spineNodeIndex].position - spineNodes[spineNodeIndex - 1].position).normalized();
}
auto planeNormal = (currentSpineDirection + previousSpineDirection).normalized();
auto pointOnPlane = spineNode.position + planeNormal * distance * 1.25; auto pointOnPlane = spineNode.position + planeNormal * distance * 1.25;
auto perpVector = isMainBodyVerticalAligned ? QVector3D(1, 0, 0) : QVector3D(0, 1, 0); auto perpVector = isMainBodyVerticalAligned ? QVector3D(1, 0, 0) : QVector3D(0, 1, 0);
auto vectorOnPlane = QVector3D::crossProduct(planeNormal, perpVector); auto vectorOnPlane = QVector3D::crossProduct(planeNormal, perpVector);
// Move this point to far away, so the checking vector will not collapse with the plane normal // Move this point to far away, so the checking vector will not collapse with the plane normal
pointOnPlane += vectorOnPlane.normalized() * 1000; pointOnPlane += vectorOnPlane.normalized() * 1000;
splitVerticesByPlane(remainingSpineVerticies, {
pointOnPlane, std::set<int> frontOrCoincidentVertices;
planeNormal, std::set<int> backVertices;
frontOrCoincidentVertices, splitVerticesByPlane(remainingSpineVerticies,
backVertices); pointOnPlane,
spineBoneVertices = backVertices; planeNormal,
frontOrCoincidentVertices,
backVertices);
spineBoneVertices = backVertices;
}
// Split again, this time, we step back a little bit // Split again, this time, we step back a little bit
pointOnPlane = spineNode.position + planeNormal * distance * 0.85; pointOnPlane = spineNode.position + planeNormal * distance * 0.85;
pointOnPlane += vectorOnPlane.normalized() * 1000; pointOnPlane += vectorOnPlane.normalized() * 1000;
splitVerticesByPlane(remainingSpineVerticies, {
pointOnPlane, std::set<int> frontOrCoincidentVertices;
planeNormal, std::set<int> backVertices;
frontOrCoincidentVertices, splitVerticesByPlane(remainingSpineVerticies,
backVertices); pointOnPlane,
remainingSpineVerticies = frontOrCoincidentVertices; planeNormal,
frontOrCoincidentVertices,
backVertices);
remainingSpineVerticies = frontOrCoincidentVertices;
}
tailPosition = spineNodes[spineNodeIndex + 1].position; tailPosition = spineNodes[spineNodeIndex + 1].position;
} else { } else {
spineBoneVertices = remainingSpineVerticies; spineBoneVertices = remainingSpineVerticies;
@ -362,16 +416,15 @@ bool GenericRigger::rig()
spineBoneHeadPosition.setZ(spineNode.coord); spineBoneHeadPosition.setZ(spineNode.coord);
} }
QString spineName = namingSpine(spineGenerateOrder);
m_resultBones.push_back(RiggerBone()); m_resultBones.push_back(RiggerBone());
RiggerBone &spineBone = m_resultBones.back(); RiggerBone &spineBone = m_resultBones.back();
spineBone.index = m_resultBones.size() - 1; spineBone.index = m_resultBones.size() - 1;
spineBone.name = "Spine" + QString::number(spineGenerateOrder); spineBone.name = spineName;
spineBone.headPosition = spineBoneHeadPosition; spineBone.headPosition = spineBoneHeadPosition;
spineBone.tailPosition = tailPosition; spineBone.tailPosition = tailPosition;
spineBone.color = twoColorsForSpine[spineGenerateOrder % 2]; spineBone.color = twoColorsForSpine[spineGenerateOrder % 2];
spineBone.hasButton = true;
spineBone.button = {buttonRow, 0};
spineBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll;
addVerticesToWeights(spineBoneVertices, spineBone.index); addVerticesToWeights(spineBoneVertices, spineBone.index);
boneIndexMap[spineBone.name] = spineBone.index; boneIndexMap[spineBone.name] = spineBone.index;
@ -381,125 +434,149 @@ bool GenericRigger::rig()
m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition = spineBone.headPosition; m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition = spineBone.headPosition;
m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(spineBone.index); m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(spineBone.index);
} else { } else {
m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder - 1)]].tailPosition = spineBone.headPosition; m_resultBones[boneIndexMap[namingSpine(spineGenerateOrder - 1)]].tailPosition = spineBone.headPosition;
m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder - 1)]].children.push_back(spineBone.index); m_resultBones[boneIndexMap[namingSpine(spineGenerateOrder - 1)]].children.push_back(spineBone.index);
} }
int limbGenerateOrder = 1; for (const auto &chainMarkIndex: spineNode.chainMarkIndicies) {
for (const auto &limbMarkIndex: spineNode.limbMarkIndicies) { const auto &chainMark = m_marks[chainMarkIndex];
const auto &limbMark = m_marks[limbMarkIndex];
QString chainBaseName = BoneMarkToString(chainMark.boneMark);
int chainGenerateOrder = ++chainOrderMapBySide[{chainBaseName, chainMark.boneSide}];
QString chainName = namingChainPrefix(chainBaseName, chainMark.boneSide, chainGenerateOrder, spineNode.chainMarkIndicies.size());
m_resultBones.push_back(RiggerBone()); m_resultBones.push_back(RiggerBone());
RiggerBone &ribBone = m_resultBones.back(); RiggerBone &ribBone = m_resultBones.back();
ribBone.index = m_resultBones.size() - 1; ribBone.index = m_resultBones.size() - 1;
ribBone.name = "Rib" + QString::number(spineGenerateOrder) + "x" + QString::number(limbGenerateOrder); ribBone.name = namingConnector(spineName, chainName);
ribBone.headPosition = spineBoneHeadPosition; ribBone.headPosition = spineBoneHeadPosition;
boneIndexMap[ribBone.name] = ribBone.index; boneIndexMap[ribBone.name] = ribBone.index;
if (1 == spineGenerateOrder) { if (1 == spineGenerateOrder) {
m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(ribBone.index); m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(ribBone.index);
} else { } else {
m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder)]].children.push_back(ribBone.index); m_resultBones[boneIndexMap[namingSpine(spineGenerateOrder)]].children.push_back(ribBone.index);
} }
std::vector<int> jointMarkIndicies; std::vector<int> jointMarkIndicies;
collectJointsForLimb(limbMarkIndex, jointMarkIndicies); if (!collectJontsForChain(chainMarkIndex, jointMarkIndicies)) {
m_jointErrorItems.push_back(chainName);
}
//qDebug() << "Limb markIndex:" << limbMarkIndex << " joints:" << jointMarkIndicies.size(); //qDebug() << "Limb markIndex:" << limbMarkIndex << " joints:" << jointMarkIndicies.size();
int jointGenerateOrder = 1; int jointGenerateOrder = 1;
auto boneColor = [&]() { auto boneColor = [&]() {
return twoColorsForLimb[(jointGenerateOrder + 1) % 2]; return twoColorsForLimb[jointGenerateOrder % 2];
};
auto boneColumn = [&]() {
return limbMark.boneSide == SkeletonSide::Left ? jointGenerateOrder : -jointGenerateOrder;
}; };
auto addToParentBone = [&](QVector3D headPosition, SkeletonSide side, int boneIndex) { auto addToParentBone = [&](QVector3D headPosition, SkeletonSide side, int boneIndex) {
if (1 == jointGenerateOrder) { if (1 == jointGenerateOrder) {
m_resultBones[boneIndexMap["Rib" + QString::number(spineGenerateOrder) + "x" + QString::number(limbGenerateOrder)]].tailPosition = headPosition; m_resultBones[boneIndexMap[namingConnector(spineName, chainName)]].tailPosition = headPosition;
m_resultBones[boneIndexMap["Rib" + QString::number(spineGenerateOrder) + "x" + QString::number(limbGenerateOrder)]].children.push_back(boneIndex); m_resultBones[boneIndexMap[namingConnector(spineName, chainName)]].children.push_back(boneIndex);
} else { } else {
QString parentLimbBoneName = namingLimb(spineGenerateOrder, side, limbGenerateOrder, jointGenerateOrder - 1); QString parentLimbBoneName = namingChain(chainBaseName, side, chainGenerateOrder, spineNode.chainMarkIndicies.size(), jointGenerateOrder - 1);
m_resultBones[boneIndexMap[parentLimbBoneName]].tailPosition = headPosition; m_resultBones[boneIndexMap[parentLimbBoneName]].tailPosition = headPosition;
m_resultBones[boneIndexMap[parentLimbBoneName]].children.push_back(boneIndex); m_resultBones[boneIndexMap[parentLimbBoneName]].children.push_back(boneIndex);
} }
}; };
std::set<int> remainingLimbVertices; std::set<int> remainingLimbVertices;
addTrianglesToVertices(limbMark.smallGroup(), remainingLimbVertices); addTrianglesToVertices(chainMark.smallGroup(), remainingLimbVertices);
addTrianglesToVertices(limbMark.markTriangles, remainingLimbVertices); addTrianglesToVertices(chainMark.markTriangles, remainingLimbVertices);
std::vector<QVector3D> jointPositions;
for (jointGenerateOrder = 1; jointGenerateOrder <= (int)jointMarkIndicies.size(); ++jointGenerateOrder) {
int jointMarkIndex = jointMarkIndicies[jointGenerateOrder - 1];
const auto jointMark = m_marks[jointMarkIndex];
jointPositions.push_back(jointMark.bonePosition);
}
std::set<int> lastJointBoneVerticies;
if (jointPositions.size() >= 2)
{
QVector3D cutoffPlaneNormal = (jointPositions[jointPositions.size() - 1] - jointPositions[jointPositions.size() - 2]).normalized();
QVector3D pointOnPlane = jointPositions[jointPositions.size() - 1];
std::set<int> frontOrCoincidentVertices;
std::set<int> backVertices;
splitVerticesByPlane(remainingLimbVertices,
pointOnPlane,
cutoffPlaneNormal,
frontOrCoincidentVertices,
backVertices);
lastJointBoneVerticies = frontOrCoincidentVertices;
} else {
lastJointBoneVerticies = remainingLimbVertices;
}
// Calculate the tail position from remaining verticies
std::vector<QVector3D> extremCoords(6, jointPositions.back());
resolveBoundingBox(lastJointBoneVerticies, extremCoords[0], extremCoords[1], extremCoords[2], extremCoords[3], extremCoords[4], extremCoords[5]);
float maxDistance2 = std::numeric_limits<float>::min();
QVector3D choosenExtreamCoord = jointPositions.back();
for (size_t i = 0; i < 6; ++i) {
const auto &position = extremCoords[i];
auto length2 = (position - jointPositions.back()).lengthSquared();
if (length2 >= maxDistance2) {
maxDistance2 = length2;
choosenExtreamCoord = position;
}
}
jointPositions.push_back(choosenExtreamCoord);
QVector3D lastPosition = spineBoneHeadPosition; QVector3D lastPosition = spineBoneHeadPosition;
for (const auto &jointMarkIndex: jointMarkIndicies) { for (jointGenerateOrder = 1; jointGenerateOrder <= (int)jointMarkIndicies.size(); ++jointGenerateOrder) {
const auto jointMark = m_marks[jointMarkIndex]; int jointMarkIndex = jointMarkIndicies[jointGenerateOrder - 1];
int buttonColumn = boneColumn(); const auto &jointMark = m_marks[jointMarkIndex];
m_resultBones.push_back(RiggerBone()); m_resultBones.push_back(RiggerBone());
RiggerBone &limbBone = m_resultBones.back(); RiggerBone &jointBone = m_resultBones.back();
limbBone.index = m_resultBones.size() - 1; jointBone.index = m_resultBones.size() - 1;
limbBone.name = namingLimb(spineGenerateOrder, jointMark.boneSide, limbGenerateOrder, jointGenerateOrder); jointBone.name = namingChain(chainBaseName, chainMark.boneSide, chainGenerateOrder, spineNode.chainMarkIndicies.size(), jointGenerateOrder);
limbBone.headPosition = jointMark.bonePosition; jointBone.headPosition = jointPositions[jointGenerateOrder - 1];
limbBone.baseNormal = jointMark.baseNormal; jointBone.tailPosition = jointPositions[jointGenerateOrder];
limbBone.color = boneColor(); jointBone.baseNormal = jointMark.baseNormal;
limbBone.hasButton = true; jointBone.color = boneColor();
limbBone.button = {buttonRow, buttonColumn};
limbBone.buttonParameterType = jointGenerateOrder == 1 ? RiggerButtonParameterType::PitchYawRoll : RiggerButtonParameterType::Intersection;
if (jointGenerateOrder == (int)jointMarkIndicies.size()) { if (jointGenerateOrder == (int)jointMarkIndicies.size()) {
// Calculate the tail position from remaining verticies addVerticesToWeights(remainingLimbVertices, jointBone.index);
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 { } else {
std::set<int> frontOrCoincidentVertices; int nextJointMarkIndex = jointMarkIndicies[jointGenerateOrder];
std::set<int> backVertices; const auto &nextJointMark = m_marks[nextJointMarkIndex];
limbBone.tailPosition = m_marks[jointMarkIndicies[jointGenerateOrder]].bonePosition; auto nextBoneDirection = (jointPositions[jointGenerateOrder + 1] - jointPositions[jointGenerateOrder]).normalized();
auto previousBoneDirection = (limbBone.headPosition - lastPosition).normalized(); auto currentBoneDirection = (jointBone.tailPosition - jointBone.headPosition).normalized();
auto currentBoneDirection = (limbBone.tailPosition - limbBone.headPosition).normalized(); auto planeNormal = (currentBoneDirection + nextBoneDirection).normalized();
auto planeNormal = (previousBoneDirection + currentBoneDirection).normalized(); auto pointOnPlane = jointBone.tailPosition + planeNormal * nextJointMark.nodeRadius;
float previousBoneLength = (limbBone.headPosition - lastPosition).length(); {
float currentBoneLength = (limbBone.tailPosition - limbBone.headPosition).length(); std::set<int> frontOrCoincidentVertices;
auto pointOnPlane = limbBone.tailPosition + currentBoneDirection * currentBoneLength * 0.25; std::set<int> backVertices;
splitVerticesByPlane(remainingLimbVertices, splitVerticesByPlane(remainingLimbVertices,
pointOnPlane, pointOnPlane,
planeNormal, planeNormal,
frontOrCoincidentVertices, frontOrCoincidentVertices,
backVertices); backVertices);
addVerticesToWeights(backVertices, limbBone.index); addVerticesToWeights(backVertices, jointBone.index);
pointOnPlane = limbBone.tailPosition - previousBoneDirection * previousBoneLength * 0.1 * (currentBoneLength / std::max(previousBoneLength, (float)0.00001)); }
splitVerticesByPlane(remainingLimbVertices, pointOnPlane = jointBone.tailPosition - planeNormal * nextJointMark.nodeRadius;
pointOnPlane, {
planeNormal, std::set<int> frontOrCoincidentVertices;
frontOrCoincidentVertices, std::set<int> backVertices;
backVertices); splitVerticesByPlane(remainingLimbVertices,
remainingLimbVertices = frontOrCoincidentVertices; pointOnPlane,
planeNormal,
frontOrCoincidentVertices,
backVertices);
remainingLimbVertices = frontOrCoincidentVertices;
}
} }
boneIndexMap[limbBone.name] = limbBone.index; boneIndexMap[jointBone.name] = jointBone.index;
addToParentBone(limbBone.headPosition, jointMark.boneSide, limbBone.index); addToParentBone(jointBone.headPosition, chainMark.boneSide, jointBone.index);
lastPosition = jointMark.bonePosition; lastPosition = jointPositions[jointGenerateOrder - 1];
++jointGenerateOrder;
} }
++limbGenerateOrder; ++chainGenerateOrder;
} }
++spineGenerateOrder; ++spineGenerateOrder;
} }
normalizeButtonColumns();
// Finalize weights // Finalize weights
for (auto &weights: m_resultWeights) { for (auto &weights: m_resultWeights) {
weights.second.finalizeWeights(); weights.second.finalizeWeights();
@ -508,27 +585,22 @@ bool GenericRigger::rig()
return true; return true;
} }
void GenericRigger::normalizeButtonColumns() QString AnimalRigger::namingChain(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide, int jointOrder)
{ {
double minColumn = std::numeric_limits<int>::max(); return namingChainPrefix(baseName, side, orderInSide, totalInSide) + "_Joint" + QString::number(jointOrder);
double 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 = (int)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) QString AnimalRigger::namingSpine(int spineOrder)
{ {
return SkeletonSideToDispName(side) + "Limb" + QString::number(spineOrder) + "x" + QString::number(jointOrder); return "Spine" + QString::number(spineOrder);
}
QString AnimalRigger::namingConnector(const QString &spineName, const QString &chainName)
{
return "Virtual_" + spineName + "_" + chainName;
}
QString AnimalRigger::namingChainPrefix(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide)
{
return SkeletonSideToDispName(side) + baseName + (totalInSide == 1 ? QString() : QString::number(orderInSide));
} }

28
src/animalrigger.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef DUST3D_ANIMAL_RIGGER_H
#define DUST3D_ANIMAL_RIGGER_H
#include <QVector3D>
#include <vector>
#include <set>
#include "rigger.h"
#include "meshsplitter.h"
class AnimalRigger: public Rigger
{
Q_OBJECT
public:
AnimalRigger(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:
bool collectJontsForChain(int markIndex, std::vector<int> &jointMarkIndicies);
QString namingSpine(int spineOrder);
QString namingConnector(const QString &spineName, const QString &chainName);
QString namingChain(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide, int jointOrder);
QString namingChainPrefix(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide);
};
#endif

View File

@ -7,40 +7,25 @@ enum class BoneMark
{ {
None = 0, None = 0,
Neck, Neck,
Shoulder,
Elbow,
Wrist,
Hip,
Knee,
Ankle,
Limb, Limb,
Tail,
Joint, Joint,
Count Count
}; };
#define BoneMarkHasSide(mark) ((mark) != BoneMark::Neck) #define BoneMarkHasSide(mark) ((mark) == BoneMark::Limb)
QColor BoneMarkToColor(BoneMark mark); QColor BoneMarkToColor(BoneMark mark);
#define IMPL_BoneMarkToColor \ #define IMPL_BoneMarkToColor \
QColor BoneMarkToColor(BoneMark mark) \ QColor BoneMarkToColor(BoneMark mark) \
{ \ { \
switch (mark) { \ switch (mark) { \
case BoneMark::Neck: \ case BoneMark::Neck: \
return QColor(0xfc, 0x0d, 0x1b); \ return QColor(0x51, 0xba, 0xf2); \
case BoneMark::Shoulder: \
return QColor(0xfd, 0x80, 0x23); \
case BoneMark::Elbow: \
return QColor(0x29, 0xfd, 0x2f); \
case BoneMark::Wrist: \
return QColor(0xff, 0xfd, 0x38); \
case BoneMark::Hip: \
return QColor(0x2c, 0xff, 0xfe); \
case BoneMark::Knee: \
return QColor(0x0b, 0x24, 0xfb); \
case BoneMark::Ankle: \
return QColor(0xfc, 0x28, 0xfc); \
case BoneMark::Limb: \ case BoneMark::Limb: \
return QColor(0xf7, 0xf7, 0xf7); \ return QColor(0x29, 0xfd, 0x2f); \
case BoneMark::Tail: \
return QColor(0xff, 0xfd, 0x38); \
case BoneMark::Joint: \ case BoneMark::Joint: \
return QColor(0xfd, 0xa9, 0xaa); \ return QColor(0xcf, 0x83, 0xe1); \
case BoneMark::None: \ case BoneMark::None: \
return Qt::transparent; \ return Qt::transparent; \
default: \ default: \
@ -54,20 +39,10 @@ const char *BoneMarkToString(BoneMark mark) \
switch (mark) { \ switch (mark) { \
case BoneMark::Neck: \ case BoneMark::Neck: \
return "Neck"; \ return "Neck"; \
case BoneMark::Shoulder: \
return "Shoulder"; \
case BoneMark::Elbow: \
return "Elbow"; \
case BoneMark::Wrist: \
return "Wrist"; \
case BoneMark::Hip: \
return "Hip"; \
case BoneMark::Knee: \
return "Knee"; \
case BoneMark::Ankle: \
return "Ankle"; \
case BoneMark::Limb: \ case BoneMark::Limb: \
return "Limb"; \ return "Limb"; \
case BoneMark::Tail: \
return "Tail"; \
case BoneMark::Joint: \ case BoneMark::Joint: \
return "Joint"; \ return "Joint"; \
case BoneMark::None: \ case BoneMark::None: \
@ -83,20 +58,10 @@ BoneMark BoneMarkFromString(const char *markString) \
QString mark = markString; \ QString mark = markString; \
if (mark == "Neck") \ if (mark == "Neck") \
return BoneMark::Neck; \ return BoneMark::Neck; \
if (mark == "Shoulder") \
return BoneMark::Shoulder; \
if (mark == "Elbow") \
return BoneMark::Elbow; \
if (mark == "Wrist") \
return BoneMark::Wrist; \
if (mark == "Hip") \
return BoneMark::Hip; \
if (mark == "Knee") \
return BoneMark::Knee; \
if (mark == "Ankle") \
return BoneMark::Ankle; \
if (mark == "Limb") \ if (mark == "Limb") \
return BoneMark::Limb; \ return BoneMark::Limb; \
if (mark == "Tail") \
return BoneMark::Tail; \
if (mark == "Joint") \ if (mark == "Joint") \
return BoneMark::Joint; \ return BoneMark::Joint; \
return BoneMark::None; \ return BoneMark::None; \
@ -108,22 +73,12 @@ QString BoneMarkToDispName(BoneMark mark) \
switch (mark) { \ switch (mark) { \
case BoneMark::Neck: \ case BoneMark::Neck: \
return QObject::tr("Neck"); \ return QObject::tr("Neck"); \
case BoneMark::Shoulder: \
return QObject::tr("Shoulder (Arm Start)"); \
case BoneMark::Elbow: \
return QObject::tr("Elbow"); \
case BoneMark::Wrist: \
return QObject::tr("Wrist"); \
case BoneMark::Hip: \
return QObject::tr("Hip (Leg Start)"); \
case BoneMark::Knee: \
return QObject::tr("Knee"); \
case BoneMark::Ankle: \
return QObject::tr("Ankle"); \
case BoneMark::Limb: \ case BoneMark::Limb: \
return QObject::tr("Limb (Generic)"); \ return QObject::tr("Limb"); \
case BoneMark::Tail: \
return QObject::tr("Tail"); \
case BoneMark::Joint: \ case BoneMark::Joint: \
return QObject::tr("Joint (Generic)"); \ return QObject::tr("Joint"); \
case BoneMark::None: \ case BoneMark::None: \
return QObject::tr("None"); \ return QObject::tr("None"); \
default: \ default: \

View File

@ -2559,8 +2559,7 @@ void Document::rigReady()
delete m_resultRigWeights; delete m_resultRigWeights;
m_resultRigWeights = m_rigGenerator->takeResultWeights(); m_resultRigWeights = m_rigGenerator->takeResultWeights();
m_resultRigMissingMarkNames = m_rigGenerator->missingMarkNames(); m_resultRigMessages = m_rigGenerator->messages();
m_resultRigErrorMarkNames = m_rigGenerator->errorMarkNames();
delete m_riggedOutcome; delete m_riggedOutcome;
m_riggedOutcome = m_rigGenerator->takeOutcome(); m_riggedOutcome = m_rigGenerator->takeOutcome();
@ -2602,8 +2601,7 @@ void Document::removeRigResults()
delete m_resultRigWeightMesh; delete m_resultRigWeightMesh;
m_resultRigWeightMesh = nullptr; m_resultRigWeightMesh = nullptr;
m_resultRigErrorMarkNames.clear(); m_resultRigMessages.clear();
m_resultRigMissingMarkNames.clear();
m_currentRigSucceed = false; m_currentRigSucceed = false;
@ -2625,14 +2623,9 @@ void Document::setRigType(RigType toRigType)
emit rigChanged(); emit rigChanged();
} }
const std::vector<QString> &Document::resultRigMissingMarkNames() const const std::vector<std::pair<QtMsgType, QString>> &Document::resultRigMessages() const
{ {
return m_resultRigMissingMarkNames; return m_resultRigMessages;
}
const std::vector<QString> &Document::resultRigErrorMarkNames() const
{
return m_resultRigErrorMarkNames;
} }
const Outcome &Document::currentRiggedOutcome() const const Outcome &Document::currentRiggedOutcome() const

View File

@ -499,8 +499,7 @@ public:
bool isPostProcessResultObsolete() const; bool isPostProcessResultObsolete() const;
void collectComponentDescendantParts(QUuid componentId, std::vector<QUuid> &partIds) const; void collectComponentDescendantParts(QUuid componentId, std::vector<QUuid> &partIds) const;
void collectComponentDescendantComponents(QUuid componentId, std::vector<QUuid> &componentIds) const; void collectComponentDescendantComponents(QUuid componentId, std::vector<QUuid> &componentIds) const;
const std::vector<QString> &resultRigMissingMarkNames() const; const std::vector<std::pair<QtMsgType, QString>> &resultRigMessages() const;
const std::vector<QString> &resultRigErrorMarkNames() const;
const Outcome &currentRiggedOutcome() const; const Outcome &currentRiggedOutcome() const;
bool currentRigSucceed() const; bool currentRigSucceed() const;
bool isMeshGenerating() const; bool isMeshGenerating() const;
@ -652,8 +651,7 @@ private:
std::deque<HistoryItem> m_undoItems; std::deque<HistoryItem> m_undoItems;
std::deque<HistoryItem> m_redoItems; std::deque<HistoryItem> m_redoItems;
GeneratedCacheContext m_generatedCacheContext; GeneratedCacheContext m_generatedCacheContext;
std::vector<QString> m_resultRigMissingMarkNames; std::vector<std::pair<QtMsgType, QString>> m_resultRigMessages;
std::vector<QString> m_resultRigErrorMarkNames;
}; };
#endif #endif

View File

@ -1,14 +0,0 @@
#include <unordered_map>
#include "genericposer.h"
GenericPoser::GenericPoser(const std::vector<RiggerBone> &bones) :
Poser(bones)
{
}
void GenericPoser::commit()
{
// TODO:
Poser::commit();
}

View File

@ -1,14 +0,0 @@
#ifndef DUST3D_GENERIC_POSER_H
#define DUST3D_GENERIC_POSER_H
#include "poser.h"
class GenericPoser : public Poser
{
Q_OBJECT
public:
GenericPoser(const std::vector<RiggerBone> &bones);
public:
void commit() override;
};
#endif

View File

@ -1,26 +0,0 @@
#ifndef DUST3D_GENERIC_RIGGER_H
#define DUST3D_GENERIC_RIGGER_H
#include <QVector3D>
#include <vector>
#include <set>
#include "rigger.h"
#include "meshsplitter.h"
class GenericRigger: public Rigger
{
Q_OBJECT
public:
GenericRigger(const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &inputTriangles);
protected:
bool validate() override;
bool isCutOffSplitter(BoneMark boneMark) override;
bool rig() override;
BoneMark translateBoneMark(BoneMark boneMark) override;
private:
void collectJointsForLimb(int markIndex, std::vector<int> &jointMarkIndicies);
void normalizeButtonColumns();
QString namingLimb(int spineOrder, SkeletonSide side, int limbOrder, int jointOrder);
};
#endif

View File

@ -26,7 +26,7 @@ MotionManageWidget::MotionManageWidget(const Document *document, QWidget *parent
auto refreshInfoLabel = [=]() { auto refreshInfoLabel = [=]() {
if (m_document->currentRigSucceed()) { if (m_document->currentRigSucceed()) {
if (m_document->rigType == RigType::Tetrapod) { if (m_document->rigType == RigType::Animal) {
infoLabel->setText(""); infoLabel->setText("");
infoLabel->hide(); infoLabel->hide();
addMotionButton->show(); addMotionButton->show();

View File

@ -189,38 +189,45 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
auto findFirst = boneIndexToHeadNodeIdMap.find(edgePair.first); auto findFirst = boneIndexToHeadNodeIdMap.find(edgePair.first);
if (findFirst == boneIndexToHeadNodeIdMap.end()) { if (findFirst == boneIndexToHeadNodeIdMap.end()) {
const auto &bone = (*rigBones)[edgePair.first]; const auto &bone = (*rigBones)[edgePair.first];
SkeletonNode node; if (!bone.name.startsWith("Virtual_")) {
node.partId = m_bonesPartId; SkeletonNode node;
node.id = QUuid::createUuid(); node.partId = m_bonesPartId;
node.setRadius(m_nodeRadius); node.id = QUuid::createUuid();
node.x = bone.headPosition.x() + 0.5; node.setRadius(m_nodeRadius);
node.y = -bone.headPosition.y() + 0.5; node.x = bone.headPosition.x() + 0.5;
node.z = -bone.headPosition.z() + 1; node.y = -bone.headPosition.y() + 0.5;
nodeMap[node.id] = node; node.z = -bone.headPosition.z() + 1;
newAddedNodeIds.insert(node.id); nodeMap[node.id] = node;
boneIndexToHeadNodeIdMap[edgePair.first] = node.id; newAddedNodeIds.insert(node.id);
firstNodeId = node.id; boneIndexToHeadNodeIdMap[edgePair.first] = node.id;
firstNodeId = node.id;
}
} else { } else {
firstNodeId = findFirst->second; firstNodeId = findFirst->second;
} }
auto findSecond = boneIndexToHeadNodeIdMap.find(edgePair.second); auto findSecond = boneIndexToHeadNodeIdMap.find(edgePair.second);
if (findSecond == boneIndexToHeadNodeIdMap.end()) { if (findSecond == boneIndexToHeadNodeIdMap.end()) {
const auto &bone = (*rigBones)[edgePair.second]; const auto &bone = (*rigBones)[edgePair.second];
SkeletonNode node; if (!bone.name.startsWith("Virtual_")) {
node.partId = m_bonesPartId; SkeletonNode node;
node.id = QUuid::createUuid(); node.partId = m_bonesPartId;
node.setRadius(m_nodeRadius); node.id = QUuid::createUuid();
node.x = bone.headPosition.x() + 0.5; node.setRadius(m_nodeRadius);
node.y = -bone.headPosition.y() + 0.5; node.x = bone.headPosition.x() + 0.5;
node.z = -bone.headPosition.z() + 1; node.y = -bone.headPosition.y() + 0.5;
nodeMap[node.id] = node; node.z = -bone.headPosition.z() + 1;
newAddedNodeIds.insert(node.id); nodeMap[node.id] = node;
boneIndexToHeadNodeIdMap[edgePair.second] = node.id; newAddedNodeIds.insert(node.id);
secondNodeId = node.id; boneIndexToHeadNodeIdMap[edgePair.second] = node.id;
secondNodeId = node.id;
}
} else { } else {
secondNodeId = findSecond->second; secondNodeId = findSecond->second;
} }
if (firstNodeId.isNull() || secondNodeId.isNull())
continue;
SkeletonEdge edge; SkeletonEdge edge;
edge.partId = m_bonesPartId; edge.partId = m_bonesPartId;
edge.id = QUuid::createUuid(); edge.id = QUuid::createUuid();
@ -231,8 +238,11 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
nodeMap[firstNodeId].edgeIds.push_back(edge.id); nodeMap[firstNodeId].edgeIds.push_back(edge.id);
nodeMap[secondNodeId].edgeIds.push_back(edge.id); nodeMap[secondNodeId].edgeIds.push_back(edge.id);
} }
for (size_t i = 0; i < rigBones->size(); ++i) {
for (size_t i = 1; i < rigBones->size(); ++i) {
const auto &bone = (*rigBones)[i]; const auto &bone = (*rigBones)[i];
if (bone.name.startsWith("Virtual_"))
continue;
if (bone.children.empty()) { if (bone.children.empty()) {
const QUuid &firstNodeId = boneIndexToHeadNodeIdMap[i]; const QUuid &firstNodeId = boneIndexToHeadNodeIdMap[i];

View File

@ -11,8 +11,6 @@
#include "skeletongraphicswidget.h" #include "skeletongraphicswidget.h"
#include "posedocument.h" #include "posedocument.h"
typedef RiggerButtonParameterType PopupWidgetType;
class PoseEditWidget : public QDialog class PoseEditWidget : public QDialog
{ {
Q_OBJECT Q_OBJECT

View File

@ -26,7 +26,7 @@ PoseManageWidget::PoseManageWidget(const Document *document, QWidget *parent) :
auto refreshInfoLabel = [=]() { auto refreshInfoLabel = [=]() {
if (m_document->currentRigSucceed()) { if (m_document->currentRigSucceed()) {
if (m_document->rigType == RigType::Tetrapod) { if (m_document->rigType == RigType::Animal) {
infoLabel->setText(""); infoLabel->setText("");
infoLabel->hide(); infoLabel->hide();
addPoseButton->show(); addPoseButton->show();

View File

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

View File

@ -53,14 +53,9 @@ bool RigGenerator::isSucceed()
return m_isSucceed; return m_isSucceed;
} }
const std::vector<QString> &RigGenerator::missingMarkNames() const std::vector<std::pair<QtMsgType, QString>> &RigGenerator::messages()
{ {
return m_missingMarkNames; return m_messages;
}
const std::vector<QString> &RigGenerator::errorMarkNames()
{
return m_errorMarkNames;
} }
void RigGenerator::generate() void RigGenerator::generate()
@ -84,8 +79,7 @@ void RigGenerator::generate()
if (bmeshNode.boneMark == BoneMark::None) if (bmeshNode.boneMark == BoneMark::None)
continue; continue;
SkeletonSide boneSide = SkeletonSide::None; SkeletonSide boneSide = SkeletonSide::None;
if (BoneMarkHasSide(bmeshNode.boneMark) && if (BoneMarkHasSide(bmeshNode.boneMark)) {
std::abs(bmeshNode.origin.x()) > 0.01 ) {
boneSide = bmeshNode.origin.x() > 0 ? SkeletonSide::Left : SkeletonSide::Right; boneSide = bmeshNode.origin.x() > 0 ? SkeletonSide::Left : SkeletonSide::Right;
} }
//qDebug() << "Add bone mark:" << BoneMarkToString(bmeshNode.boneMark) << "side:" << SkeletonSideToDispName(boneSide); //qDebug() << "Add bone mark:" << BoneMarkToString(bmeshNode.boneMark) << "side:" << SkeletonSideToDispName(boneSide);
@ -168,12 +162,11 @@ void RigGenerator::generate()
qDebug() << "Rig succeed"; qDebug() << "Rig succeed";
} else { } else {
qDebug() << "Rig failed"; qDebug() << "Rig failed";
if (nullptr != m_autoRigger) { }
m_missingMarkNames = m_autoRigger->missingMarkNames(); if (nullptr != m_autoRigger) {
m_errorMarkNames = m_autoRigger->errorMarkNames(); m_messages = m_autoRigger->messages();
for (const auto &message: m_autoRigger->messages()) { for (const auto &message: m_autoRigger->messages()) {
qDebug() << "errorType:" << message.first << "Message:" << message.second; qDebug() << "errorType:" << message.first << "Message:" << message.second;
}
} }
} }

View File

@ -17,8 +17,7 @@ public:
MeshLoader *takeResultMesh(); MeshLoader *takeResultMesh();
std::vector<RiggerBone> *takeResultBones(); std::vector<RiggerBone> *takeResultBones();
std::map<int, RiggerVertexWeights> *takeResultWeights(); std::map<int, RiggerVertexWeights> *takeResultWeights();
const std::vector<QString> &missingMarkNames(); const std::vector<std::pair<QtMsgType, QString>> &messages();
const std::vector<QString> &errorMarkNames();
Outcome *takeOutcome(); Outcome *takeOutcome();
bool isSucceed(); bool isSucceed();
void generate(); void generate();
@ -33,8 +32,7 @@ private:
Rigger *m_autoRigger = nullptr; Rigger *m_autoRigger = nullptr;
std::vector<RiggerBone> *m_resultBones = nullptr; std::vector<RiggerBone> *m_resultBones = nullptr;
std::map<int, RiggerVertexWeights> *m_resultWeights = nullptr; std::map<int, RiggerVertexWeights> *m_resultWeights = nullptr;
std::vector<QString> m_missingMarkNames; std::vector<std::pair<QtMsgType, QString>> m_messages;
std::vector<QString> m_errorMarkNames;
bool m_isSucceed = false; bool m_isSucceed = false;
}; };

View File

@ -11,7 +11,8 @@ QString Rigger::rootBoneName = "Body";
Rigger::Rigger(const std::vector<QVector3D> &verticesPositions, Rigger::Rigger(const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &inputTriangles) : const std::set<MeshSplitterTriangle> &inputTriangles) :
m_verticesPositions(verticesPositions), m_verticesPositions(verticesPositions),
m_inputTriangles(inputTriangles) m_inputTriangles(inputTriangles),
m_extraMessagedAdded(false)
{ {
} }
@ -58,10 +59,7 @@ bool Rigger::addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bo
if (isCutOffSplitter(mark.boneMark)) { if (isCutOffSplitter(mark.boneMark)) {
if (!mark.split(m_inputTriangles, m_maxCutOffSplitterExpandRound)) { if (!mark.split(m_inputTriangles, m_maxCutOffSplitterExpandRound)) {
m_marksMap[std::make_pair(mark.boneMark, mark.boneSide)].push_back(m_marks.size() - 1); m_cutoffErrorItems.push_back(SkeletonSideToDispName(mark.boneSide) + " " + BoneMarkToDispName(mark.boneMark));
m_errorMarkNames.push_back(SkeletonSideToDispName(mark.boneSide) + " " + BoneMarkToDispName(mark.boneMark));
m_messages.push_back(std::make_pair(QtCriticalMsg,
tr("Mark \"%1 %2\" couldn't cut off the mesh").arg(SkeletonSideToDispName(mark.boneSide)).arg(BoneMarkToString(boneMark))));
return false; return false;
} }
} }
@ -73,6 +71,22 @@ bool Rigger::addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bo
const std::vector<std::pair<QtMsgType, QString>> &Rigger::messages() const std::vector<std::pair<QtMsgType, QString>> &Rigger::messages()
{ {
if (!m_extraMessagedAdded) {
m_extraMessagedAdded = true;
if (!m_cutoffErrorItems.empty()) {
QStringList cutoffErrorNames;
for (const auto &item: m_cutoffErrorItems) {
cutoffErrorNames.append("<b>" + item + "</b>");
}
m_messages.push_back(std::make_pair(QtCriticalMsg, tr("The following marks couldn't cut off the mesh, pelease try mark more nearby nodes for them: %1").arg(cutoffErrorNames.join(", "))));
}
if (!m_jointErrorItems.empty()) {
QStringList jointErrorNames;
for (const auto &item: m_jointErrorItems)
jointErrorNames.append("<b>" + item + "</b>");
m_messages.push_back(std::make_pair(QtCriticalMsg, tr("The following marks looks like don't contain any vertices, pelease try mark other nearby nodes for them: %1").arg(jointErrorNames.join(", "))));
}
}
return m_messages; return m_messages;
} }
@ -246,12 +260,3 @@ void Rigger::addVerticesToWeights(const std::set<int> &vertices, int boneIndex)
} }
} }
const std::vector<QString> &Rigger::missingMarkNames()
{
return m_missingMarkNames;
}
const std::vector<QString> &Rigger::errorMarkNames()
{
return m_errorMarkNames;
}

View File

@ -11,14 +11,6 @@
#include "rigtype.h" #include "rigtype.h"
#include "skeletonside.h" #include "skeletonside.h"
enum class RiggerButtonParameterType
{
None = 0,
PitchYawRoll,
Intersection,
Translation
};
class RiggerMark class RiggerMark
{ {
public: public:
@ -65,9 +57,6 @@ public:
QVector3D headPosition; QVector3D headPosition;
QVector3D tailPosition; QVector3D tailPosition;
QColor color; QColor color;
bool hasButton = false;
RiggerButtonParameterType buttonParameterType = RiggerButtonParameterType::None;
std::pair<int, int> button = {0, 0};
QVector3D baseNormal; QVector3D baseNormal;
std::vector<int> children; std::vector<int> children;
}; };
@ -122,8 +111,6 @@ public:
const std::vector<std::pair<QtMsgType, QString>> &messages(); const std::vector<std::pair<QtMsgType, QString>> &messages();
const std::vector<RiggerBone> &resultBones(); const std::vector<RiggerBone> &resultBones();
const std::map<int, RiggerVertexWeights> &resultWeights(); const std::map<int, RiggerVertexWeights> &resultWeights();
const std::vector<QString> &missingMarkNames();
const std::vector<QString> &errorMarkNames();
virtual bool rig() = 0; virtual bool rig() = 0;
static QString rootBoneName; static QString rootBoneName;
protected: protected:
@ -152,9 +139,10 @@ protected:
std::map<std::pair<BoneMark, SkeletonSide>, std::vector<int>> m_marksMap; std::map<std::pair<BoneMark, SkeletonSide>, std::vector<int>> m_marksMap;
std::vector<RiggerBone> m_resultBones; std::vector<RiggerBone> m_resultBones;
std::map<int, RiggerVertexWeights> m_resultWeights; std::map<int, RiggerVertexWeights> m_resultWeights;
std::vector<QString> m_missingMarkNames; std::vector<QString> m_cutoffErrorItems;
std::vector<QString> m_errorMarkNames; std::vector<QString> m_jointErrorItems;
static size_t m_maxCutOffSplitterExpandRound; static size_t m_maxCutOffSplitterExpandRound;
bool m_extraMessagedAdded;
}; };
#endif #endif

View File

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

View File

@ -5,8 +5,7 @@
enum class RigType enum class RigType
{ {
None = 0, None = 0,
Generic, Animal,
Tetrapod,
Count Count
}; };
RigType RigTypeFromString(const char *typeString); RigType RigTypeFromString(const char *typeString);
@ -14,10 +13,8 @@ RigType RigTypeFromString(const char *typeString);
RigType RigTypeFromString(const char *typeString) \ RigType RigTypeFromString(const char *typeString) \
{ \ { \
QString type = typeString; \ QString type = typeString; \
if (type == "Generic") \ if (type == "Animal") \
return RigType::Generic; \ return RigType::Animal; \
if (type == "Tetrapod") \
return RigType::Tetrapod; \
return RigType::None; \ return RigType::None; \
} }
const char *RigTypeToString(RigType type); const char *RigTypeToString(RigType type);
@ -25,10 +22,8 @@ const char *RigTypeToString(RigType type);
const char *RigTypeToString(RigType type) \ const char *RigTypeToString(RigType type) \
{ \ { \
switch (type) { \ switch (type) { \
case RigType::Generic: \ case RigType::Animal: \
return "Generic"; \ return "Animal"; \
case RigType::Tetrapod: \
return "Tetrapod"; \
case RigType::None: \ case RigType::None: \
return "None"; \ return "None"; \
default: \ default: \
@ -40,10 +35,8 @@ QString RigTypeToDispName(RigType type);
QString RigTypeToDispName(RigType type) \ QString RigTypeToDispName(RigType type) \
{ \ { \
switch (type) { \ switch (type) { \
case RigType::Generic: \ case RigType::Animal: \
return QObject::tr("Generic"); \ return QObject::tr("Animal"); \
case RigType::Tetrapod: \
return QObject::tr("Tetrapod"); \
case RigType::None: \ case RigType::None: \
return QObject::tr("None"); \ return QObject::tr("None"); \
default: \ default: \

View File

@ -19,13 +19,8 @@ RigWidget::RigWidget(const Document *document, QWidget *parent) :
m_rigTypeBox->addItem(RigTypeToDispName(rigType)); m_rigTypeBox->addItem(RigTypeToDispName(rigType));
} }
//m_poseCheckButton = new QPushButton(QChar(fa::child));
//Theme::initAwesomeButton(m_poseCheckButton);
//m_poseCheckButton->hide();
QHBoxLayout *controlsLayout = new QHBoxLayout; QHBoxLayout *controlsLayout = new QHBoxLayout;
controlsLayout->addWidget(m_rigTypeBox); controlsLayout->addWidget(m_rigTypeBox);
//controlsLayout->addWidget(m_poseCheckButton);
formLayout->addRow(tr("Type"), controlsLayout); formLayout->addRow(tr("Type"), controlsLayout);
@ -38,23 +33,17 @@ RigWidget::RigWidget(const Document *document, QWidget *parent) :
m_rigWeightRenderWidget = new ModelWidget(this); m_rigWeightRenderWidget = new ModelWidget(this);
m_rigWeightRenderWidget->setMinimumSize(128, 128); m_rigWeightRenderWidget->setMinimumSize(128, 128);
//m_rigWeightRenderWidget->resize(256, 256);
//m_rigWeightRenderWidget->move(-64, 0);
m_rigWeightRenderWidget->setXRotation(0); m_rigWeightRenderWidget->setXRotation(0);
m_rigWeightRenderWidget->setYRotation(0); m_rigWeightRenderWidget->setYRotation(0);
m_rigWeightRenderWidget->setZRotation(0); m_rigWeightRenderWidget->setZRotation(0);
m_missingMarksInfoLabel = new InfoLabel; m_infoLabel = new InfoLabel;
m_missingMarksInfoLabel->hide(); m_infoLabel->hide();
m_errorMarksInfoLabel = new InfoLabel;
m_errorMarksInfoLabel->hide();
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
layout->addLayout(formLayout); layout->addLayout(formLayout);
layout->addWidget(m_rigWeightRenderWidget); layout->addWidget(m_rigWeightRenderWidget);
layout->addWidget(m_missingMarksInfoLabel); layout->addWidget(m_infoLabel);
layout->addWidget(m_errorMarksInfoLabel);
layout->addStretch(); layout->addStretch();
setLayout(layout); setLayout(layout);
@ -62,36 +51,21 @@ RigWidget::RigWidget(const Document *document, QWidget *parent) :
void RigWidget::rigTypeChanged() void RigWidget::rigTypeChanged()
{ {
//m_poseCheckButton->setVisible(RigType::None != m_document->rigType);
m_rigTypeBox->setCurrentIndex((int)m_document->rigType); m_rigTypeBox->setCurrentIndex((int)m_document->rigType);
} }
void RigWidget::updateResultInfo() void RigWidget::updateResultInfo()
{ {
QStringList missingMarkNames; const auto &messages = m_document->resultRigMessages();
for (const auto &markName: m_document->resultRigMissingMarkNames()) { if (messages.empty()) {
missingMarkNames.append(markName); m_infoLabel->hide();
}
QString missingNamesString = missingMarkNames.join(tr(", "));
if (missingNamesString.isEmpty()) {
m_missingMarksInfoLabel->hide();
} else { } else {
m_missingMarksInfoLabel->setText(tr("Missing marks: ") + missingNamesString); QStringList messageList;
m_missingMarksInfoLabel->setMaximumWidth(width() * 0.8); for (const auto &item: messages)
m_missingMarksInfoLabel->show(); messageList.append(item.second);
} m_infoLabel->setText(messageList.join("<br><br>"));
m_infoLabel->setMaximumWidth(width() * 0.90);
QStringList errorMarkNames; m_infoLabel->show();
for (const auto &markName: m_document->resultRigErrorMarkNames()) {
errorMarkNames.append(markName);
}
QString errorNamesString = errorMarkNames.join(tr(","));
if (errorNamesString.isEmpty()) {
m_errorMarksInfoLabel->hide();
} else {
m_errorMarksInfoLabel->setText(tr("Error marks: ") + errorNamesString);
m_errorMarksInfoLabel->setMaximumWidth(width() * 0.8);
m_errorMarksInfoLabel->show();
} }
} }

View File

@ -23,8 +23,7 @@ private:
const Document *m_document = nullptr; const Document *m_document = nullptr;
QComboBox *m_rigTypeBox = nullptr; QComboBox *m_rigTypeBox = nullptr;
ModelWidget *m_rigWeightRenderWidget = nullptr; ModelWidget *m_rigWeightRenderWidget = nullptr;
InfoLabel *m_missingMarksInfoLabel = nullptr; InfoLabel *m_infoLabel = nullptr;
InfoLabel *m_errorMarksInfoLabel = nullptr;
}; };
#endif #endif

View File

@ -1,219 +0,0 @@
#include <cmath>
#include <QtMath>
#include "tetrapodposer.h"
#include "util.h"
TetrapodPoser::TetrapodPoser(const std::vector<RiggerBone> &bones) :
Poser(bones)
{
}
void TetrapodPoser::resolveTranslation()
{
for (const auto &item: parameters()) {
int boneIndex = findBoneIndex(item.first);
if (-1 == boneIndex) {
continue;
}
auto findTranslateXResult = item.second.find("translateX");
auto findTranslateYResult = item.second.find("translateY");
auto findTranslateZResult = item.second.find("translateZ");
if (findTranslateXResult != item.second.end() ||
findTranslateYResult != item.second.end() ||
findTranslateZResult != item.second.end()) {
float x = valueOfKeyInMapOrEmpty(item.second, "translateX").toFloat();
float y = valueOfKeyInMapOrEmpty(item.second, "translateY").toFloat();
float z = valueOfKeyInMapOrEmpty(item.second, "translateZ").toFloat();
QVector3D translation = {x, y, z};
m_jointNodeTree.addTranslation(boneIndex, translation);
continue;
}
}
resolveLimbRotation({QString("LeftUpperArm"), QString("LeftLowerArm"), QString("LeftHand")});
resolveLimbRotation({QString("RightUpperArm"), QString("RightLowerArm"), QString("RightHand")});
resolveLimbRotation({QString("LeftUpperLeg"), QString("LeftLowerLeg"), QString("LeftFoot")});
resolveLimbRotation({QString("RightUpperLeg"), QString("RightLowerLeg"), QString("RightFoot")});
resolveLimbRotation({QString("Spine"), QString("Chest"), QString("Neck"), QString("Head")});
}
std::pair<bool, QVector3D> TetrapodPoser::findQVector3DFromMap(const std::map<QString, QString> &map, const QString &xName, const QString &yName, const QString &zName)
{
auto findXResult = map.find(xName);
auto findYResult = map.find(yName);
auto findZResult = map.find(zName);
if (findXResult == map.end() &&
findYResult == map.end() &&
findZResult == map.end()) {
return {false, QVector3D()};
}
return {true, {
valueOfKeyInMapOrEmpty(map, xName).toFloat(),
valueOfKeyInMapOrEmpty(map, yName).toFloat(),
valueOfKeyInMapOrEmpty(map, zName).toFloat()
}};
}
std::pair<bool, std::pair<QVector3D, QVector3D>> TetrapodPoser::findBonePositionsFromParameters(const std::map<QString, QString> &map)
{
auto findBoneStartResult = findQVector3DFromMap(map, "fromX", "fromY", "fromZ");
auto findBoneStopResult = findQVector3DFromMap(map, "toX", "toY", "toZ");
if (!findBoneStartResult.first || !findBoneStopResult.first)
return {false, {QVector3D(), QVector3D()}};
return {true, {findBoneStartResult.second, findBoneStopResult.second}};
}
void TetrapodPoser::resolveLimbRotation(const std::vector<QString> &limbBoneNames)
{
// We match the poses by the distance and rotation plane
if (limbBoneNames.size() < 3) {
qDebug() << "Cann't resolve limb bones with invalid joints:" << limbBoneNames.size();
return;
}
const auto &beginBoneName = limbBoneNames[0];
const auto &middleBoneName = limbBoneNames[1];
const auto &endBoneName = limbBoneNames[2];
const auto &beginBoneParameters = parameters().find(beginBoneName);
if (beginBoneParameters == parameters().end()) {
qDebug() << beginBoneName << "'s parameters not found";
return;
}
auto matchBeginBonePositions = findBonePositionsFromParameters(beginBoneParameters->second);
if (!matchBeginBonePositions.first) {
qDebug() << beginBoneName << "'s positions not found";
return;
}
const auto &endBoneParameters = parameters().find(endBoneName);
if (endBoneParameters == parameters().end()) {
qDebug() << endBoneName << "'s parameters not found";
return;
}
auto matchEndBonePositions = findBonePositionsFromParameters(endBoneParameters->second);
if (!matchEndBonePositions.first) {
qDebug() << endBoneName << "'s positions not found";
return;
}
float matchLimbLength = (matchBeginBonePositions.second.first - matchBeginBonePositions.second.second).length() +
(matchBeginBonePositions.second.second - matchEndBonePositions.second.first).length();
auto matchDistanceBetweenBeginAndEndBones = (matchBeginBonePositions.second.first - matchEndBonePositions.second.first).length();
auto matchRotatePlaneNormal = QVector3D::crossProduct((matchBeginBonePositions.second.second - matchBeginBonePositions.second.first).normalized(), (matchEndBonePositions.second.first - matchBeginBonePositions.second.second).normalized());
auto matchDirectionBetweenBeginAndEndPones = (matchEndBonePositions.second.first - matchBeginBonePositions.second.first).normalized();
auto matchEndBoneDirection = (matchEndBonePositions.second.second - matchEndBonePositions.second.first).normalized();
int beginBoneIndex = findBoneIndex(beginBoneName);
if (-1 == beginBoneIndex) {
qDebug() << beginBoneName << "not found in rigged bones";
return;
}
const auto &beginBone = bones()[beginBoneIndex];
int middleBoneIndex = findBoneIndex(middleBoneName);
if (-1 == middleBoneIndex) {
qDebug() << middleBoneName << "not found in rigged bones";
return;
}
int endBoneIndex = findBoneIndex(endBoneName);
if (-1 == endBoneIndex) {
qDebug() << endBoneName << "not found in rigged bones";
return;
}
const auto &endBone = bones()[endBoneIndex];
float targetBeginBoneLength = (beginBone.headPosition - beginBone.tailPosition).length();
float targetMiddleBoneLength = (beginBone.tailPosition - endBone.headPosition).length();
float targetLimbLength = targetBeginBoneLength + targetMiddleBoneLength;
float targetDistanceBetweenBeginAndEndBones = matchDistanceBetweenBeginAndEndBones * (targetLimbLength / matchLimbLength);
QVector3D targetEndBoneStartPosition = beginBone.headPosition + matchDirectionBetweenBeginAndEndPones * targetDistanceBetweenBeginAndEndBones;
float angleBetweenDistanceAndMiddleBones = 0;
{
const float &a = targetMiddleBoneLength;
const float &b = targetDistanceBetweenBeginAndEndBones;
const float &c = targetBeginBoneLength;
double cosC = (a*a + b*b - c*c) / (2.0*a*b);
angleBetweenDistanceAndMiddleBones = qRadiansToDegrees(acos(cosC));
}
QVector3D targetMiddleBoneStartPosition;
{
qDebug() << beginBoneName << "Angle:" << angleBetweenDistanceAndMiddleBones;
auto rotation = QQuaternion::fromAxisAndAngle(matchRotatePlaneNormal, angleBetweenDistanceAndMiddleBones);
targetMiddleBoneStartPosition = targetEndBoneStartPosition + rotation.rotatedVector(-matchDirectionBetweenBeginAndEndPones).normalized() * targetMiddleBoneLength;
}
// Now the bones' positions have been resolved, we calculate the rotation
auto oldBeginBoneDirection = (beginBone.tailPosition - beginBone.headPosition).normalized();
auto newBeginBoneDirection = (targetMiddleBoneStartPosition - beginBone.headPosition).normalized();
auto beginBoneRotation = QQuaternion::rotationTo(oldBeginBoneDirection, newBeginBoneDirection);
m_jointNodeTree.updateRotation(beginBoneIndex, beginBoneRotation);
auto oldMiddleBoneDirection = (endBone.headPosition - beginBone.tailPosition).normalized();
auto newMiddleBoneDirection = (targetEndBoneStartPosition - targetMiddleBoneStartPosition).normalized();
oldMiddleBoneDirection = beginBoneRotation.rotatedVector(oldMiddleBoneDirection);
auto middleBoneRotation = QQuaternion::rotationTo(oldMiddleBoneDirection, newMiddleBoneDirection);
m_jointNodeTree.updateRotation(middleBoneIndex, middleBoneRotation);
// Calculate the end effectors' rotation
auto oldEndBoneDirection = (endBone.tailPosition - endBone.headPosition).normalized();
auto newEndBoneDirection = matchEndBoneDirection;
oldEndBoneDirection = beginBoneRotation.rotatedVector(oldEndBoneDirection);
oldEndBoneDirection = middleBoneRotation.rotatedVector(oldEndBoneDirection);
auto endBoneRotation = QQuaternion::rotationTo(oldEndBoneDirection, newEndBoneDirection);
m_jointNodeTree.updateRotation(endBoneIndex, endBoneRotation);
if (limbBoneNames.size() > 3) {
std::vector<QQuaternion> rotations;
rotations.push_back(beginBoneRotation);
rotations.push_back(middleBoneRotation);
rotations.push_back(endBoneRotation);
for (size_t i = 3; i < limbBoneNames.size(); ++i) {
const auto &boneName = limbBoneNames[i];
int boneIndex = findBoneIndex(boneName);
if (-1 == boneIndex) {
qDebug() << "Find bone failed:" << boneName;
continue;
}
const auto &bone = bones()[boneIndex];
const auto &boneParameters = parameters().find(boneName);
if (boneParameters == parameters().end()) {
qDebug() << "Find bone parameters:" << boneName;
continue;
}
auto matchBonePositions = findBonePositionsFromParameters(boneParameters->second);
if (!matchBonePositions.first) {
qDebug() << "Find bone positions failed:" << boneName;
continue;
}
auto matchBoneDirection = (matchBonePositions.second.second - matchBonePositions.second.first).normalized();
auto oldBoneDirection = (bone.tailPosition - bone.headPosition).normalized();
auto newBoneDirection = matchBoneDirection;
for (const auto &rotation: rotations) {
oldBoneDirection = rotation.rotatedVector(oldBoneDirection);
}
auto boneRotation = QQuaternion::rotationTo(oldBoneDirection, newBoneDirection);
m_jointNodeTree.updateRotation(boneIndex, boneRotation);
rotations.push_back(boneRotation);
}
}
}
void TetrapodPoser::commit()
{
resolveTranslation();
Poser::commit();
}

View File

@ -1,689 +0,0 @@
#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 = Rigger::rootBoneName;
bodyBone.headPosition = QVector3D(0, 0, 0);
bodyBone.tailPosition = bonesOrigin;
bodyBone.hasButton = true;
bodyBone.button = {7, 1};
bodyBone.buttonParameterType = RiggerButtonParameterType::Translation;
boneIndexMap[bodyBone.name] = bodyBone.index;
m_resultBones.push_back(RiggerBone());
RiggerBone &leftHipBone = m_resultBones.back();
leftHipBone.index = m_resultBones.size() - 1;
leftHipBone.name = "LeftHip";
leftHipBone.headPosition = m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition;
leftHipBone.tailPosition = leftUpperLegBoneStartPosition;
boneIndexMap[leftHipBone.name] = leftHipBone.index;
m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(leftHipBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &leftUpperLegBone = m_resultBones.back();
leftUpperLegBone.index = m_resultBones.size() - 1;
leftUpperLegBone.name = "LeftUpperLeg";
leftUpperLegBone.headPosition = m_resultBones[boneIndexMap["LeftHip"]].tailPosition;
leftUpperLegBone.tailPosition = leftLowerLegBoneStartPosition;
leftUpperLegBone.color = BoneMarkToColor(BoneMark::Hip);
leftUpperLegBone.hasButton = true;
leftUpperLegBone.button = {4, 2};
leftUpperLegBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll;
boneIndexMap[leftUpperLegBone.name] = leftUpperLegBone.index;
m_resultBones[boneIndexMap["LeftHip"]].children.push_back(leftUpperLegBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &leftLowerLegBone = m_resultBones.back();
leftLowerLegBone.index = m_resultBones.size() - 1;
leftLowerLegBone.name = "LeftLowerLeg";
leftLowerLegBone.headPosition = m_resultBones[boneIndexMap["LeftUpperLeg"]].tailPosition;
leftLowerLegBone.tailPosition = leftFootBoneStartPosition;
leftLowerLegBone.color = BoneMarkToColor(BoneMark::Knee);
leftLowerLegBone.hasButton = true;
leftLowerLegBone.button = {5, 2};
leftLowerLegBone.buttonParameterType = RiggerButtonParameterType::Intersection;
boneIndexMap[leftLowerLegBone.name] = leftLowerLegBone.index;
m_resultBones[boneIndexMap["LeftUpperLeg"]].children.push_back(leftLowerLegBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &leftFootBone = m_resultBones.back();
leftFootBone.index = m_resultBones.size() - 1;
leftFootBone.name = "LeftFoot";
leftFootBone.headPosition = m_resultBones[boneIndexMap["LeftLowerLeg"]].tailPosition;
leftFootBone.tailPosition = leftFootBoneStopPosition;
leftFootBone.color = BoneMarkToColor(BoneMark::Ankle);
leftFootBone.hasButton = true;
leftFootBone.button = {6, 2};
leftFootBone.buttonParameterType = RiggerButtonParameterType::Intersection;
boneIndexMap[leftFootBone.name] = leftFootBone.index;
m_resultBones[boneIndexMap["LeftLowerLeg"]].children.push_back(leftFootBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &rightHipBone = m_resultBones.back();
rightHipBone.index = m_resultBones.size() - 1;
rightHipBone.name = "RightHip";
rightHipBone.headPosition = m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition;
rightHipBone.tailPosition = rightUpperLegBoneStartPosition;
boneIndexMap[rightHipBone.name] = rightHipBone.index;
m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(rightHipBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &rightUpperLegBone = m_resultBones.back();
rightUpperLegBone.index = m_resultBones.size() - 1;
rightUpperLegBone.name = "RightUpperLeg";
rightUpperLegBone.headPosition = m_resultBones[boneIndexMap["RightHip"]].tailPosition;
rightUpperLegBone.tailPosition = rightLowerLegBoneStartPosition;
rightUpperLegBone.color = BoneMarkToColor(BoneMark::Hip);
rightUpperLegBone.hasButton = true;
rightUpperLegBone.button = {4, 0};
rightUpperLegBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll;
boneIndexMap[rightUpperLegBone.name] = rightUpperLegBone.index;
m_resultBones[boneIndexMap["RightHip"]].children.push_back(rightUpperLegBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &rightLowerLegBone = m_resultBones.back();
rightLowerLegBone.index = m_resultBones.size() - 1;
rightLowerLegBone.name = "RightLowerLeg";
rightLowerLegBone.headPosition = m_resultBones[boneIndexMap["RightUpperLeg"]].tailPosition;
rightLowerLegBone.tailPosition = rightFootBoneStartPosition;
rightLowerLegBone.color = BoneMarkToColor(BoneMark::Knee);
rightLowerLegBone.hasButton = true;
rightLowerLegBone.button = {5, 0};
rightLowerLegBone.buttonParameterType = RiggerButtonParameterType::Intersection;
boneIndexMap[rightLowerLegBone.name] = rightLowerLegBone.index;
m_resultBones[boneIndexMap["RightUpperLeg"]].children.push_back(rightLowerLegBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &rightFootBone = m_resultBones.back();
rightFootBone.index = m_resultBones.size() - 1;
rightFootBone.name = "RightFoot";
rightFootBone.headPosition = m_resultBones[boneIndexMap["RightLowerLeg"]].tailPosition;
rightFootBone.tailPosition = rightFootBoneStopPosition;
rightFootBone.color = BoneMarkToColor(BoneMark::Ankle);
rightFootBone.hasButton = true;
rightFootBone.button = {6, 0};
rightFootBone.buttonParameterType = RiggerButtonParameterType::Intersection;
boneIndexMap[rightFootBone.name] = rightFootBone.index;
m_resultBones[boneIndexMap["RightLowerLeg"]].children.push_back(rightFootBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &spineBone = m_resultBones.back();
spineBone.index = m_resultBones.size() - 1;
spineBone.name = "Spine";
spineBone.headPosition = m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition;
spineBone.tailPosition = chestBoneStartPosition;
spineBone.color = Qt::white;
spineBone.hasButton = true;
spineBone.button = {3, 1};
spineBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll;
boneIndexMap[spineBone.name] = spineBone.index;
m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(spineBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &chestBone = m_resultBones.back();
chestBone.index = m_resultBones.size() - 1;
chestBone.name = "Chest";
chestBone.headPosition = m_resultBones[boneIndexMap["Spine"]].tailPosition;
chestBone.tailPosition = neckBoneStartPosition;
chestBone.color = QColor(0x57, 0x43, 0x98);
chestBone.hasButton = true;
chestBone.button = {2, 1};
chestBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll;
boneIndexMap[chestBone.name] = chestBone.index;
m_resultBones[boneIndexMap["Spine"]].children.push_back(chestBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &leftShoulderBone = m_resultBones.back();
leftShoulderBone.index = m_resultBones.size() - 1;
leftShoulderBone.name = "LeftShoulder";
leftShoulderBone.headPosition = m_resultBones[boneIndexMap["Chest"]].tailPosition;
leftShoulderBone.tailPosition = leftUpperArmBoneStartPosition;
boneIndexMap[leftShoulderBone.name] = leftShoulderBone.index;
m_resultBones[boneIndexMap["Chest"]].children.push_back(leftShoulderBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &leftUpperArmBone = m_resultBones.back();
leftUpperArmBone.index = m_resultBones.size() - 1;
leftUpperArmBone.name = "LeftUpperArm";
leftUpperArmBone.headPosition = m_resultBones[boneIndexMap["LeftShoulder"]].tailPosition;
leftUpperArmBone.tailPosition = leftLowerArmBoneStartPosition;
leftUpperArmBone.color = BoneMarkToColor(BoneMark::Shoulder);
leftUpperArmBone.hasButton = true;
leftUpperArmBone.button = {1, 2};
leftUpperArmBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll;
boneIndexMap[leftUpperArmBone.name] = leftUpperArmBone.index;
m_resultBones[boneIndexMap["LeftShoulder"]].children.push_back(leftUpperArmBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &leftLowerArmBone = m_resultBones.back();
leftLowerArmBone.index = m_resultBones.size() - 1;
leftLowerArmBone.name = "LeftLowerArm";
leftLowerArmBone.headPosition = m_resultBones[boneIndexMap["LeftUpperArm"]].tailPosition;
leftLowerArmBone.tailPosition = leftHandBoneStartPosition;
leftLowerArmBone.color = BoneMarkToColor(BoneMark::Elbow);
leftLowerArmBone.hasButton = true;
leftLowerArmBone.button = {2, 2};
leftLowerArmBone.buttonParameterType = RiggerButtonParameterType::Intersection;
boneIndexMap[leftLowerArmBone.name] = leftLowerArmBone.index;
m_resultBones[boneIndexMap["LeftUpperArm"]].children.push_back(leftLowerArmBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &leftHandBone = m_resultBones.back();
leftHandBone.index = m_resultBones.size() - 1;
leftHandBone.name = "LeftHand";
leftHandBone.headPosition = m_resultBones[boneIndexMap["LeftLowerArm"]].tailPosition;
leftHandBone.tailPosition = leftHandBoneStopPosition;
leftHandBone.color = BoneMarkToColor(BoneMark::Wrist);
leftHandBone.hasButton = true;
leftHandBone.button = {3, 2};
leftHandBone.buttonParameterType = RiggerButtonParameterType::Intersection;
boneIndexMap[leftHandBone.name] = leftHandBone.index;
m_resultBones[boneIndexMap["LeftLowerArm"]].children.push_back(leftHandBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &rightShoulderBone = m_resultBones.back();
rightShoulderBone.index = m_resultBones.size() - 1;
rightShoulderBone.name = "RightShoulder";
rightShoulderBone.headPosition = m_resultBones[boneIndexMap["Chest"]].tailPosition;
rightShoulderBone.tailPosition = rightUpperArmBoneStartPosition;
boneIndexMap[rightShoulderBone.name] = rightShoulderBone.index;
m_resultBones[boneIndexMap["Chest"]].children.push_back(rightShoulderBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &rightUpperArmBone = m_resultBones.back();
rightUpperArmBone.index = m_resultBones.size() - 1;
rightUpperArmBone.name = "RightUpperArm";
rightUpperArmBone.headPosition = m_resultBones[boneIndexMap["RightShoulder"]].tailPosition;
rightUpperArmBone.tailPosition = rightLowerArmBoneStartPosition;
rightUpperArmBone.color = BoneMarkToColor(BoneMark::Shoulder);
rightUpperArmBone.hasButton = true;
rightUpperArmBone.button = {1, 0};
rightUpperArmBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll;
boneIndexMap[rightUpperArmBone.name] = rightUpperArmBone.index;
m_resultBones[boneIndexMap["RightShoulder"]].children.push_back(rightUpperArmBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &rightLowerArmBone = m_resultBones.back();
rightLowerArmBone.index = m_resultBones.size() - 1;
rightLowerArmBone.name = "RightLowerArm";
rightLowerArmBone.headPosition = m_resultBones[boneIndexMap["RightUpperArm"]].tailPosition;
rightLowerArmBone.tailPosition = rightHandBoneStartPosition;
rightLowerArmBone.color = BoneMarkToColor(BoneMark::Elbow);
rightLowerArmBone.hasButton = true;
rightLowerArmBone.button = {2, 0};
rightLowerArmBone.buttonParameterType = RiggerButtonParameterType::Intersection;
boneIndexMap[rightLowerArmBone.name] = rightLowerArmBone.index;
m_resultBones[boneIndexMap["RightUpperArm"]].children.push_back(rightLowerArmBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &rightHandBone = m_resultBones.back();
rightHandBone.index = m_resultBones.size() - 1;
rightHandBone.name = "RightHand";
rightHandBone.headPosition = m_resultBones[boneIndexMap["RightLowerArm"]].tailPosition;
rightHandBone.tailPosition = rightHandBoneStopPosition;
rightHandBone.color = BoneMarkToColor(BoneMark::Wrist);
rightHandBone.hasButton = true;
rightHandBone.button = {3, 0};
rightHandBone.buttonParameterType = RiggerButtonParameterType::Intersection;
boneIndexMap[rightHandBone.name] = rightHandBone.index;
m_resultBones[boneIndexMap["RightLowerArm"]].children.push_back(rightHandBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &neckBone = m_resultBones.back();
neckBone.index = m_resultBones.size() - 1;
neckBone.name = "Neck";
neckBone.headPosition = m_resultBones[boneIndexMap["Chest"]].tailPosition;
neckBone.tailPosition = headBoneStartPosition;
neckBone.color = BoneMarkToColor(BoneMark::Neck);
neckBone.hasButton = true;
neckBone.button = {1, 1};
neckBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll;
boneIndexMap[neckBone.name] = neckBone.index;
m_resultBones[boneIndexMap["Chest"]].children.push_back(neckBone.index);
m_resultBones.push_back(RiggerBone());
RiggerBone &headBone = m_resultBones.back();
headBone.index = m_resultBones.size() - 1;
headBone.name = "Head";
headBone.headPosition = m_resultBones[boneIndexMap["Neck"]].tailPosition;
headBone.tailPosition = headBoneStopPosition;
headBone.color = QColor(0xfb, 0xef, 0x8b);
headBone.hasButton = true;
headBone.button = {0, 1};
headBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll;
boneIndexMap[headBone.name] = headBone.index;
m_resultBones[boneIndexMap["Neck"]].children.push_back(headBone.index);
// 6. Calculate weights for vertices
addVerticesToWeights(headVertices, boneIndexMap["Head"]);
addVerticesToWeights(neckVertices, boneIndexMap["Neck"]);
addVerticesToWeights(chestVertices, boneIndexMap["Chest"]);
addVerticesToWeights(spineVertices, boneIndexMap["Spine"]);
addVerticesToWeights(leftUpperArmVertices, boneIndexMap["LeftUpperArm"]);
addVerticesToWeights(leftLowerArmVertices, boneIndexMap["LeftLowerArm"]);
addVerticesToWeights(leftHandVertices, boneIndexMap["LeftHand"]);
addVerticesToWeights(rightUpperArmVertices, boneIndexMap["RightUpperArm"]);
addVerticesToWeights(rightLowerArmVertices, boneIndexMap["RightLowerArm"]);
addVerticesToWeights(rightHandVertices, boneIndexMap["RightHand"]);
addVerticesToWeights(leftUpperLegVertices, boneIndexMap["LeftUpperLeg"]);
addVerticesToWeights(leftLowerLegVertices, boneIndexMap["LeftLowerLeg"]);
addVerticesToWeights(leftFootVertices, boneIndexMap["LeftFoot"]);
addVerticesToWeights(rightUpperLegVertices, boneIndexMap["RightUpperLeg"]);
addVerticesToWeights(rightLowerLegVertices, boneIndexMap["RightLowerLeg"]);
addVerticesToWeights(rightFootVertices, boneIndexMap["RightFoot"]);
for (auto &weights: m_resultWeights) {
weights.second.finalizeWeights();
}
return true;
}
bool TetrapodRigger::validate()
{
bool foundError = false;
std::vector<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;
}

View File

@ -1,19 +0,0 @@
#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