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
HEADERS += src/jointnodetree.h
SOURCES += src/tetrapodposer.cpp
HEADERS += src/tetrapodposer.h
SOURCES += src/poser.cpp
HEADERS += src/poser.h
@ -276,14 +273,11 @@ HEADERS += src/uvunwrap.h
SOURCES += src/triangletangentresolve.cpp
HEADERS += src/triangletangentresolve.h
SOURCES += src/tetrapodrigger.cpp
HEADERS += src/tetrapodrigger.h
SOURCES += src/animalrigger.cpp
HEADERS += src/animalrigger.h
SOURCES += src/genericrigger.cpp
HEADERS += src/genericrigger.h
SOURCES += src/genericposer.cpp
HEADERS += src/genericposer.h
SOURCES += src/animalposer.cpp
HEADERS += src/animalposer.h
SOURCES += src/riggerconstruct.cpp
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
#define DUST3D_TETRAPOD_POSER_H
#ifndef DUST3D_ANIMAL_POSER_H
#define DUST3D_ANIMAL_POSER_H
#include <vector>
#include "poser.h"
class TetrapodPoser : public Poser
class AnimalPoser : public Poser
{
Q_OBJECT
public:
TetrapodPoser(const std::vector<RiggerBone> &bones);
AnimalPoser(const std::vector<RiggerBone> &bones);
void commit() override;
private:
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, std::pair<QVector3D, QVector3D>> findBonePositionsFromParameters(const std::map<QString, QString> &map);
};

View File

