316 lines
12 KiB
C++
316 lines
12 KiB
C++
#include <QGuiApplication>
|
|
#include <QElapsedTimer>
|
|
#include <cmath>
|
|
#include "motionsgenerator.h"
|
|
#include "posemeshcreator.h"
|
|
#include "poserconstruct.h"
|
|
|
|
MotionsGenerator::MotionsGenerator(RigType rigType,
|
|
const std::vector<RiggerBone> *rigBones,
|
|
const std::map<int, RiggerVertexWeights> *rigWeights,
|
|
const Outcome &outcome) :
|
|
m_rigType(rigType),
|
|
m_rigBones(*rigBones),
|
|
m_rigWeights(*rigWeights),
|
|
m_outcome(outcome)
|
|
{
|
|
}
|
|
|
|
MotionsGenerator::~MotionsGenerator()
|
|
{
|
|
for (auto &item: m_resultPreviewMeshs) {
|
|
for (auto &subItem: item.second) {
|
|
delete subItem.second;
|
|
}
|
|
}
|
|
delete m_poser;
|
|
}
|
|
|
|
void MotionsGenerator::addPoseToLibrary(const QUuid &poseId, const std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> &frames)
|
|
{
|
|
m_poses[poseId] = frames;
|
|
}
|
|
|
|
void MotionsGenerator::addMotionToLibrary(const QUuid &motionId, const std::vector<MotionClip> &clips)
|
|
{
|
|
m_motions[motionId] = clips;
|
|
}
|
|
|
|
void MotionsGenerator::addRequirement(const QUuid &motionId)
|
|
{
|
|
m_requiredMotionIds.insert(motionId);
|
|
}
|
|
|
|
const std::set<QUuid> &MotionsGenerator::requiredMotionIds()
|
|
{
|
|
return m_requiredMotionIds;
|
|
}
|
|
|
|
const std::set<QUuid> &MotionsGenerator::generatedMotionIds()
|
|
{
|
|
return m_generatedMotionIds;
|
|
}
|
|
|
|
std::vector<MotionClip> *MotionsGenerator::findMotionClips(const QUuid &motionId)
|
|
{
|
|
auto findMotionResult = m_motions.find(motionId);
|
|
if (findMotionResult == m_motions.end())
|
|
return nullptr;
|
|
std::vector<MotionClip> &clips = findMotionResult->second;
|
|
return &clips;
|
|
}
|
|
|
|
std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> *MotionsGenerator::findPoseFrames(const QUuid &poseId)
|
|
{
|
|
auto findPoseResult = m_poses.find(poseId);
|
|
if (findPoseResult == m_poses.end())
|
|
return nullptr;
|
|
std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> &frames = findPoseResult->second;
|
|
return &frames;
|
|
}
|
|
|
|
void MotionsGenerator::generatePreviewsForOutcomes(const std::vector<std::pair<float, JointNodeTree>> &outcomes, std::vector<std::pair<float, MeshLoader *>> &previews)
|
|
{
|
|
for (const auto &item: outcomes) {
|
|
PoseMeshCreator *poseMeshCreator = new PoseMeshCreator(item.second.nodes(), m_outcome, m_rigWeights);
|
|
poseMeshCreator->createMesh();
|
|
previews.push_back({item.first, poseMeshCreator->takeResultMesh()});
|
|
delete poseMeshCreator;
|
|
}
|
|
}
|
|
|
|
float MotionsGenerator::calculatePoseDuration(const QUuid &poseId)
|
|
{
|
|
const std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> *pose = findPoseFrames(poseId);
|
|
if (nullptr == pose)
|
|
return 0;
|
|
float totalDuration = 0;
|
|
for (const auto &frame: *pose) {
|
|
totalDuration += valueOfKeyInMapOrEmpty(frame.first, "duration").toFloat();
|
|
}
|
|
return totalDuration;
|
|
}
|
|
|
|
float MotionsGenerator::calculateMotionDuration(const QUuid &motionId, std::set<QUuid> &visited)
|
|
{
|
|
const std::vector<MotionClip> *motionClips = findMotionClips(motionId);
|
|
if (!motionClips || motionClips->empty())
|
|
return 0;
|
|
if (visited.find(motionId) != visited.end()) {
|
|
qDebug() << "Found recursive motion link";
|
|
return 0;
|
|
}
|
|
float totalDuration = 0;
|
|
visited.insert(motionId);
|
|
for (const auto &clip: *motionClips) {
|
|
if (clip.clipType == MotionClipType::Interpolation)
|
|
totalDuration += clip.duration;
|
|
else if (clip.clipType == MotionClipType::Pose)
|
|
totalDuration += calculatePoseDuration(clip.linkToId);
|
|
else if (clip.clipType == MotionClipType::Motion)
|
|
totalDuration += calculateMotionDuration(clip.linkToId, visited);
|
|
}
|
|
return totalDuration;
|
|
}
|
|
|
|
void MotionsGenerator::generateMotion(const QUuid &motionId, std::set<QUuid> &visited, std::vector<std::pair<float, JointNodeTree>> &outcomes)
|
|
{
|
|
if (visited.find(motionId) != visited.end()) {
|
|
qDebug() << "Found recursive motion link";
|
|
return;
|
|
}
|
|
|
|
visited.insert(motionId);
|
|
|
|
std::vector<MotionClip> *motionClips = findMotionClips(motionId);
|
|
if (!motionClips || motionClips->empty())
|
|
return;
|
|
|
|
std::vector<float> timePoints;
|
|
float totalDuration = 0;
|
|
for (auto &clip: *motionClips) {
|
|
if (clip.clipType == MotionClipType::Motion) {
|
|
std::set<QUuid> subVisited;
|
|
clip.duration = calculateMotionDuration(clip.linkToId, subVisited);
|
|
} else if (clip.clipType == MotionClipType::Pose) {
|
|
clip.duration = calculatePoseDuration(clip.linkToId);
|
|
}
|
|
timePoints.push_back(totalDuration);
|
|
totalDuration += clip.duration;
|
|
}
|
|
|
|
auto findClipIndexByProgress = [=](float progress) {
|
|
for (size_t i = 0; i < timePoints.size(); ++i) {
|
|
if (progress >= timePoints[i] && i + 1 < timePoints.size() && progress <= timePoints[i + 1])
|
|
return (int)i;
|
|
}
|
|
return (int)timePoints.size() - 1;
|
|
};
|
|
|
|
float interval = 1.0 / m_fps;
|
|
float lastProgress = 0;
|
|
for (float progress = 0; progress < totalDuration; ) {
|
|
int clipIndex = findClipIndexByProgress(progress);
|
|
if (-1 == clipIndex) {
|
|
qDebug() << "findClipIndexByProgress failed, progress:" << progress << "total duration:" << totalDuration << "interval:" << interval;
|
|
break;
|
|
}
|
|
float clipLocalProgress = progress - timePoints[clipIndex];
|
|
const MotionClip &progressClip = (*motionClips)[clipIndex];
|
|
if (MotionClipType::Interpolation == progressClip.clipType) {
|
|
if (clipIndex <= 0) {
|
|
qDebug() << "Clip type is interpolation, but clip sit at begin";
|
|
break;
|
|
}
|
|
if (clipIndex >= (int)motionClips->size() - 1) {
|
|
qDebug() << "Clip type is interpolation, but clip sit at end";
|
|
break;
|
|
}
|
|
const JointNodeTree *beginJointNodeTree = findClipBeginJointNodeTree((*motionClips)[clipIndex - 1]);
|
|
if (nullptr == beginJointNodeTree) {
|
|
qDebug() << "findClipBeginJointNodeTree failed";
|
|
break;
|
|
}
|
|
const JointNodeTree *endJointNodeTree = findClipEndJointNodeTree((*motionClips)[clipIndex + 1]);
|
|
if (nullptr == endJointNodeTree) {
|
|
qDebug() << "findClipEndJointNodeTree failed";
|
|
break;
|
|
}
|
|
outcomes.push_back({progress - lastProgress,
|
|
generateInterpolation(progressClip.interpolationType, *beginJointNodeTree, *endJointNodeTree, clipLocalProgress / std::max((float)0.0001, progressClip.duration))});
|
|
lastProgress = progress;
|
|
progress += interval;
|
|
continue;
|
|
} else if (MotionClipType::Pose == progressClip.clipType) {
|
|
const auto &frames = findPoseFrames(progressClip.linkToId);
|
|
float clipDuration = std::max((float)0.0001, progressClip.duration);
|
|
int frame = clipLocalProgress * frames->size() / clipDuration;
|
|
if (frame >= (int)frames->size())
|
|
frame = frames->size() - 1;
|
|
int previousFrame = frame - 1;
|
|
if (previousFrame < 0)
|
|
previousFrame = frames->size() - 1;
|
|
int nextFrame = frame + 1;
|
|
if (nextFrame >= (int)frames->size())
|
|
nextFrame = 0;
|
|
if (frame >= 0 && frame < (int)frames->size()) {
|
|
const JointNodeTree previousJointNodeTree = poseJointNodeTree(progressClip.linkToId, previousFrame);
|
|
const JointNodeTree jointNodeTree = poseJointNodeTree(progressClip.linkToId, frame);
|
|
const JointNodeTree nextJointNodeTree = poseJointNodeTree(progressClip.linkToId, nextFrame);
|
|
const JointNodeTree middleJointNodeTree = generateInterpolation(InterpolationType::Linear, previousJointNodeTree, nextJointNodeTree, 0.5);
|
|
outcomes.push_back({progress - lastProgress,
|
|
generateInterpolation(InterpolationType::Linear, jointNodeTree, middleJointNodeTree, 0.5)});
|
|
lastProgress = progress;
|
|
}
|
|
progress += interval;
|
|
continue;
|
|
} else if (MotionClipType::Motion == progressClip.clipType) {
|
|
generateMotion(progressClip.linkToId, visited, outcomes);
|
|
progress += progressClip.duration;
|
|
continue;
|
|
}
|
|
progress += interval;
|
|
}
|
|
}
|
|
|
|
JointNodeTree MotionsGenerator::generateInterpolation(InterpolationType interpolationType, const JointNodeTree &first, const JointNodeTree &second, float progress)
|
|
{
|
|
return JointNodeTree::slerp(first, second, calculateInterpolation(interpolationType, progress));
|
|
}
|
|
|
|
const JointNodeTree &MotionsGenerator::poseJointNodeTree(const QUuid &poseId, int frame)
|
|
{
|
|
auto findResult = m_poseJointNodeTreeMap.find({poseId, frame});
|
|
if (findResult != m_poseJointNodeTreeMap.end())
|
|
return findResult->second;
|
|
|
|
const auto &frames = m_poses[poseId];
|
|
|
|
m_poser->reset();
|
|
if (frame < (int)frames.size()) {
|
|
const auto ¶meters = frames[frame].second;
|
|
m_poser->parameters() = parameters;
|
|
}
|
|
m_poser->commit();
|
|
auto insertResult = m_poseJointNodeTreeMap.insert({{poseId, frame}, m_poser->resultJointNodeTree()});
|
|
return insertResult.first->second;
|
|
}
|
|
|
|
const JointNodeTree *MotionsGenerator::findClipBeginJointNodeTree(const MotionClip &clip)
|
|
{
|
|
if (MotionClipType::Pose == clip.clipType) {
|
|
const JointNodeTree &jointNodeTree = poseJointNodeTree(clip.linkToId, 0);
|
|
return &jointNodeTree;
|
|
} else if (MotionClipType::Motion == clip.clipType) {
|
|
const std::vector<MotionClip> *motionClips = findMotionClips(clip.linkToId);
|
|
if (nullptr != motionClips && !motionClips->empty()) {
|
|
return findClipBeginJointNodeTree((*motionClips)[0]);
|
|
}
|
|
return nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const JointNodeTree *MotionsGenerator::findClipEndJointNodeTree(const MotionClip &clip)
|
|
{
|
|
if (MotionClipType::Pose == clip.clipType) {
|
|
const std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> *poseFrames = findPoseFrames(clip.linkToId);
|
|
if (nullptr != poseFrames && !poseFrames->empty()) {
|
|
return &poseJointNodeTree(clip.linkToId, poseFrames->size() - 1);
|
|
}
|
|
return nullptr;
|
|
} else if (MotionClipType::Motion == clip.clipType) {
|
|
const std::vector<MotionClip> *motionClips = findMotionClips(clip.linkToId);
|
|
if (nullptr != motionClips && !motionClips->empty()) {
|
|
return findClipEndJointNodeTree((*motionClips)[motionClips->size() - 1]);
|
|
}
|
|
return nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<std::pair<float, MeshLoader *>> MotionsGenerator::takeResultPreviewMeshs(const QUuid &motionId)
|
|
{
|
|
auto findResult = m_resultPreviewMeshs.find(motionId);
|
|
if (findResult == m_resultPreviewMeshs.end())
|
|
return {};
|
|
auto result = findResult->second;
|
|
m_resultPreviewMeshs.erase(findResult);
|
|
return result;
|
|
}
|
|
|
|
std::vector<std::pair<float, JointNodeTree>> MotionsGenerator::takeResultJointNodeTrees(const QUuid &motionId)
|
|
{
|
|
auto findResult = m_resultJointNodeTrees.find(motionId);
|
|
if (findResult == m_resultJointNodeTrees.end())
|
|
return {};
|
|
return findResult->second;
|
|
}
|
|
|
|
void MotionsGenerator::generate()
|
|
{
|
|
m_poser = newPoser(m_rigType, m_rigBones);
|
|
if (nullptr == m_poser)
|
|
return;
|
|
|
|
for (const auto &motionId: m_requiredMotionIds) {
|
|
std::set<QUuid> visited;
|
|
generateMotion(motionId, visited, m_resultJointNodeTrees[motionId]);
|
|
generatePreviewsForOutcomes(m_resultJointNodeTrees[motionId], m_resultPreviewMeshs[motionId]);
|
|
m_generatedMotionIds.insert(motionId);
|
|
}
|
|
}
|
|
|
|
void MotionsGenerator::process()
|
|
{
|
|
QElapsedTimer countTimeConsumed;
|
|
countTimeConsumed.start();
|
|
|
|
generate();
|
|
|
|
qDebug() << "The motions generation took" << countTimeConsumed.elapsed() << "milliseconds";
|
|
|
|
this->moveToThread(QGuiApplication::instance()->thread());
|
|
emit finished();
|
|
}
|