Unify rig type of generic and tetrapod as animal
Marks also reduced to four types: Neck, Limb, Tail and Joint.master
parent
d72738feb1
commit
2c8975b1cd
14
dust3d.pro
14
dust3d.pro
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
splitVerticesByPlane(remainingSpineVerticies,
|
||||
pointOnPlane,
|
||||
planeNormal,
|
||||
frontOrCoincidentVertices,
|
||||
backVertices);
|
||||
spineBoneVertices = backVertices;
|
||||
{
|
||||
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;
|
||||
splitVerticesByPlane(remainingSpineVerticies,
|
||||
pointOnPlane,
|
||||
planeNormal,
|
||||
frontOrCoincidentVertices,
|
||||
backVertices);
|
||||
remainingSpineVerticies = frontOrCoincidentVertices;
|
||||
{
|
||||
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,125 +434,149 @@ 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);
|
||||
|
||||
QVector3D lastPosition = spineBoneHeadPosition;
|
||||
for (const auto &jointMarkIndex: jointMarkIndicies) {
|
||||
addTrianglesToVertices(chainMark.smallGroup(), remainingLimbVertices);
|
||||
addTrianglesToVertices(chainMark.markTriangles, remainingLimbVertices);
|
||||
|
||||
std::vector<QVector3D> jointPositions;
|
||||
for (jointGenerateOrder = 1; jointGenerateOrder <= (int)jointMarkIndicies.size(); ++jointGenerateOrder) {
|
||||
int jointMarkIndex = jointMarkIndicies[jointGenerateOrder - 1];
|
||||
const auto jointMark = m_marks[jointMarkIndex];
|
||||
int buttonColumn = boneColumn();
|
||||
jointPositions.push_back(jointMark.bonePosition);
|
||||
}
|
||||
std::set<int> lastJointBoneVerticies;
|
||||
if (jointPositions.size() >= 2)
|
||||
{
|
||||
QVector3D cutoffPlaneNormal = (jointPositions[jointPositions.size() - 1] - jointPositions[jointPositions.size() - 2]).normalized();
|
||||
QVector3D pointOnPlane = jointPositions[jointPositions.size() - 1];
|
||||
std::set<int> frontOrCoincidentVertices;
|
||||
std::set<int> backVertices;
|
||||
splitVerticesByPlane(remainingLimbVertices,
|
||||
pointOnPlane,
|
||||
cutoffPlaneNormal,
|
||||
frontOrCoincidentVertices,
|
||||
backVertices);
|
||||
lastJointBoneVerticies = frontOrCoincidentVertices;
|
||||
} else {
|
||||
lastJointBoneVerticies = remainingLimbVertices;
|
||||
}
|
||||
// Calculate the tail position from remaining verticies
|
||||
std::vector<QVector3D> extremCoords(6, jointPositions.back());
|
||||
resolveBoundingBox(lastJointBoneVerticies, extremCoords[0], extremCoords[1], extremCoords[2], extremCoords[3], extremCoords[4], extremCoords[5]);
|
||||
float maxDistance2 = std::numeric_limits<float>::min();
|
||||
QVector3D choosenExtreamCoord = jointPositions.back();
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
const auto &position = extremCoords[i];
|
||||
auto length2 = (position - jointPositions.back()).lengthSquared();
|
||||
if (length2 >= maxDistance2) {
|
||||
maxDistance2 = length2;
|
||||
choosenExtreamCoord = position;
|
||||
}
|
||||
}
|
||||
jointPositions.push_back(choosenExtreamCoord);
|
||||
|
||||
QVector3D lastPosition = spineBoneHeadPosition;
|
||||
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 &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;
|
||||
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()) {
|
||||
// Calculate the tail position from remaining verticies
|
||||
std::vector<QVector3D> extremCoords(6, jointMark.bonePosition);
|
||||
resolveBoundingBox(remainingLimbVertices, extremCoords[0], extremCoords[1], extremCoords[2], extremCoords[3], extremCoords[4], extremCoords[5]);
|
||||
float maxDistance2 = std::numeric_limits<float>::min();
|
||||
QVector3D choosenExtreamCoord;
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
const auto &position = extremCoords[i];
|
||||
auto length2 = (position - jointMark.bonePosition).lengthSquared();
|
||||
if (length2 >= maxDistance2) {
|
||||
maxDistance2 = length2;
|
||||
choosenExtreamCoord = position;
|
||||
}
|
||||
}
|
||||
limbBone.tailPosition = choosenExtreamCoord;
|
||||
addVerticesToWeights(remainingLimbVertices, limbBone.index);
|
||||
addVerticesToWeights(remainingLimbVertices, jointBone.index);
|
||||
} else {
|
||||
std::set<int> frontOrCoincidentVertices;
|
||||
std::set<int> backVertices;
|
||||
limbBone.tailPosition = m_marks[jointMarkIndicies[jointGenerateOrder]].bonePosition;
|
||||
auto previousBoneDirection = (limbBone.headPosition - lastPosition).normalized();
|
||||
auto currentBoneDirection = (limbBone.tailPosition - limbBone.headPosition).normalized();
|
||||
auto planeNormal = (previousBoneDirection + currentBoneDirection).normalized();
|
||||
float previousBoneLength = (limbBone.headPosition - lastPosition).length();
|
||||
float currentBoneLength = (limbBone.tailPosition - limbBone.headPosition).length();
|
||||
auto pointOnPlane = limbBone.tailPosition + currentBoneDirection * currentBoneLength * 0.25;
|
||||
splitVerticesByPlane(remainingLimbVertices,
|
||||
pointOnPlane,
|
||||
planeNormal,
|
||||
frontOrCoincidentVertices,
|
||||
backVertices);
|
||||
addVerticesToWeights(backVertices, limbBone.index);
|
||||
pointOnPlane = limbBone.tailPosition - previousBoneDirection * previousBoneLength * 0.1 * (currentBoneLength / std::max(previousBoneLength, (float)0.00001));
|
||||
splitVerticesByPlane(remainingLimbVertices,
|
||||
pointOnPlane,
|
||||
planeNormal,
|
||||
frontOrCoincidentVertices,
|
||||
backVertices);
|
||||
remainingLimbVertices = frontOrCoincidentVertices;
|
||||
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;
|
||||
splitVerticesByPlane(remainingLimbVertices,
|
||||
pointOnPlane,
|
||||
planeNormal,
|
||||
frontOrCoincidentVertices,
|
||||
backVertices);
|
||||
addVerticesToWeights(backVertices, jointBone.index);
|
||||
}
|
||||
pointOnPlane = jointBone.tailPosition - planeNormal * nextJointMark.nodeRadius;
|
||||
{
|
||||
std::set<int> frontOrCoincidentVertices;
|
||||
std::set<int> backVertices;
|
||||
splitVerticesByPlane(remainingLimbVertices,
|
||||
pointOnPlane,
|
||||
planeNormal,
|
||||
frontOrCoincidentVertices,
|
||||
backVertices);
|
||||
remainingLimbVertices = frontOrCoincidentVertices;
|
||||
}
|
||||
}
|
||||
|
||||
boneIndexMap[limbBone.name] = limbBone.index;
|
||||
addToParentBone(limbBone.headPosition, jointMark.boneSide, limbBone.index);
|
||||
boneIndexMap[jointBone.name] = jointBone.index;
|
||||
addToParentBone(jointBone.headPosition, chainMark.boneSide, jointBone.index);
|
||||
|
||||
lastPosition = jointMark.bonePosition;
|
||||
|
||||
++jointGenerateOrder;
|
||||
lastPosition = jointPositions[jointGenerateOrder - 1];
|
||||
}
|
||||
|
||||
++limbGenerateOrder;
|
||||
++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));
|
||||
}
|
|
@ -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
|
|
@ -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: \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ¤tRiggedOutcome() 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
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
|
|
|
@ -189,38 +189,45 @@ 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];
|
||||
SkeletonNode node;
|
||||
node.partId = m_bonesPartId;
|
||||
node.id = QUuid::createUuid();
|
||||
node.setRadius(m_nodeRadius);
|
||||
node.x = bone.headPosition.x() + 0.5;
|
||||
node.y = -bone.headPosition.y() + 0.5;
|
||||
node.z = -bone.headPosition.z() + 1;
|
||||
nodeMap[node.id] = node;
|
||||
newAddedNodeIds.insert(node.id);
|
||||
boneIndexToHeadNodeIdMap[edgePair.first] = node.id;
|
||||
firstNodeId = node.id;
|
||||
if (!bone.name.startsWith("Virtual_")) {
|
||||
SkeletonNode node;
|
||||
node.partId = m_bonesPartId;
|
||||
node.id = QUuid::createUuid();
|
||||
node.setRadius(m_nodeRadius);
|
||||
node.x = bone.headPosition.x() + 0.5;
|
||||
node.y = -bone.headPosition.y() + 0.5;
|
||||
node.z = -bone.headPosition.z() + 1;
|
||||
nodeMap[node.id] = node;
|
||||
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];
|
||||
SkeletonNode node;
|
||||
node.partId = m_bonesPartId;
|
||||
node.id = QUuid::createUuid();
|
||||
node.setRadius(m_nodeRadius);
|
||||
node.x = bone.headPosition.x() + 0.5;
|
||||
node.y = -bone.headPosition.y() + 0.5;
|
||||
node.z = -bone.headPosition.z() + 1;
|
||||
nodeMap[node.id] = node;
|
||||
newAddedNodeIds.insert(node.id);
|
||||
boneIndexToHeadNodeIdMap[edgePair.second] = node.id;
|
||||
secondNodeId = node.id;
|
||||
if (!bone.name.startsWith("Virtual_")) {
|
||||
SkeletonNode node;
|
||||
node.partId = m_bonesPartId;
|
||||
node.id = QUuid::createUuid();
|
||||
node.setRadius(m_nodeRadius);
|
||||
node.x = bone.headPosition.x() + 0.5;
|
||||
node.y = -bone.headPosition.y() + 0.5;
|
||||
node.z = -bone.headPosition.z() + 1;
|
||||
nodeMap[node.id] = node;
|
||||
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];
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
#include "skeletongraphicswidget.h"
|
||||
#include "posedocument.h"
|
||||
|
||||
typedef RiggerButtonParameterType PopupWidgetType;
|
||||
|
||||
class PoseEditWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,12 +162,11 @@ void RigGenerator::generate()
|
|||
qDebug() << "Rig succeed";
|
||||
} else {
|
||||
qDebug() << "Rig failed";
|
||||
if (nullptr != m_autoRigger) {
|
||||
m_missingMarkNames = m_autoRigger->missingMarkNames();
|
||||
m_errorMarkNames = m_autoRigger->errorMarkNames();
|
||||
for (const auto &message: m_autoRigger->messages()) {
|
||||
qDebug() << "errorType:" << message.first << "Message:" << message.second;
|
||||
}
|
||||
}
|
||||
if (nullptr != m_autoRigger) {
|
||||
m_messages = m_autoRigger->messages();
|
||||
for (const auto &message: m_autoRigger->messages()) {
|
||||
qDebug() << "errorType:" << message.first << "Message:" << message.second;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
18
src/rigger.h
18
src/rigger.h
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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: \
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
Loading…
Reference in New Issue