@ -2,35 +2,36 @@
#include <cmath>
#include <queue>
#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) :
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;
}
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 ||
boneMark == BoneMark::Shoulder ||
boneMark == BoneMark::Hip ||
boneMark == BoneMark::Limb)
return BoneMark::Limb;
return BoneMark::Joint;
return boneMark;
}
void GenericRigger::collectJointsForLimb(int markIndex, std::vector<int> &jointMarkIndicies)
bool AnimalRigger::collectJontsForChain(int markIndex, std::vector<int> &jointMarkIndicies)
{
const auto &mark = m_marks[markIndex];
@ -51,13 +52,13 @@ void GenericRigger::collectJointsForLimb(int markIndex, std::vector<int> &jointM
if (triangleToMarkMap.size() <= 1) {
qDebug() << "Collect joints for limb failed because of lack marks";
return;
return true;
}
const auto &group = mark.smallGroup();
if (group.empty()) {
qDebug() << "Collect joints for limb failed because of lack verticies";
return;
return false;
}
// Build the edge to triangle map;
@ -85,7 +86,7 @@ void GenericRigger::collectJointsForLimb(int markIndex, std::vector<int> &jointM
if (waitTriangles.empty()) {
qDebug() << "Couldn't find a triangle to start";
return;
return false;
}
// 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) {
qDebug() << "Find nearest joint failed";
return;
return false;
}
jointMarkIndicies.push_back(nearestMarkIndex);
visited.insert(nearestMarkIndex);
@ -185,9 +186,11 @@ void GenericRigger::collectJointsForLimb(int markIndex, std::vector<int> &jointM
break;
findPairResult = pairs.find(linkTo);
}
return true;
}
bool GenericRigger::rig()
bool AnimalRigger::rig()
{
if (!validate())
return false;
@ -205,27 +208,44 @@ bool GenericRigger::rig()
isMainBodyVerticalAligned = fabs(yMax.y() - yMin.y()) > fabs(zMax.z() - zMin.z());
}
// Collect all limbs
auto nosideLimbIndicies = m_marksMap.find(std::make_pair(BoneMark::Limb, SkeletonSide::None));
// Collect all branchs
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 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
auto sortLimbIndiciesInSpineOrder = [=](const int &first, const int &second) {
auto sortMarkIndiciesInSpineOrder = [=](const int &first, const int &second) {
return isMainBodyVerticalAligned ?
(m_marks[first].bonePosition.y() < m_marks[second].bonePosition.y()) :
(m_marks[first].bonePosition.z() < m_marks[second].bonePosition.z());
};
if (nosideLimbIndicies != m_marksMap.end())
std::sort(nosideLimbIndicies->second.begin(), nosideLimbIndicies->second.end(), sortLimbIndiciesInSpineOrder);
if (leftLimbIndicies != m_marksMap.end())
std::sort(leftLimbIndicies->second.begin(), leftLimbIndicies->second.end(), sortLimbIndiciesInSpineOrder);
if (rightLimbIndicies != m_marksMap.end())
std::sort(rightLimbIndicies->second.begin(), rightLimbIndicies->second.end(), sortLimbIndiciesInSpineOrder);
std::sort(nosideMarkIndicies.begin(), nosideMarkIndicies.end(), sortMarkIndiciesInSpineOrder);
std::sort(leftMarkIndicies.begin(), leftMarkIndicies.end(), sortMarkIndiciesInSpineOrder);
std::sort(righMarkIndicies.begin(), righMarkIndicies.end(), sortMarkIndiciesInSpineOrder);
const static std::vector<int> s_empty;
const std::vector<int> *limbColumns[3] = {leftLimbIndicies != m_marksMap.end() ? &leftLimbIndicies->second : &s_empty,
nosideLimbIndicies != m_marksMap.end() ? &nosideLimbIndicies->second : &s_empty,
rightLimbIndicies != m_marksMap.end() ? &rightLimbIndicies->second : &s_empty
const std::vector<int> *chainColumns[3] = {&leftMarkIndicies,
&nosideMarkIndicies,
&righMarkIndicies
};
enum Column
{
@ -237,18 +257,18 @@ bool GenericRigger::rig()
{
float coord;
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};
sideIndicies[Column::Noside] < limbColumns[Column::Noside]->size() ||
sideIndicies[Column::Left] < limbColumns[Column::Left]->size() ||
sideIndicies[Column::Right] < limbColumns[Column::Right]->size();) {
sideIndicies[Column::Noside] < chainColumns[Column::Noside]->size() ||
sideIndicies[Column::Left] < chainColumns[Column::Left]->size() ||
sideIndicies[Column::Right] < chainColumns[Column::Right]->size();) {
float choosenCoord = std::numeric_limits<float>::max();
int choosenColumn = -1;
for (size_t side = Column::Left; side <= Column::Right; ++side) {
if (sideIndicies[side] < limbColumns[side]->size()) {
const auto &mark = m_marks[limbColumns[side]->at(sideIndicies[side])];
if (sideIndicies[side] < chainColumns[side]->size()) {
const auto &mark = m_marks[chainColumns[side]->at(sideIndicies[side])];
const auto &coord = isMainBodyVerticalAligned ? mark.bonePosition.y() :
mark.bonePosition.z();
if (coord < choosenCoord) {
@ -261,42 +281,69 @@ bool GenericRigger::rig()
qDebug() << "Should not come here, coord corrupted";
break;
}
// Find all the limbs before or near this choosenCoord
QVector3D sumOfLimbPositions;
int countOfLimbs = 0;
std::set<int> limbMarkIndicies;
// Find all the chains before or near this choosenCoord
QVector3D sumOfChainPositions;
int countOfChains = 0;
std::set<int> chainMarkIndicies;
for (size_t side = Column::Left; side <= Column::Right; ++side) {
if (sideIndicies[side] < limbColumns[side]->size()) {
const auto &mark = m_marks[limbColumns[side]->at(sideIndicies[side])];
if (sideIndicies[side] < chainColumns[side]->size()) {
const auto &mark = m_marks[chainColumns[side]->at(sideIndicies[side])];
const auto &coord = isMainBodyVerticalAligned ? mark.bonePosition.y() :
mark.bonePosition.z();
if (coord <= choosenCoord + 0.001) {
limbMarkIndicies.insert(limbColumns[side]->at(sideIndicies[side]));
sumOfLimbPositions += mark.bonePosition;
++countOfLimbs;
chainMarkIndicies.insert(chainColumns[side]->at(sideIndicies[side]));
sumOfChainPositions += mark.bonePosition;
++countOfChains;
++sideIndicies[side];
}
}
}
if (countOfLimbs <= 0) {
if (countOfChains <= 0) {
qDebug() << "Should not come here, there must be at least one limb";
break;
}
//qDebug() << "Create new spine node from" << countOfLimbs << "limbs current coord:" << choosenCoord;
spineNodes.push_back(SpineNode());
SpineNode &spineNode = spineNodes.back();
rawSpineNodes.push_back(SpineNode());
SpineNode &spineNode = rawSpineNodes.back();
spineNode.coord = choosenCoord;
spineNode.limbMarkIndicies = limbMarkIndicies;
spineNode.position = sumOfLimbPositions / countOfLimbs;
spineNode.chainMarkIndicies = chainMarkIndicies;
spineNode.position = sumOfChainPositions / countOfChains;
}
if (spineNodes.empty()) {
if (rawSpineNodes.empty()) {
qDebug() << "Couldn't find limbs to create a spine";
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;
m_resultBones.push_back(RiggerBone());
@ -304,47 +351,54 @@ bool GenericRigger::rig()
bodyBone.index = m_resultBones.size() - 1;
bodyBone.name = Rigger::rootBoneName;
bodyBone.headPosition = QVector3D(0, 0, 0);
bodyBone.hasButton = true;
bodyBone.button = {spineNodes.size(), 0};
bodyBone.buttonParameterType = RiggerButtonParameterType::Translation;
boneIndexMap[bodyBone.name] = bodyBone.index;
auto remainingSpineVerticies = bodyVerticies;
const std::vector<QColor> twoColorsForSpine = {QColor(0x57, 0x43, 0x98), Qt::white};
const std::vector<QColor> twoColorsForLimb = {BoneMarkToColor(BoneMark::Joint), BoneMarkToColor(BoneMark::Hip)};
const std::vector<QColor> twoColorsForSpine = {BoneMarkToColor(BoneMark::Neck), BoneMarkToColor(BoneMark::Tail)};
const std::vector<QColor> twoColorsForLimb = {BoneMarkToColor(BoneMark::Joint), BoneMarkToColor(BoneMark::Limb)};
int spineGenerateOrder = 1;
std::map<std::pair<QString, SkeletonSide>, int> chainOrderMapBySide;
for (int spineNodeIndex = 0; spineNodeIndex < (int)spineNodes.size(); ++spineNodeIndex) {
const auto &spineNode = spineNodes[spineNodeIndex];
std::set<int> spineBoneVertices;
QVector3D tailPosition;
int buttonRow = spineNodes.size() - spineGenerateOrder;
if (spineNodeIndex + 1 < (int)spineNodes.size()) {
std::set<int> frontOrCoincidentVertices;
std::set<int> backVertices;
float distance = (spineNodes[spineNodeIndex + 1].position - spineNode.position).length();
auto planeNormal = (spineNodes[spineNodeIndex + 1].position - spineNode.position).normalized();
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 perpVector = isMainBodyVerticalAligned ? QVector3D(1, 0, 0) : QVector3D(0, 1, 0);
auto vectorOnPlane = QVector3D::crossProduct(planeNormal, perpVector);
// Move this point to far away, so the checking vector will not collapse with the plane normal
pointOnPlane += vectorOnPlane.normalized() * 1000;
{
std::set<int> frontOrCoincidentVertices;
std::set<int> backVertices;
splitVerticesByPlane(remainingSpineVerticies,
pointOnPlane,
planeNormal,
frontOrCoincidentVertices,
backVertices);
spineBoneVertices = backVertices;
}
// Split again, this time, we step back a little bit
pointOnPlane = spineNode.position + planeNormal * distance * 0.85;
pointOnPlane += vectorOnPlane.normalized() * 1000;
{
std::set<int> frontOrCoincidentVertices;
std::set<int> backVertices;
splitVerticesByPlane(remainingSpineVerticies,
pointOnPlane,
planeNormal,
frontOrCoincidentVertices,
backVertices);
remainingSpineVerticies = frontOrCoincidentVertices;
}
tailPosition = spineNodes[spineNodeIndex + 1].position;
} else {
spineBoneVertices = remainingSpineVerticies;
@ -362,16 +416,15 @@ bool GenericRigger::rig()
spineBoneHeadPosition.setZ(spineNode.coord);
}
QString spineName = namingSpine(spineGenerateOrder);
m_resultBones.push_back(RiggerBone());
RiggerBone &spineBone = m_resultBones.back();
spineBone.index = m_resultBones.size() - 1;
spineBone.name = "Spine" + QString::number(spineGenerateOrder);
spineBone.name = spineName;
spineBone.headPosition = spineBoneHeadPosition;
spineBone.tailPosition = tailPosition;
spineBone.color = twoColorsForSpine[spineGenerateOrder % 2];
spineBone.hasButton = true;
spineBone.button = {buttonRow, 0};
spineBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll;
addVerticesToWeights(spineBoneVertices, spineBone.index);
boneIndexMap[spineBone.name] = spineBone.index;
@ -381,101 +434,128 @@ bool GenericRigger::rig()
m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition = spineBone.headPosition;
m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(spineBone.index);
} else {
m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder - 1)]].tailPosition = spineBone.headPosition;
m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder - 1)]].children.push_back(spineBone.index);
m_resultBones[boneIndexMap[namingSpine(spineGenerateOrder - 1)]].tailPosition = spineBone.headPosition;
m_resultBones[boneIndexMap[namingSpine(spineGenerateOrder - 1)]].children.push_back(spineBone.index);
}
int limbGenerateOrder = 1;
for (const auto &limbMarkIndex: spineNode.limbMarkIndicies) {
const auto &limbMark = m_marks[limbMarkIndex];
for (const auto &chainMarkIndex: spineNode.chainMarkIndicies) {
const auto &chainMark = m_marks[chainMarkIndex];
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());
RiggerBone &ribBone = m_resultBones.back();
ribBone.index = m_resultBones.size() - 1;
ribBone.name = "Rib" + QString::number(spineGenerateOrder) + "x" + QString::number(limbGenerateOrder);
ribBone.name = namingConnector(spineName, chainName);
ribBone.headPosition = spineBoneHeadPosition;
boneIndexMap[ribBone.name] = ribBone.index;
if (1 == spineGenerateOrder) {
m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(ribBone.index);
} 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;
collectJointsForLimb(limbMarkIndex, jointMarkIndicies);
if (!collectJontsForChain(chainMarkIndex, jointMarkIndicies)) {
m_jointErrorItems.push_back(chainName);
}
//qDebug() << "Limb markIndex:" << limbMarkIndex << " joints:" << jointMarkIndicies.size();
int jointGenerateOrder = 1;
auto boneColor = [&]() {
return twoColorsForLimb[(jointGenerateOrder + 1) % 2];
};
auto boneColumn = [&]() {
return limbMark.boneSide == SkeletonSide::Left ? jointGenerateOrder : -jointGenerateOrder;
return twoColorsForLimb[jointGenerateOrder % 2];
};
auto addToParentBone = [&](QVector3D headPosition, SkeletonSide side, int boneIndex) {
if (1 == jointGenerateOrder) {
m_resultBones[boneIndexMap["Rib" + QString::number(spineGenerateOrder) + "x" + QString::number(limbGenerateOrder)]].tailPosition = headPosition;
m_resultBones[boneIndexMap["Rib" + QString::number(spineGenerateOrder) + "x" + QString::number(limbGenerateOrder)]].children.push_back(boneIndex);
m_resultBones[boneIndexMap[namingConnector(spineName, chainName)]].tailPosition = headPosition;
m_resultBones[boneIndexMap[namingConnector(spineName, chainName)]].children.push_back(boneIndex);
} 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]].children.push_back(boneIndex);
}
};
std::set<int> remainingLimbVertices;
addTrianglesToVertices(limbMark.smallGroup(), remainingLimbVertices);
addTrianglesToVertices(limbMark.markTriangles, remainingLimbVertices);
addTrianglesToVertices(chainMark.smallGroup(), remainingLimbVertices);
addTrianglesToVertices(chainMark.markTriangles, remainingLimbVertices);
QVector3D lastPosition = spineBoneHeadPosition;
for (const auto &jointMarkIndex: jointMarkIndicies) {
std::vector<QVector3D> jointPositions;
for (jointGenerateOrder = 1; jointGenerateOrder <= (int)jointMarkIndicies.size(); ++jointGenerateOrder) {
int jointMarkIndex = jointMarkIndicies[jointGenerateOrder - 1];
const auto jointMark = m_marks[jointMarkIndex];
int buttonColumn = boneColumn();
m_resultBones.push_back(RiggerBone());
RiggerBone &limbBone = m_resultBones.back();
limbBone.index = m_resultBones.size() - 1;
limbBone.name = namingLimb(spineGenerateOrder, jointMark.boneSide, limbGenerateOrder, jointGenerateOrder);
limbBone.headPosition = jointMark.bonePosition;
limbBone.baseNormal = jointMark.baseNormal;
limbBone.color = boneColor();
limbBone.hasButton = true;
limbBone.button = {buttonRow, buttonColumn};
limbBone.buttonParameterType = jointGenerateOrder == 1 ? RiggerButtonParameterType::PitchYawRoll : RiggerButtonParameterType::Intersection;
if (jointGenerateOrder == (int)jointMarkIndicies.size()) {
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, jointMark.bonePosition);
resolveBoundingBox(remainingLimbVertices, extremCoords[0], extremCoords[1], extremCoords[2], extremCoords[3], extremCoords[4], extremCoords[5]);
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;
QVector3D choosenExtreamCoord = jointPositions.back();
for (size_t i = 0; i < 6; ++i) {
const auto &position = extremCoords[i];
auto length2 = (position - jointMark.bonePosition).lengthSquared();
auto length2 = (position - jointPositions.back()).lengthSquared();
if (length2 >= maxDistance2) {
maxDistance2 = length2;
choosenExtreamCoord = position;
}
}
limbBone.tailPosition = choosenExtreamCoord;
addVerticesToWeights(remainingLimbVertices, limbBone.index);
jointPositions.push_back(choosenExtreamCoord);
QVector3D lastPosition = spineBoneHeadPosition;
for (jointGenerateOrder = 1; jointGenerateOrder <= (int)jointMarkIndicies.size(); ++jointGenerateOrder) {
int jointMarkIndex = jointMarkIndicies[jointGenerateOrder - 1];
const auto &jointMark = m_marks[jointMarkIndex];
m_resultBones.push_back(RiggerBone());
RiggerBone &jointBone = m_resultBones.back();
jointBone.index = m_resultBones.size() - 1;
jointBone.name = namingChain(chainBaseName, chainMark.boneSide, chainGenerateOrder, spineNode.chainMarkIndicies.size(), jointGenerateOrder);
jointBone.headPosition = jointPositions[jointGenerateOrder - 1];
jointBone.tailPosition = jointPositions[jointGenerateOrder];
jointBone.baseNormal = jointMark.baseNormal;
jointBone.color = boneColor();
if (jointGenerateOrder == (int)jointMarkIndicies.size()) {
addVerticesToWeights(remainingLimbVertices, jointBone.index);
} else {
int nextJointMarkIndex = jointMarkIndicies[jointGenerateOrder];
const auto &nextJointMark = m_marks[nextJointMarkIndex];
auto nextBoneDirection = (jointPositions[jointGenerateOrder + 1] - jointPositions[jointGenerateOrder]).normalized();
auto currentBoneDirection = (jointBone.tailPosition - jointBone.headPosition).normalized();
auto planeNormal = (currentBoneDirection + nextBoneDirection).normalized();
auto pointOnPlane = jointBone.tailPosition + planeNormal * nextJointMark.nodeRadius;
{
std::set<int> frontOrCoincidentVertices;
std::set<int> backVertices;
limbBone.tailPosition = m_marks[jointMarkIndicies[jointGenerateOrder]].bonePosition;
auto previousBoneDirection = (limbBone.headPosition - lastPosition).normalized();
auto currentBoneDirection = (limbBone.tailPosition - limbBone.headPosition).normalized();
auto planeNormal = (previousBoneDirection + currentBoneDirection).normalized();
float previousBoneLength = (limbBone.headPosition - lastPosition).length();
float currentBoneLength = (limbBone.tailPosition - limbBone.headPosition).length();
auto pointOnPlane = limbBone.tailPosition + currentBoneDirection * currentBoneLength * 0.25;
splitVerticesByPlane(remainingLimbVertices,
pointOnPlane,
planeNormal,
frontOrCoincidentVertices,
backVertices);
addVerticesToWeights(backVertices, limbBone.index);
pointOnPlane = limbBone.tailPosition - previousBoneDirection * previousBoneLength * 0.1 * (currentBoneLength / std::max(previousBoneLength, (float)0.00001));
addVerticesToWeights(backVertices, jointBone.index);
}
pointOnPlane = jointBone.tailPosition - planeNormal * nextJointMark.nodeRadius;
{
std::set<int> frontOrCoincidentVertices;
std::set<int> backVertices;
splitVerticesByPlane(remainingLimbVertices,
pointOnPlane,
planeNormal,
@ -483,23 +563,20 @@ bool GenericRigger::rig()
backVertices);
remainingLimbVertices = frontOrCoincidentVertices;
}
boneIndexMap[limbBone.name] = limbBone.index;
addToParentBone(limbBone.headPosition, jointMark.boneSide, limbBone.index);
lastPosition = jointMark.bonePosition;
++jointGenerateOrder;
}
++limbGenerateOrder;
boneIndexMap[jointBone.name] = jointBone.index;
addToParentBone(jointBone.headPosition, chainMark.boneSide, jointBone.index);
lastPosition = jointPositions[jointGenerateOrder - 1];
}
++chainGenerateOrder;
}
++spineGenerateOrder;
}
normalizeButtonColumns();
// Finalize weights
for (auto &weights: m_resultWeights) {
weights.second.finalizeWeights();
@ -508,27 +585,22 @@ bool GenericRigger::rig()
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();
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;
}
return namingChainPrefix(baseName, side, orderInSide, totalInSide) + "_Joint" + QString::number(jointOrder);
}
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,
Neck,
Shoulder,
Elbow,
Wrist,
Hip,
Knee,
Ankle,
Limb,
Tail,
Joint,
Count
};
#define BoneMarkHasSide(mark) ((mark) != BoneMark::Neck)
#define BoneMarkHasSide(mark) ((mark) == BoneMark::Limb)
QColor BoneMarkToColor(BoneMark mark);
#define IMPL_BoneMarkToColor \
QColor BoneMarkToColor(BoneMark mark) \
{ \
switch (mark) { \
case BoneMark::Neck: \
return QColor(0xfc, 0x0d, 0x1b); \
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); \
return QColor(0x51, 0xba, 0xf2); \
case BoneMark::Limb: \
return QColor(0xf7, 0xf7, 0xf7); \
return QColor(0x29, 0xfd, 0x2f); \
case BoneMark::Tail: \
return QColor(0xff, 0xfd, 0x38); \
case BoneMark::Joint: \
return QColor(0xfd, 0xa9, 0xaa); \
return QColor(0xcf, 0x83, 0xe1); \
case BoneMark::None: \
return Qt::transparent; \
default: \
@ -54,20 +39,10 @@ const char *BoneMarkToString(BoneMark mark) \
switch (mark) { \
case BoneMark::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: \
return "Limb"; \
case BoneMark::Tail: \
return "Tail"; \
case BoneMark::Joint: \
return "Joint"; \
case BoneMark::None: \
@ -83,20 +58,10 @@ BoneMark BoneMarkFromString(const char *markString) \
QString mark = markString; \
if (mark == "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") \
return BoneMark::Limb; \
if (mark == "Tail") \
return BoneMark::Tail; \
if (mark == "Joint") \
return BoneMark::Joint; \
return BoneMark::None; \
@ -108,22 +73,12 @@ QString BoneMarkToDispName(BoneMark mark) \
switch (mark) { \
case BoneMark::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: \
return QObject::tr("Limb (Generic)"); \
return QObject::tr("Limb"); \
case BoneMark::Tail: \
return QObject::tr("Tail"); \
case BoneMark::Joint: \
return QObject::tr("Joint (Generic)"); \
return QObject::tr("Joint"); \
case BoneMark::None: \
return QObject::tr("None"); \
default: \

View File

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

View File

@ -499,8 +499,7 @@ public:
bool isPostProcessResultObsolete() const;
void collectComponentDescendantParts(QUuid componentId, std::vector<QUuid> &partIds) const;
void collectComponentDescendantComponents(QUuid componentId, std::vector<QUuid> &componentIds) const;
const std::vector<QString> &resultRigMissingMarkNames() const;
const std::vector<QString> &resultRigErrorMarkNames() const;
const std::vector<std::pair<QtMsgType, QString>> &resultRigMessages() const;
const Outcome &currentRiggedOutcome() const;
bool currentRigSucceed() const;
bool isMeshGenerating() const;
@ -652,8 +651,7 @@ private:
std::deque<HistoryItem> m_undoItems;
std::deque<HistoryItem> m_redoItems;
GeneratedCacheContext m_generatedCacheContext;
std::vector<QString> m_resultRigMissingMarkNames;
std::vector<QString> m_resultRigErrorMarkNames;
std::vector<std::pair<QtMsgType, QString>> m_resultRigMessages;
};
#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 = [=]() {
if (m_document->currentRigSucceed()) {
if (m_document->rigType == RigType::Tetrapod) {
if (m_document->rigType == RigType::Animal) {
infoLabel->setText("");
infoLabel->hide();
addMotionButton->show();

View File

@ -189,6 +189,7 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
auto findFirst = boneIndexToHeadNodeIdMap.find(edgePair.first);
if (findFirst == boneIndexToHeadNodeIdMap.end()) {
const auto &bone = (*rigBones)[edgePair.first];
if (!bone.name.startsWith("Virtual_")) {
SkeletonNode node;
node.partId = m_bonesPartId;
node.id = QUuid::createUuid();
@ -200,12 +201,14 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
newAddedNodeIds.insert(node.id);
boneIndexToHeadNodeIdMap[edgePair.first] = node.id;
firstNodeId = node.id;
}
} else {
firstNodeId = findFirst->second;
}
auto findSecond = boneIndexToHeadNodeIdMap.find(edgePair.second);
if (findSecond == boneIndexToHeadNodeIdMap.end()) {
const auto &bone = (*rigBones)[edgePair.second];
if (!bone.name.startsWith("Virtual_")) {
SkeletonNode node;
node.partId = m_bonesPartId;
node.id = QUuid::createUuid();
@ -217,10 +220,14 @@ void PoseDocument::updateRigBones(const std::vector<RiggerBone> *rigBones, const
newAddedNodeIds.insert(node.id);
boneIndexToHeadNodeIdMap[edgePair.second] = node.id;
secondNodeId = node.id;
}
} else {
secondNodeId = findSecond->second;
}
if (firstNodeId.isNull() || secondNodeId.isNull())
continue;
SkeletonEdge edge;
edge.partId = m_bonesPartId;
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[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];
if (bone.name.startsWith("Virtual_"))
continue;
if (bone.children.empty()) {
const QUuid &firstNodeId = boneIndexToHeadNodeIdMap[i];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,7 +11,8 @@ QString Rigger::rootBoneName = "Body";
Rigger::Rigger(const std::vector<QVector3D> &verticesPositions,
const std::set<MeshSplitterTriangle> &inputTriangles) :
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 (!mark.split(m_inputTriangles, m_maxCutOffSplitterExpandRound)) {
m_marksMap[std::make_pair(mark.boneMark, mark.boneSide)].push_back(m_marks.size() - 1);
m_errorMarkNames.push_back(SkeletonSideToDispName(mark.boneSide) + " " + BoneMarkToDispName(mark.boneMark));
m_messages.push_back(std::make_pair(QtCriticalMsg,
tr("Mark \"%1 %2\" couldn't cut off the mesh").arg(SkeletonSideToDispName(mark.boneSide)).arg(BoneMarkToString(boneMark))));
m_cutoffErrorItems.push_back(SkeletonSideToDispName(mark.boneSide) + " " + BoneMarkToDispName(mark.boneMark));
return false;
}
}
@ -73,6 +71,22 @@ bool Rigger::addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bo
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;
}
@ -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 "skeletonside.h"
enum class RiggerButtonParameterType
{
None = 0,
PitchYawRoll,
Intersection,
Translation
};
class RiggerMark
{
public:
@ -65,9 +57,6 @@ public:
QVector3D headPosition;
QVector3D tailPosition;
QColor color;
bool hasButton = false;
RiggerButtonParameterType buttonParameterType = RiggerButtonParameterType::None;
std::pair<int, int> button = {0, 0};
QVector3D baseNormal;
std::vector<int> children;
};
@ -122,8 +111,6 @@ public:
const std::vector<std::pair<QtMsgType, QString>> &messages();
const std::vector<RiggerBone> &resultBones();
const std::map<int, RiggerVertexWeights> &resultWeights();
const std::vector<QString> &missingMarkNames();
const std::vector<QString> &errorMarkNames();
virtual bool rig() = 0;
static QString rootBoneName;
protected:
@ -152,9 +139,10 @@ protected:
std::map<std::pair<BoneMark, SkeletonSide>, std::vector<int>> m_marksMap;
std::vector<RiggerBone> m_resultBones;
std::map<int, RiggerVertexWeights> m_resultWeights;
std::vector<QString> m_missingMarkNames;
std::vector<QString> m_errorMarkNames;
std::vector<QString> m_cutoffErrorItems;
std::vector<QString> m_jointErrorItems;
static size_t m_maxCutOffSplitterExpandRound;
bool m_extraMessagedAdded;
};
#endif

View File

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

View File

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

View File

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

View File

@ -23,8 +23,7 @@ private:
const Document *m_document = nullptr;
QComboBox *m_rigTypeBox = nullptr;
ModelWidget *m_rigWeightRenderWidget = nullptr;
InfoLabel *m_missingMarksInfoLabel = nullptr;
InfoLabel *m_errorMarksInfoLabel = nullptr;
InfoLabel *m_infoLabel = nullptr;
};
#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