Add multiple frames per pose support

Usually one pose consists of one frame, however, sometimes multiple frames per one reference sheet for a serial of action could be very useful, such as a sprite sheet. Multiple frames per pose is different with one motion, one motion could contains multiple poses. Currently, the duration of one frame is fixed to 0.042s, it's based on the 24 frames per second calculation.
master
Jeremy Hu 2018-11-09 11:20:48 +08:00
parent a551995235
commit fff39b0835
18 changed files with 394 additions and 154 deletions

View File

@ -307,14 +307,15 @@ QUuid Document::createNode(float x, float y, float z, float radius, QUuid fromNo
return node.id;
}
void Document::addPose(QString name, std::map<QString, std::map<QString, QString>> parameters)
void Document::addPose(QString name, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames, QUuid turnaroundImageId)
{
QUuid newPoseId = QUuid::createUuid();
auto &pose = poseMap[newPoseId];
pose.id = newPoseId;
pose.name = name;
pose.parameters = parameters;
pose.frames = frames;
pose.turnaroundImageId = turnaroundImageId;
pose.dirty = true;
poseIdList.push_back(newPoseId);
@ -402,31 +403,32 @@ void Document::removePose(QUuid poseId)
emit optionsChanged();
}
void Document::setPoseParameters(QUuid poseId, std::map<QString, std::map<QString, QString>> parameters)
void Document::setPoseFrames(QUuid poseId, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames)
{
auto findPoseResult = poseMap.find(poseId);
if (findPoseResult == poseMap.end()) {
qDebug() << "Find pose failed:" << poseId;
return;
}
findPoseResult->second.parameters = parameters;
findPoseResult->second.frames = frames;
findPoseResult->second.dirty = true;
emit posesChanged();
emit poseParametersChanged(poseId);
emit poseFramesChanged(poseId);
emit optionsChanged();
}
void Document::setPoseAttributes(QUuid poseId, std::map<QString, QString> attributes)
void Document::setPoseTurnaroundImageId(QUuid poseId, QUuid imageId)
{
auto findPoseResult = poseMap.find(poseId);
if (findPoseResult == poseMap.end()) {
qDebug() << "Find pose failed:" << poseId;
return;
}
findPoseResult->second.attributes = attributes;
if (findPoseResult->second.turnaroundImageId == imageId)
return;
findPoseResult->second.turnaroundImageId = imageId;
findPoseResult->second.dirty = true;
emit posesChanged();
emit poseAttributesChanged(poseId);
emit poseTurnaroundImageIdChanged(poseId);
emit optionsChanged();
}
@ -971,11 +973,13 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
continue;
}
auto &poseIt = *findPoseResult;
std::map<QString, QString> pose = poseIt.second.attributes;
std::map<QString, QString> pose;
pose["id"] = poseIt.second.id.toString();
if (!poseIt.second.name.isEmpty())
pose["name"] = poseIt.second.name;
snapshot->poses.push_back(std::make_pair(pose, poseIt.second.parameters));
if (!poseIt.second.turnaroundImageId.isNull())
pose["canvasImageId"] = poseIt.second.turnaroundImageId.toString();
snapshot->poses.push_back(std::make_pair(pose, poseIt.second.frames));
}
}
if (DocumentToSnapshotFor::Document == forWhat ||
@ -1221,14 +1225,10 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
newPose.id = newPoseId;
const auto &poseAttributes = poseIt.first;
newPose.name = valueOfKeyInMapOrEmpty(poseAttributes, "name");
for (const auto &attribute: poseAttributes) {
if (attribute.first == "name" ||
attribute.first == "id") {
continue;
}
newPose.attributes.insert({attribute.first, attribute.second});
}
newPose.parameters = poseIt.second;
auto findCanvasImageId = poseAttributes.find("canvasImageId");
if (findCanvasImageId != poseAttributes.end())
newPose.turnaroundImageId = QUuid(findCanvasImageId->second);
newPose.frames = poseIt.second;
oldNewIdMap[QUuid(valueOfKeyInMapOrEmpty(poseAttributes, "id"))] = newPoseId;
poseIdList.push_back(newPoseId);
emit poseAdded(newPoseId);
@ -2654,7 +2654,7 @@ void Document::generateMotions()
m_motionsGenerator = new MotionsGenerator(rigType, rigBones, rigWeights, currentRiggedOutcome());
bool hasDirtyMotion = false;
for (const auto &pose: poseMap) {
m_motionsGenerator->addPoseToLibrary(pose.first, pose.second.parameters);
m_motionsGenerator->addPoseToLibrary(pose.first, pose.second.frames);
}
for (auto &motion: motionMap) {
if (motion.second.dirty) {
@ -2722,7 +2722,12 @@ void Document::generatePosePreviews()
for (auto &poseIt: poseMap) {
if (!poseIt.second.dirty)
continue;
m_posePreviewsGenerator->addPose(poseIt.first, poseIt.second.parameters);
if (poseIt.second.frames.empty())
continue;
int middle = poseIt.second.frames.size() / 2;
if (middle >= (int)poseIt.second.frames.size())
middle = 0;
m_posePreviewsGenerator->addPose({poseIt.first, middle}, poseIt.second.frames[middle].second);
poseIt.second.dirty = false;
hasDirtyPose = true;
}
@ -2745,12 +2750,12 @@ void Document::generatePosePreviews()
void Document::posePreviewsReady()
{
for (const auto &poseId: m_posePreviewsGenerator->generatedPreviewPoseIds()) {
auto pose = poseMap.find(poseId);
for (const auto &poseIdAndFrame: m_posePreviewsGenerator->generatedPreviewPoseIdAndFrames()) {
auto pose = poseMap.find(poseIdAndFrame.first);
if (pose != poseMap.end()) {
MeshLoader *resultPartPreviewMesh = m_posePreviewsGenerator->takePreview(poseId);
MeshLoader *resultPartPreviewMesh = m_posePreviewsGenerator->takePreview(poseIdAndFrame);
pose->second.updatePreviewMesh(resultPartPreviewMesh);
emit posePreviewChanged(poseId);
emit posePreviewChanged(poseIdAndFrame.first);
}
}

View File

@ -195,8 +195,8 @@ public:
QUuid id;
QString name;
bool dirty = true;
std::map<QString, QString> attributes;
std::map<QString, std::map<QString, QString>> parameters;
QUuid turnaroundImageId;
std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames; // pair<attributes, parameters>
void updatePreviewMesh(MeshLoader *previewMesh)
{
delete m_previewMesh;
@ -290,7 +290,8 @@ public:
{
if (m_previewMeshs.empty())
return nullptr;
return new MeshLoader(*m_previewMeshs[0].second);
int middle = std::max((int)m_previewMeshs.size() / 2 - 1, (int)0);
return new MeshLoader(*m_previewMeshs[middle].second);
}
private:
Q_DISABLE_COPY(Motion);
@ -424,8 +425,8 @@ signals:
void poseRemoved(QUuid);
void poseListChanged();
void poseNameChanged(QUuid poseId);
void poseParametersChanged(QUuid poseId);
void poseAttributesChanged(QUuid poseId);
void poseFramesChanged(QUuid poseId);
void poseTurnaroundImageIdChanged(QUuid poseId);
void posePreviewChanged(QUuid poseId);
void motionAdded(QUuid motionId);
void motionRemoved(QUuid motionId);
@ -591,10 +592,11 @@ public slots:
void toggleSmoothNormal();
void enableWeld(bool enabled);
void setRigType(RigType toRigType);
void addPose(QString name, std::map<QString, std::map<QString, QString>> parameters);
void addPose(QString name, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames,
QUuid turnaroundImageId);
void removePose(QUuid poseId);
void setPoseParameters(QUuid poseId, std::map<QString, std::map<QString, QString>> parameters);
void setPoseAttributes(QUuid poseId, std::map<QString, QString> attributes);
void setPoseFrames(QUuid poseId, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames);
void setPoseTurnaroundImageId(QUuid poseId, QUuid imageId);
void renamePose(QUuid poseId, QString name);
void addMotion(QString name, std::vector<MotionClip> clips);
void removeMotion(QUuid motionId);

View File

@ -874,7 +874,7 @@ DocumentWindow::DocumentWindow() :
Q_UNUSED(poseId);
m_document->generatePosePreviews();
});
connect(m_document, &Document::poseParametersChanged, this, [=](QUuid poseId) {
connect(m_document, &Document::poseFramesChanged, this, [=](QUuid poseId) {
Q_UNUSED(poseId);
m_document->generatePosePreviews();
});
@ -1112,7 +1112,7 @@ void DocumentWindow::saveTo(const QString &saveAsFilename)
}
std::set<QUuid> imageIds;
for (auto &material: snapshot.materials) {
for (const auto &material: snapshot.materials) {
for (auto &layer: material.second) {
for (auto &mapItem: layer.second) {
auto findImageIdString = mapItem.find("linkData");

View File

@ -118,7 +118,7 @@ MaterialEditWidget::MaterialEditWidget(const Document *document, QWidget *parent
m_unsaved = true;
updateTitle();
});
QPushButton *saveButton = new QPushButton(tr("Save"));
QPushButton *saveButton = new QPushButton(tr("Apply"));
connect(saveButton, &QPushButton::clicked, this, &MaterialEditWidget::save);
saveButton->setDefault(true);

View File

@ -113,7 +113,7 @@ MotionEditWidget::MotionEditWidget(const Document *document, QWidget *parent) :
m_nameEdit = new QLineEdit;
m_nameEdit->setFixedWidth(200);
connect(m_nameEdit, &QLineEdit::textChanged, this, &MotionEditWidget::setUnsavedState);
QPushButton *saveButton = new QPushButton(tr("Save"));
QPushButton *saveButton = new QPushButton(tr("Apply"));
connect(saveButton, &QPushButton::clicked, this, &MotionEditWidget::save);
saveButton->setDefault(true);
@ -250,7 +250,7 @@ void MotionEditWidget::generatePreviews()
m_previewsGenerator = new MotionsGenerator(m_document->rigType, rigBones, rigWeights,
m_document->currentRiggedOutcome());
for (const auto &pose: m_document->poseMap)
m_previewsGenerator->addPoseToLibrary(pose.first, pose.second.parameters);
m_previewsGenerator->addPoseToLibrary(pose.first, pose.second.frames);
for (const auto &motion: m_document->motionMap)
m_previewsGenerator->addMotionToLibrary(motion.first, motion.second.clips);
m_previewsGenerator->addMotionToLibrary(QUuid(), m_timelineWidget->clips());

View File

@ -22,7 +22,7 @@ MotionManageWidget::MotionManageWidget(const Document *document, QWidget *parent
connect(m_motionListWidget, &MotionListWidget::modifyMotion, this, &MotionManageWidget::showMotionDialog);
InfoLabel *infoLabel = new InfoLabel;
infoLabel->show();
infoLabel->hide();
auto refreshInfoLabel = [=]() {
if (m_document->currentRigSucceed()) {

View File

@ -26,9 +26,9 @@ MotionsGenerator::~MotionsGenerator()
delete m_poser;
}
void MotionsGenerator::addPoseToLibrary(const QUuid &poseId, const std::map<QString, std::map<QString, QString>> &parameters)
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] = parameters;
m_poses[poseId] = frames;
}
void MotionsGenerator::addMotionToLibrary(const QUuid &motionId, const std::vector<MotionClip> &clips)
@ -60,6 +60,15 @@ std::vector<MotionClip> *MotionsGenerator::findMotionClips(const QUuid &motionId
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) {
@ -70,6 +79,18 @@ void MotionsGenerator::generatePreviewsForOutcomes(const std::vector<std::pair<f
}
}
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);
@ -85,7 +106,7 @@ float MotionsGenerator::calculateMotionDuration(const QUuid &motionId, std::set<
if (clip.clipType == MotionClipType::Interpolation)
totalDuration += clip.duration;
else if (clip.clipType == MotionClipType::Pose)
totalDuration += clip.duration;
totalDuration += calculatePoseDuration(clip.linkToId);
else if (clip.clipType == MotionClipType::Motion)
totalDuration += calculateMotionDuration(clip.linkToId, visited);
}
@ -111,6 +132,8 @@ void MotionsGenerator::generateMotion(const QUuid &motionId, std::set<QUuid> &vi
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;
@ -159,13 +182,15 @@ void MotionsGenerator::generateMotion(const QUuid &motionId, std::set<QUuid> &vi
progress += interval;
continue;
} else if (MotionClipType::Pose == progressClip.clipType) {
const JointNodeTree *beginJointNodeTree = findClipBeginJointNodeTree((*motionClips)[clipIndex]);
if (nullptr == beginJointNodeTree) {
qDebug() << "findClipBeginJointNodeTree failed";
break;
}
outcomes.push_back({progress - lastProgress, *beginJointNodeTree});
const auto &frames = findPoseFrames(progressClip.linkToId);
int frame = clipLocalProgress * frames->size() / std::max((float)0.01, progressClip.duration);
if (frame >= (int)frames->size())
frame = frames->size() - 1;
if (frame >= 0 && frame < (int)frames->size()) {
const JointNodeTree jointNodeTree = poseJointNodeTree(progressClip.linkToId, frame);
outcomes.push_back({progress - lastProgress, jointNodeTree});
lastProgress = progress;
}
progress += interval;
continue;
} else if (MotionClipType::Motion == progressClip.clipType) {
@ -182,25 +207,28 @@ JointNodeTree MotionsGenerator::generateInterpolation(InterpolationType interpol
return JointNodeTree::slerp(first, second, calculateInterpolation(interpolationType, progress));
}
const JointNodeTree &MotionsGenerator::poseJointNodeTree(const QUuid &poseId)
const JointNodeTree &MotionsGenerator::poseJointNodeTree(const QUuid &poseId, int frame)
{
auto findResult = m_poseJointNodeTreeMap.find(poseId);
auto findResult = m_poseJointNodeTreeMap.find({poseId, frame});
if (findResult != m_poseJointNodeTreeMap.end())
return findResult->second;
const auto &parameters = m_poses[poseId];
const auto &frames = m_poses[poseId];
m_poser->reset();
if (frame < (int)frames.size()) {
const auto &parameters = frames[frame].second;
m_poser->parameters() = parameters;
}
m_poser->commit();
auto insertResult = m_poseJointNodeTreeMap.insert({poseId, m_poser->resultJointNodeTree()});
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);
const JointNodeTree &jointNodeTree = poseJointNodeTree(clip.linkToId, 0);
return &jointNodeTree;
} else if (MotionClipType::Motion == clip.clipType) {
const std::vector<MotionClip> *motionClips = findMotionClips(clip.linkToId);
@ -215,8 +243,11 @@ const JointNodeTree *MotionsGenerator::findClipBeginJointNodeTree(const MotionCl
const JointNodeTree *MotionsGenerator::findClipEndJointNodeTree(const MotionClip &clip)
{
if (MotionClipType::Pose == clip.clipType) {
const JointNodeTree &jointNodeTree = poseJointNodeTree(clip.linkToId);
return &jointNodeTree;
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()) {

View File

@ -19,7 +19,7 @@ public:
const std::map<int, RiggerVertexWeights> *rigWeights,
const Outcome &outcome);
~MotionsGenerator();
void addPoseToLibrary(const QUuid &poseId, const std::map<QString, std::map<QString, QString>> &parameters);
void addPoseToLibrary(const QUuid &poseId, const std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> &frames);
void addMotionToLibrary(const QUuid &motionId, const std::vector<MotionClip> &clips);
void addRequirement(const QUuid &motionId);
std::vector<std::pair<float, MeshLoader *>> takeResultPreviewMeshs(const QUuid &motionId);
@ -35,25 +35,27 @@ public slots:
private:
void generateMotion(const QUuid &motionId, std::set<QUuid> &visited, std::vector<std::pair<float, JointNodeTree>> &outcomes);
const JointNodeTree &poseJointNodeTree(const QUuid &poseId);
const JointNodeTree &poseJointNodeTree(const QUuid &poseId, int frame);
JointNodeTree generateInterpolation(InterpolationType interpolationType, const JointNodeTree &first, const JointNodeTree &second, float progress);
const JointNodeTree *findClipBeginJointNodeTree(const MotionClip &clip);
const JointNodeTree *findClipEndJointNodeTree(const MotionClip &clip);
std::vector<MotionClip> *findMotionClips(const QUuid &motionId);
std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> *findPoseFrames(const QUuid &poseId);
void generatePreviewsForOutcomes(const std::vector<std::pair<float, JointNodeTree>> &outcomes, std::vector<std::pair<float, MeshLoader *>> &previews);
float calculateMotionDuration(const QUuid &motionId, std::set<QUuid> &visited);
float calculatePoseDuration(const QUuid &poseId);
RigType m_rigType = RigType::None;
std::vector<RiggerBone> m_rigBones;
std::map<int, RiggerVertexWeights> m_rigWeights;
Outcome m_outcome;
std::map<QUuid, std::map<QString, std::map<QString, QString>>> m_poses;
std::map<QUuid, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>>> m_poses;
std::map<QUuid, std::vector<MotionClip>> m_motions;
std::set<QUuid> m_requiredMotionIds;
std::set<QUuid> m_generatedMotionIds;
std::map<QUuid, std::vector<std::pair<float, MeshLoader *>>> m_resultPreviewMeshs;
std::map<QUuid, std::vector<std::pair<float, JointNodeTree>>> m_resultJointNodeTrees;
std::map<QUuid, JointNodeTree> m_poseJointNodeTreeMap;
std::map<std::pair<QUuid, int>, JointNodeTree> m_poseJointNodeTreeMap;
Poser *m_poser = nullptr;
int m_fps = 30;
};

View File

@ -1,7 +1,14 @@
#include <QDebug>
#include <QXmlStreamWriter>
#include <QClipboard>
#include <QApplication>
#include <QMimeData>
#include "posedocument.h"
#include "rigger.h"
#include "util.h"
#include "document.h"
#include "snapshot.h"
#include "snapshotxml.h"
const float PoseDocument::m_nodeRadius = 0.01;
const float PoseDocument::m_groundPlaneHalfThickness = 0.01 / 4;
@ -10,6 +17,12 @@ const float PoseDocument::m_outcomeScaleFactor = 0.5;
bool PoseDocument::hasPastableNodesInClipboard() const
{
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
if (mimeData->hasText()) {
if (-1 != mimeData->text().indexOf("<pose ") && -1 != mimeData->text().indexOf("<parameter "))
return true;
}
return false;
}
@ -20,17 +33,37 @@ bool PoseDocument::originSettled() const
bool PoseDocument::isNodeEditable(QUuid nodeId) const
{
Q_UNUSED(nodeId);
return true;
}
bool PoseDocument::isEdgeEditable(QUuid edgeId) const
{
Q_UNUSED(edgeId);
return true;
}
void PoseDocument::copyNodes(std::set<QUuid> nodeIdSet) const
{
// TODO:
std::map<QString, std::map<QString, QString>> parameters;
toParameters(parameters, nodeIdSet);
if (parameters.empty())
return;
Document document;
QUuid poseId = QUuid::createUuid();
auto &pose = document.poseMap[poseId];
pose.id = poseId;
pose.frames.push_back({std::map<QString, QString>(), parameters});
document.poseIdList.push_back(poseId);
Snapshot snapshot;
std::set<QUuid> limitPoseIds;
document.toSnapshot(&snapshot, limitPoseIds, DocumentToSnapshotFor::Poses);
QString snapshotXml;
QXmlStreamWriter xmlStreamWriter(&snapshotXml);
saveSkeletonToXmlStream(&snapshot, &xmlStreamWriter);
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(snapshotXml);
}
void PoseDocument::saveHistoryItem()
@ -72,6 +105,21 @@ void PoseDocument::redo()
void PoseDocument::paste()
{
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
if (mimeData->hasText()) {
QXmlStreamReader xmlStreamReader(mimeData->text());
Snapshot snapshot;
loadSkeletonFromXmlStream(&snapshot, xmlStreamReader);
if (snapshot.poses.empty())
return;
const auto &firstPose = *snapshot.poses.begin();
if (firstPose.second.empty())
return;
const auto &firstFrame = *firstPose.second.begin();
fromParameters(&m_riggerBones, firstFrame.second);
saveHistoryItem();
}
}
void PoseDocument::updateTurnaround(const QImage &image)
@ -93,6 +141,12 @@ void PoseDocument::reset()
emit parametersChanged();
}
void PoseDocument::clearHistories()
{
m_undoItems.clear();
m_redoItems.clear();
}
void PoseDocument::fromParameters(const std::vector<RiggerBone> *rigBones,
const std::map<QString, std::map<QString, QString>> &parameters)
{
@ -390,7 +444,7 @@ float PoseDocument::findGroundY() const
return maxY;
}
void PoseDocument::toParameters(std::map<QString, std::map<QString, QString>> &parameters) const
void PoseDocument::toParameters(std::map<QString, std::map<QString, QString>> &parameters, const std::set<QUuid> &limitNodeIds) const
{
float translateY = 0;
auto findGroundEdge = edgeMap.find(m_groundEdgeId);
@ -400,6 +454,8 @@ void PoseDocument::toParameters(std::map<QString, std::map<QString, QString>> &p
auto findFirstNode = nodeMap.find(nodeIds[0]);
auto findSecondNode = nodeMap.find(nodeIds[1]);
if (findFirstNode != nodeMap.end() && findSecondNode != nodeMap.end()) {
if (limitNodeIds.empty() || limitNodeIds.find(findFirstNode->first) != limitNodeIds.end() ||
limitNodeIds.find(findSecondNode->first) != limitNodeIds.end())
translateY = (findFirstNode->second.y + findSecondNode->second.y) / 2 -
(findGroundY() + m_groundPlaneHalfThickness);
}
@ -417,6 +473,8 @@ void PoseDocument::toParameters(std::map<QString, std::map<QString, QString>> &p
auto findSecondNode = nodeMap.find(boneNodeIdPair.second);
if (findSecondNode == nodeMap.end())
continue;
if (limitNodeIds.empty() || limitNodeIds.find(boneNodeIdPair.first) != limitNodeIds.end() ||
limitNodeIds.find(boneNodeIdPair.second) != limitNodeIds.end()) {
auto &boneParameter = parameters[item.first];
boneParameter["fromX"] = QString::number(toOutcomeX(findFirstNode->second.x));
boneParameter["fromY"] = QString::number(toOutcomeY(findFirstNode->second.y));
@ -426,6 +484,7 @@ void PoseDocument::toParameters(std::map<QString, std::map<QString, QString>> &p
boneParameter["toZ"] = QString::number(toOutcomeZ(findSecondNode->second.z));
}
}
}
float PoseDocument::fromOutcomeX(float x)
{

View File

@ -35,12 +35,13 @@ public:
void updateRigBones(const std::vector<RiggerBone> *rigBones, const QVector3D &rootTranslation=QVector3D(0, 0, 0));
void reset();
void toParameters(std::map<QString, std::map<QString, QString>> &parameters) const;
void toParameters(std::map<QString, std::map<QString, QString>> &parameters, const std::set<QUuid> &limitNodeIds=std::set<QUuid>()) const;
void fromParameters(const std::vector<RiggerBone> *rigBones,
const std::map<QString, std::map<QString, QString>> &parameters);
public slots:
void saveHistoryItem();
void clearHistories();
void undo() override;
void redo() override;
void paste() override;

View File

@ -7,6 +7,7 @@
#include <QLineEdit>
#include <QMessageBox>
#include <QFileDialog>
#include <QSpinBox>
#include "theme.h"
#include "poseeditwidget.h"
#include "floatnumberwidget.h"
@ -17,6 +18,8 @@
#include "shortcuts.h"
#include "imageforever.h"
const float PoseEditWidget::m_frameDuration = 0.042; //(1.0 / 24)
PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) :
QDialog(parent),
m_document(document),
@ -75,8 +78,9 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) :
connect(m_poseDocument, &PoseDocument::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged);
connect(m_poseDocument, &PoseDocument::parametersChanged, this, [&]() {
m_parameters.clear();
m_poseDocument->toParameters(m_parameters);
m_currentParameters.clear();
m_poseDocument->toParameters(m_currentParameters);
syncFrameFromCurrent();
emit parametersAdjusted();
});
@ -86,10 +90,9 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) :
m_nameEdit = new QLineEdit;
m_nameEdit->setFixedWidth(200);
connect(m_nameEdit, &QLineEdit::textChanged, this, [=]() {
m_unsaved = true;
updateTitle();
setUnsaveState();
});
QPushButton *saveButton = new QPushButton(tr("Save"));
QPushButton *saveButton = new QPushButton(tr("Apply"));
connect(saveButton, &QPushButton::clicked, this, &PoseEditWidget::save);
saveButton->setDefault(true);
@ -98,14 +101,30 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) :
connect(m_poseDocument, &PoseDocument::turnaroundChanged,
graphicsWidget, &SkeletonGraphicsWidget::turnaroundChanged);
m_framesSettingButton = new QPushButton();
connect(m_framesSettingButton, &QPushButton::clicked, this, [=]() {
showFramesSettingPopup(mapFromGlobal(QCursor::pos()));
});
m_currentFrameSlider = new QSlider(Qt::Horizontal);
m_currentFrameSlider->setRange(0, m_frames.size() - 1);
m_currentFrameSlider->setValue(m_currentFrame);
m_currentFrameSlider->hide();
connect(m_currentFrameSlider, static_cast<void (QSlider::*)(int)>(&QSlider::valueChanged), this, [=](int value) {
setCurrentFrame(value);
});
connect(m_document, &Document::resultRigChanged, this, &PoseEditWidget::updatePoseDocument);
QHBoxLayout *baseInfoLayout = new QHBoxLayout;
baseInfoLayout->addWidget(new QLabel(tr("Name")));
baseInfoLayout->addWidget(m_nameEdit);
baseInfoLayout->addWidget(changeReferenceSheet);
baseInfoLayout->addWidget(m_framesSettingButton);
baseInfoLayout->addWidget(m_currentFrameSlider);
baseInfoLayout->addStretch();
baseInfoLayout->addWidget(saveButton);
baseInfoLayout->setStretch(4, 1);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(paramtersLayout);
@ -114,22 +133,99 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) :
setLayout(mainLayout);
connect(m_document, &Document::resultRigChanged, this, &PoseEditWidget::updatePoseDocument);
connect(this, &PoseEditWidget::parametersAdjusted, this, &PoseEditWidget::updatePreview);
connect(this, &PoseEditWidget::parametersAdjusted, [=]() {
m_unsaved = true;
updateTitle();
setUnsaveState();
});
connect(this, &PoseEditWidget::addPose, m_document, &Document::addPose);
connect(this, &PoseEditWidget::renamePose, m_document, &Document::renamePose);
connect(this, &PoseEditWidget::setPoseParameters, m_document, &Document::setPoseParameters);
connect(this, &PoseEditWidget::setPoseAttributes, m_document, &Document::setPoseAttributes);
connect(this, &PoseEditWidget::setPoseFrames, m_document, &Document::setPoseFrames);
connect(this, &PoseEditWidget::setPoseTurnaroundImageId, m_document, &Document::setPoseTurnaroundImageId);
updatePoseDocument();
updateTitle();
updateFramesSettingButton();
m_poseDocument->saveHistoryItem();
}
void PoseEditWidget::showFramesSettingPopup(const QPoint &pos)
{
QMenu popupMenu;
QWidget *popup = new QWidget;
QSpinBox *framesEdit = new QSpinBox();
framesEdit->setMaximum(60);
framesEdit->setMinimum(1);
framesEdit->setSingleStep(1);
framesEdit->setValue(m_frames.size());
connect(framesEdit, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [=](int value) {
setFrameCount(value);
});
QFormLayout *formLayout = new QFormLayout;
formLayout->addRow(tr("Frames:"), framesEdit);
popup->setLayout(formLayout);
QWidgetAction *action = new QWidgetAction(this);
action->setDefaultWidget(popup);
popupMenu.addAction(action);
popupMenu.exec(mapToGlobal(pos));
}
void PoseEditWidget::updateFramesSettingButton()
{
m_currentFrameSlider->setRange(0, m_frames.size() - 1);
m_currentFrameSlider->setVisible(m_frames.size() > 1);
m_framesSettingButton->setText(tr("Frame: %1 / %2").arg(QString::number(m_currentFrame + 1).rightJustified(2, ' ')).arg(QString::number(m_frames.size()).leftJustified(2, ' ')));
}
void PoseEditWidget::ensureEnoughFrames()
{
if (m_currentFrame >= (int)m_frames.size()) {
m_frames.resize(m_currentFrame + 1);
setUnsaveState();
updateFramesSettingButton();
}
}
void PoseEditWidget::syncFrameFromCurrent()
{
ensureEnoughFrames();
m_frames[m_currentFrame] = {m_currentAttributes, m_currentParameters};
m_frames[m_currentFrame].first["duration"] = QString::number(m_frameDuration);
}
void PoseEditWidget::setFrameCount(int count)
{
if (count == (int)m_frames.size())
return;
setUnsaveState();
count = std::max(count, 1);
m_frames.resize(count);
updateFramesSettingButton();
if (m_currentFrame >= count) {
setCurrentFrame(count - 1);
}
}
void PoseEditWidget::setCurrentFrame(int frame)
{
if (m_currentFrame == frame)
return;
m_currentFrame = frame;
ensureEnoughFrames();
updateFramesSettingButton();
m_currentAttributes = m_frames[m_currentFrame].first;
m_currentParameters = m_frames[m_currentFrame].second;
updatePoseDocument();
}
void PoseEditWidget::changeTurnaround()
{
QString fileName = QFileDialog::getOpenFileName(this, QString(), QString(),
@ -139,26 +235,19 @@ void PoseEditWidget::changeTurnaround()
QImage image;
if (!image.load(fileName))
return;
m_imageId = ImageForever::add(&image);
m_attributes["canvasImageId"] = m_imageId.toString();
auto newImageId = ImageForever::add(&image);
if (m_imageId == newImageId)
return;
setUnsaveState();
m_imageId = newImageId;
m_poseDocument->updateTurnaround(image);
}
QUuid PoseEditWidget::findImageIdFromAttributes(const std::map<QString, QString> &attributes)
{
auto findImageIdResult = attributes.find("canvasImageId");
if (findImageIdResult == attributes.end())
return QUuid();
return QUuid(findImageIdResult->second);
}
void PoseEditWidget::updatePoseDocument()
{
m_poseDocument->fromParameters(m_document->resultRigBones(), m_parameters);
QUuid imageId = findImageIdFromAttributes(m_attributes);
auto image = ImageForever::get(imageId);
if (nullptr != image)
m_poseDocument->updateTurnaround(*image);
m_poseDocument->fromParameters(m_document->resultRigBones(), m_currentParameters);
m_poseDocument->clearHistories();
m_poseDocument->saveHistoryItem();
updatePreview();
}
@ -222,10 +311,7 @@ void PoseEditWidget::updatePreview()
if (nullptr == poser)
return;
m_parameters.clear();
m_poseDocument->toParameters(m_parameters);
poser->parameters() = m_parameters;
poser->parameters() = m_currentParameters;
poser->commit();
m_posePreviewManager->postUpdate(*poser, m_document->currentRiggedOutcome(), *rigWeights);
delete poser;
@ -260,19 +346,28 @@ void PoseEditWidget::setEditPoseName(QString name)
updateTitle();
}
void PoseEditWidget::setEditParameters(std::map<QString, std::map<QString, QString>> parameters)
void PoseEditWidget::setEditPoseFrames(std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames)
{
m_parameters = parameters;
m_frames = frames;
if (!m_frames.empty()) {
m_currentFrame = 0;
const auto &frame = m_frames[m_currentFrame];
m_currentAttributes = frame.first;
m_currentParameters = frame.second;
}
updatePoseDocument();
updatePreview();
updateFramesSettingButton();
m_poseDocument->saveHistoryItem();
}
void PoseEditWidget::setEditAttributes(std::map<QString, QString> attributes)
void PoseEditWidget::setEditPoseTurnaroundImageId(QUuid imageId)
{
m_attributes = attributes;
updatePoseDocument();
updatePreview();
m_imageId = imageId;
const auto &image = ImageForever::get(m_imageId);
if (nullptr == image)
return;
m_poseDocument->updateTurnaround(*image);
}
void PoseEditWidget::clearUnsaveState()
@ -281,14 +376,20 @@ void PoseEditWidget::clearUnsaveState()
updateTitle();
}
void PoseEditWidget::setUnsaveState()
{
m_unsaved = true;
updateTitle();
}
void PoseEditWidget::save()
{
if (m_poseId.isNull()) {
emit addPose(m_nameEdit->text(), m_parameters);
emit addPose(m_nameEdit->text(), m_frames, m_imageId);
} else if (m_unsaved) {
emit renamePose(m_poseId, m_nameEdit->text());
emit setPoseParameters(m_poseId, m_parameters);
emit setPoseAttributes(m_poseId, m_attributes);
emit setPoseFrames(m_poseId, m_frames);
emit setPoseTurnaroundImageId(m_poseId, m_imageId);
}
m_unsaved = false;
close();

View File

@ -4,56 +4,71 @@
#include <map>
#include <QCloseEvent>
#include <QLineEdit>
#include <QSlider>
#include "posepreviewmanager.h"
#include "document.h"
#include "modelwidget.h"
#include "rigger.h"
#include "skeletongraphicswidget.h"
#include "posedocument.h"
#include "floatnumberwidget.h"
class PoseEditWidget : public QDialog
{
Q_OBJECT
signals:
void addPose(QString name, std::map<QString, std::map<QString, QString>> parameters);
void addPose(QString name, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames, QUuid turnaroundImageId);
void removePose(QUuid poseId);
void setPoseParameters(QUuid poseId, std::map<QString, std::map<QString, QString>> parameters);
void setPoseAttributes(QUuid poseId, std::map<QString, QString> attributes);
void setPoseFrames(QUuid poseId, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames);
void setPoseTurnaroundImageId(QUuid poseId, QUuid imageId);
void renamePose(QUuid poseId, QString name);
void parametersAdjusted();
public:
PoseEditWidget(const Document *document, QWidget *parent=nullptr);
~PoseEditWidget();
static const float m_frameDuration;
public slots:
void updatePoseDocument();
void updatePreview();
void syncFrameFromCurrent();
void setEditPoseId(QUuid poseId);
void setEditPoseName(QString name);
void setEditParameters(std::map<QString, std::map<QString, QString>> parameters);
void setEditAttributes(std::map<QString, QString> attributes);
void setEditPoseFrames(std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames);
void setEditPoseTurnaroundImageId(QUuid imageId);
void setCurrentFrame(int frame);
void setFrameCount(int count);
void updateTitle();
void save();
void clearUnsaveState();
void setUnsaveState();
void changeTurnaround();
private slots:
void updateFramesSettingButton();
void showFramesSettingPopup(const QPoint &pos);
protected:
QSize sizeHint() const override;
void closeEvent(QCloseEvent *event) override;
void reject() override;
private:
void ensureEnoughFrames();
const Document *m_document = nullptr;
PosePreviewManager *m_posePreviewManager = nullptr;
ModelWidget *m_previewWidget = nullptr;
bool m_isPreviewDirty = false;
bool m_closed = false;
std::map<QString, std::map<QString, QString>> m_parameters;
std::map<QString, QString> m_attributes;
std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> m_frames;
std::map<QString, QString> m_currentAttributes;
std::map<QString, std::map<QString, QString>> m_currentParameters;
int m_currentFrame = 0;
QUuid m_poseId;
bool m_unsaved = false;
QUuid m_imageId;
QLineEdit *m_nameEdit = nullptr;
PoseDocument *m_poseDocument = nullptr;
SkeletonGraphicsWidget *m_poseGraphicsWidget = nullptr;
QUuid findImageIdFromAttributes(const std::map<QString, QString> &attributes);
QPushButton *m_framesSettingButton = nullptr;
QSlider *m_currentFrameSlider = nullptr;
};
#endif

View File

@ -22,7 +22,7 @@ PoseManageWidget::PoseManageWidget(const Document *document, QWidget *parent) :
connect(m_poseListWidget, &PoseListWidget::modifyPose, this, &PoseManageWidget::showPoseDialog);
InfoLabel *infoLabel = new InfoLabel;
infoLabel->show();
infoLabel->hide();
auto refreshInfoLabel = [=]() {
if (m_document->currentRigSucceed()) {
@ -77,8 +77,8 @@ void PoseManageWidget::showPoseDialog(QUuid poseId)
if (nullptr != pose) {
poseEditWidget->setEditPoseId(poseId);
poseEditWidget->setEditPoseName(pose->name);
poseEditWidget->setEditParameters(pose->parameters);
poseEditWidget->setEditAttributes(pose->attributes);
poseEditWidget->setEditPoseFrames(pose->frames);
poseEditWidget->setEditPoseTurnaroundImageId(pose->turnaroundImageId);
poseEditWidget->clearUnsaveState();
}
}

View File

@ -23,20 +23,20 @@ PosePreviewsGenerator::~PosePreviewsGenerator()
delete m_outcome;
}
void PosePreviewsGenerator::addPose(QUuid poseId, const std::map<QString, std::map<QString, QString>> &pose)
void PosePreviewsGenerator::addPose(std::pair<QUuid, int> idAndFrame, const std::map<QString, std::map<QString, QString>> &pose)
{
m_poses.push_back(std::make_pair(poseId, pose));
m_poses.push_back(std::make_pair(idAndFrame, pose));
}
const std::set<QUuid> &PosePreviewsGenerator::generatedPreviewPoseIds()
const std::set<std::pair<QUuid, int>> &PosePreviewsGenerator::generatedPreviewPoseIdAndFrames()
{
return m_generatedPoseIds;
return m_generatedPoseIdAndFrames;
}
MeshLoader *PosePreviewsGenerator::takePreview(QUuid poseId)
MeshLoader *PosePreviewsGenerator::takePreview(std::pair<QUuid, int> idAndFrame)
{
MeshLoader *resultMesh = m_previews[poseId];
m_previews[poseId] = nullptr;
MeshLoader *resultMesh = m_previews[idAndFrame];
m_previews[idAndFrame] = nullptr;
return resultMesh;
}
@ -57,7 +57,7 @@ void PosePreviewsGenerator::process()
poser->reset();
m_generatedPoseIds.insert(pose.first);
m_generatedPoseIdAndFrames.insert(pose.first);
}
delete poser;

View File

@ -18,9 +18,9 @@ public:
const std::map<int, RiggerVertexWeights> *rigWeights,
const Outcome &outcome);
~PosePreviewsGenerator();
void addPose(QUuid poseId, const std::map<QString, std::map<QString, QString>> &pose);
const std::set<QUuid> &generatedPreviewPoseIds();
MeshLoader *takePreview(QUuid poseId);
void addPose(std::pair<QUuid, int> idAndFrame, const std::map<QString, std::map<QString, QString>> &pose);
const std::set<std::pair<QUuid, int>> &generatedPreviewPoseIdAndFrames();
MeshLoader *takePreview(std::pair<QUuid, int> idAndFrame);
signals:
void finished();
public slots:
@ -30,9 +30,9 @@ private:
std::vector<RiggerBone> m_rigBones;
std::map<int, RiggerVertexWeights> m_rigWeights;
Outcome *m_outcome = nullptr;
std::vector<std::pair<QUuid, std::map<QString, std::map<QString, QString>>>> m_poses;
std::map<QUuid, MeshLoader *> m_previews;
std::set<QUuid> m_generatedPoseIds;
std::vector<std::pair<std::pair<QUuid, int>, std::map<QString, std::map<QString, QString>>>> m_poses;
std::map<std::pair<QUuid, int>, MeshLoader *> m_previews;
std::set<std::pair<QUuid, int>> m_generatedPoseIdAndFrames;
};
#endif

View File

@ -163,13 +163,13 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
}
QAction copyAction(tr("Copy"), this);
if (!m_nodePositionModifyOnly && hasSelection()) {
if (hasNodeSelection()) {
connect(&copyAction, &QAction::triggered, this, &SkeletonGraphicsWidget::copy);
contextMenu.addAction(&copyAction);
}
QAction pasteAction(tr("Paste"), this);
if (!m_nodePositionModifyOnly && m_document->hasPastableNodesInClipboard()) {
if (m_document->hasPastableNodesInClipboard()) {
connect(&pasteAction, &QAction::triggered, m_document, &SkeletonDocument::paste);
contextMenu.addAction(&pasteAction);
}

View File

@ -18,7 +18,7 @@ public:
std::map<QString, std::map<QString, QString>> parts;
std::map<QString, std::map<QString, QString>> components;
std::map<QString, QString> rootComponent;
std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> poses; // std::pair<Pose attributes, Bone attributes>
std::vector<std::pair<std::map<QString, QString>, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>>>> poses; // std::pair<Pose attributes, frames> frame: std::pair<Frame attributes, Frame parameters>
std::vector<std::pair<std::map<QString, QString>, std::vector<std::map<QString, QString>>>> motions; // std::pair<Motion attributes, clips>
std::vector<std::pair<std::map<QString, QString>, std::vector<std::pair<std::map<QString, QString>, std::vector<std::map<QString, QString>>>>>> materials; // std::pair<Material attributes, layers> layer: std::pair<Layer attributes, maps>
@ -72,11 +72,17 @@ public:
addQStringToBuffer(subItem.second);
}
for (const auto &subItem: item.second) {
addQStringToBuffer(subItem.first);
for (const auto &subSubItem: subItem.second) {
for (const auto &subSubItem: subItem.first) {
addQStringToBuffer(subSubItem.first);
addQStringToBuffer(subSubItem.second);
}
for (const auto &subSubItem: subItem.second) {
addQStringToBuffer(subSubItem.first);
for (const auto &subSubSubItem: subSubItem.second) {
addQStringToBuffer(subSubSubItem.first);
addQStringToBuffer(subSubSubItem.second);
}
}
}
}
for (const auto &item: motions) {

View File

@ -127,16 +127,25 @@ void saveSkeletonToXmlStream(Snapshot *snapshot, QXmlStreamWriter *writer)
writer->writeEndElement();
writer->writeStartElement("poses");
std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>>::iterator poseIterator;
//std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>>::iterator poseIterator;
std::vector<std::pair<std::map<QString, QString>, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>>>>::iterator poseIterator;
for (poseIterator = snapshot->poses.begin(); poseIterator != snapshot->poses.end(); poseIterator++) {
std::map<QString, QString>::iterator poseAttributeIterator;
writer->writeStartElement("pose");
for (poseAttributeIterator = poseIterator->first.begin(); poseAttributeIterator != poseIterator->first.end(); poseAttributeIterator++) {
writer->writeAttribute(poseAttributeIterator->first, poseAttributeIterator->second);
}
writer->writeStartElement("frames");
std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>>::iterator frameIterator;
for (frameIterator = poseIterator->second.begin(); frameIterator != poseIterator->second.end(); frameIterator++) {
std::map<QString, QString>::iterator frameAttributeIterator;
writer->writeStartElement("frame");
for (frameAttributeIterator = frameIterator->first.begin(); frameAttributeIterator != frameIterator->first.end(); frameAttributeIterator++) {
writer->writeAttribute(frameAttributeIterator->first, frameAttributeIterator->second);
}
writer->writeStartElement("parameters");
std::map<QString, std::map<QString, QString>>::iterator itemsIterator;
for (itemsIterator = poseIterator->second.begin(); itemsIterator != poseIterator->second.end(); itemsIterator++) {
for (itemsIterator = frameIterator->second.begin(); itemsIterator != frameIterator->second.end(); itemsIterator++) {
std::map<QString, QString>::iterator parametersIterator;
writer->writeStartElement("parameter");
writer->writeAttribute("for", itemsIterator->first);
@ -150,6 +159,9 @@ void saveSkeletonToXmlStream(Snapshot *snapshot, QXmlStreamWriter *writer)
writer->writeEndElement();
}
writer->writeEndElement();
writer->writeEndElement();
}
writer->writeEndElement();
writer->writeStartElement("motions");
std::vector<std::pair<std::map<QString, QString>, std::vector<std::map<QString, QString>>>>::iterator motionIterator;
@ -188,7 +200,8 @@ void loadSkeletonFromXmlStream(Snapshot *snapshot, QXmlStreamReader &reader)
std::vector<QString> elementNameStack;
std::pair<std::map<QString, QString>, std::vector<std::map<QString, QString>>> currentMaterialLayer;
std::pair<std::map<QString, QString>, std::vector<std::pair<std::map<QString, QString>, std::vector<std::map<QString, QString>>>>> currentMaterial;
std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>> currentPose;
std::pair<std::map<QString, QString>, std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>>> currentPose;
std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>> currentPoseFrame;
std::pair<std::map<QString, QString>, std::vector<std::map<QString, QString>>> currentMotion;
while (!reader.atEnd()) {
reader.readNext();
@ -293,15 +306,18 @@ void loadSkeletonFromXmlStream(Snapshot *snapshot, QXmlStreamReader &reader)
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
currentPose.first[attr.name().toString()] = attr.value().toString();
}
} else if (fullName == "canvas.poses.pose.parameter" ||
fullName == "canvas.poses.pose.parameters.parameter") {
} else if (fullName == "canvas.poses.pose.frames.frame") {
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
currentPoseFrame.first[attr.name().toString()] = attr.value().toString();
}
} else if (fullName == "canvas.poses.pose.frames.frame.parameters.parameter") {
QString forWhat = reader.attributes().value("for").toString();
if (forWhat.isEmpty())
continue;
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
if ("for" == attr.name().toString())
continue;
currentPose.second[forWhat][attr.name().toString()] = attr.value().toString();
currentPoseFrame.second[forWhat][attr.name().toString()] = attr.value().toString();
}
} else if (fullName == "canvas.motions.motion") {
QString motionId = reader.attributes().value("id").toString();
@ -325,6 +341,8 @@ void loadSkeletonFromXmlStream(Snapshot *snapshot, QXmlStreamReader &reader)
currentMaterial.second.push_back(currentMaterialLayer);
} else if (fullName == "canvas.materials.material") {
snapshot->materials.push_back(currentMaterial);
} else if (fullName == "canvas.poses.pose.frames.frame") {
currentPose.second.push_back(currentPoseFrame);
} else if (fullName == "canvas.poses.pose") {
snapshot->poses.push_back(currentPose);
} else if (fullName == "canvas.motions.motion") {