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
parent
a551995235
commit
fff39b0835
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -26,9 +26,9 @@ MotionsGenerator::~MotionsGenerator()
|
|||
delete m_poser;
|
||||
}
|
||||
|
||||
void MotionsGenerator::addPoseToLibrary(const QUuid &poseId, const std::map<QString, std::map<QString, QString>> ¶meters)
|
||||
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;
|
||||
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;
|
||||
}
|
||||
outcomes.push_back({progress - lastProgress, *beginJointNodeTree});
|
||||
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 ¶meters = m_poses[poseId];
|
||||
const auto &frames = m_poses[poseId];
|
||||
|
||||
m_poser->reset();
|
||||
m_poser->parameters() = parameters;
|
||||
if (frame < (int)frames.size()) {
|
||||
const auto ¶meters = 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()) {
|
||||
|
|
|
@ -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>> ¶meters);
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -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>> ¶meters)
|
||||
{
|
||||
|
@ -390,7 +444,7 @@ float PoseDocument::findGroundY() const
|
|||
return maxY;
|
||||
}
|
||||
|
||||
void PoseDocument::toParameters(std::map<QString, std::map<QString, QString>> ¶meters) const
|
||||
void PoseDocument::toParameters(std::map<QString, std::map<QString, QString>> ¶meters, 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,13 +473,16 @@ void PoseDocument::toParameters(std::map<QString, std::map<QString, QString>> &p
|
|||
auto findSecondNode = nodeMap.find(boneNodeIdPair.second);
|
||||
if (findSecondNode == nodeMap.end())
|
||||
continue;
|
||||
auto &boneParameter = parameters[item.first];
|
||||
boneParameter["fromX"] = QString::number(toOutcomeX(findFirstNode->second.x));
|
||||
boneParameter["fromY"] = QString::number(toOutcomeY(findFirstNode->second.y));
|
||||
boneParameter["fromZ"] = QString::number(toOutcomeZ(findFirstNode->second.z));
|
||||
boneParameter["toX"] = QString::number(toOutcomeX(findSecondNode->second.x));
|
||||
boneParameter["toY"] = QString::number(toOutcomeY(findSecondNode->second.y));
|
||||
boneParameter["toZ"] = QString::number(toOutcomeZ(findSecondNode->second.z));
|
||||
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));
|
||||
boneParameter["fromZ"] = QString::number(toOutcomeZ(findFirstNode->second.z));
|
||||
boneParameter["toX"] = QString::number(toOutcomeX(findSecondNode->second.x));
|
||||
boneParameter["toY"] = QString::number(toOutcomeY(findSecondNode->second.y));
|
||||
boneParameter["toZ"] = QString::number(toOutcomeZ(findSecondNode->second.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>> ¶meters) const;
|
||||
void toParameters(std::map<QString, std::map<QString, QString>> ¶meters, const std::set<QUuid> &limitNodeIds=std::set<QUuid>()) const;
|
||||
void fromParameters(const std::vector<RiggerBone> *rigBones,
|
||||
const std::map<QString, std::map<QString, QString>> ¶meters);
|
||||
|
||||
public slots:
|
||||
void saveHistoryItem();
|
||||
void clearHistories();
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
void paste() override;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -163,13 +163,13 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
|
|||
}
|
||||
|
||||
QAction copyAction(tr("Copy"), this);
|
||||
if (!m_nodePositionModifyOnly && hasSelection()) {
|
||||
if (hasNodeSelection()) {
|
||||
connect(©Action, &QAction::triggered, this, &SkeletonGraphicsWidget::copy);
|
||||
contextMenu.addAction(©Action);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -127,25 +127,37 @@ 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("parameters");
|
||||
std::map<QString, std::map<QString, QString>>::iterator itemsIterator;
|
||||
for (itemsIterator = poseIterator->second.begin(); itemsIterator != poseIterator->second.end(); itemsIterator++) {
|
||||
std::map<QString, QString>::iterator parametersIterator;
|
||||
writer->writeStartElement("parameter");
|
||||
writer->writeAttribute("for", itemsIterator->first);
|
||||
for (parametersIterator = itemsIterator->second.begin(); parametersIterator != itemsIterator->second.end();
|
||||
parametersIterator++) {
|
||||
writer->writeAttribute(parametersIterator->first, parametersIterator->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 = frameIterator->second.begin(); itemsIterator != frameIterator->second.end(); itemsIterator++) {
|
||||
std::map<QString, QString>::iterator parametersIterator;
|
||||
writer->writeStartElement("parameter");
|
||||
writer->writeAttribute("for", itemsIterator->first);
|
||||
for (parametersIterator = itemsIterator->second.begin(); parametersIterator != itemsIterator->second.end();
|
||||
parametersIterator++) {
|
||||
writer->writeAttribute(parametersIterator->first, parametersIterator->second);
|
||||
}
|
||||
writer->writeEndElement();
|
||||
}
|
||||
writer->writeEndElement();
|
||||
writer->writeEndElement();
|
||||
}
|
||||
writer->writeEndElement();
|
||||
}
|
||||
writer->writeEndElement();
|
||||
writer->writeEndElement();
|
||||
}
|
||||
|
@ -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") {
|
||||
|
|
Loading…
Reference in New Issue