update
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
#include "persistence/AnimationBinary.h"
|
||||
|
||||
#include "persistence/PersistentBinaryObject.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QFile>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace core {
|
||||
namespace {
|
||||
|
||||
qint32 toInt(Project::AnimationTargetKind v) {
|
||||
return static_cast<qint32>(v);
|
||||
}
|
||||
|
||||
qint32 toInt(Project::AnimationProperty v) {
|
||||
return static_cast<qint32>(v);
|
||||
}
|
||||
|
||||
qint32 toInt(Project::AnimationValueType v) {
|
||||
return static_cast<qint32>(v);
|
||||
}
|
||||
|
||||
qint32 toInt(Project::AnimationInterpolation v) {
|
||||
return static_cast<qint32>(v);
|
||||
}
|
||||
|
||||
Project::AnimationTargetKind targetKindFromInt(qint32 v) {
|
||||
if (v == toInt(Project::AnimationTargetKind::Tool)) return Project::AnimationTargetKind::Tool;
|
||||
if (v == toInt(Project::AnimationTargetKind::Camera)) return Project::AnimationTargetKind::Camera;
|
||||
return Project::AnimationTargetKind::Entity;
|
||||
}
|
||||
|
||||
Project::AnimationProperty propertyFromInt(qint32 v) {
|
||||
switch (v) {
|
||||
case 1: return Project::AnimationProperty::UserScale;
|
||||
case 2: return Project::AnimationProperty::Sprite;
|
||||
case 3: return Project::AnimationProperty::Visibility;
|
||||
case 4: return Project::AnimationProperty::DepthScale;
|
||||
case 5: return Project::AnimationProperty::CameraScale;
|
||||
default: return Project::AnimationProperty::Position;
|
||||
}
|
||||
}
|
||||
|
||||
Project::AnimationValueType valueTypeFromInt(qint32 v) {
|
||||
switch (v) {
|
||||
case 1: return Project::AnimationValueType::Double;
|
||||
case 2: return Project::AnimationValueType::Bool;
|
||||
case 3: return Project::AnimationValueType::ImagePath;
|
||||
case 4: return Project::AnimationValueType::PngBytes;
|
||||
default: return Project::AnimationValueType::Vec2;
|
||||
}
|
||||
}
|
||||
|
||||
Project::AnimationInterpolation interpolationFromInt(qint32 v) {
|
||||
return v == 0 ? Project::AnimationInterpolation::Hold : Project::AnimationInterpolation::Linear;
|
||||
}
|
||||
|
||||
void sortAndDedupe(QVector<Project::AnimationKey>& keys) {
|
||||
std::sort(keys.begin(), keys.end(), [](const auto& a, const auto& b) {
|
||||
return a.frame < b.frame;
|
||||
});
|
||||
QVector<Project::AnimationKey> out;
|
||||
out.reserve(keys.size());
|
||||
for (const auto& k : keys) {
|
||||
if (!out.isEmpty() && out.last().frame == k.frame) {
|
||||
out.last() = k;
|
||||
} else {
|
||||
out.push_back(k);
|
||||
}
|
||||
}
|
||||
keys = std::move(out);
|
||||
}
|
||||
|
||||
class AnimationRecord final : public PersistentBinaryObject {
|
||||
public:
|
||||
explicit AnimationRecord(const Project::Animation& animation) : m_src(&animation), m_dst(nullptr) {}
|
||||
explicit AnimationRecord(Project::Animation& animation) : m_src(nullptr), m_dst(&animation) {}
|
||||
|
||||
quint32 recordMagic() const override { return AnimationBinary::kMagicAnimation; }
|
||||
quint32 recordFormatVersion() const override { return AnimationBinary::kAnimationVersion; }
|
||||
|
||||
void writeBody(QDataStream& ds) const override {
|
||||
const Project::Animation& a = *m_src;
|
||||
ds << a.id << a.name << a.payloadPath;
|
||||
ds << qint32(a.fps) << qint32(a.lengthFrames) << bool(a.loop);
|
||||
ds << qint32(a.tracks.size());
|
||||
for (const auto& t : a.tracks) {
|
||||
ds << t.id << toInt(t.targetKind) << t.targetId << toInt(t.property)
|
||||
<< toInt(t.valueType) << toInt(t.interpolation);
|
||||
ds << qint32(t.keys.size());
|
||||
for (const auto& k : t.keys) {
|
||||
ds << qint32(k.frame);
|
||||
switch (t.valueType) {
|
||||
case Project::AnimationValueType::Vec2:
|
||||
ds << double(k.vec2Value.x()) << double(k.vec2Value.y());
|
||||
break;
|
||||
case Project::AnimationValueType::Double:
|
||||
ds << double(k.numberValue);
|
||||
break;
|
||||
case Project::AnimationValueType::Bool:
|
||||
ds << bool(k.boolValue);
|
||||
break;
|
||||
case Project::AnimationValueType::ImagePath:
|
||||
ds << k.stringValue;
|
||||
break;
|
||||
case Project::AnimationValueType::PngBytes:
|
||||
ds << k.bytesValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool readBody(QDataStream& ds) override {
|
||||
Project::Animation a;
|
||||
qint32 fps = 30;
|
||||
qint32 length = Project::kClipFixedFrames;
|
||||
bool loop = true;
|
||||
ds >> a.id >> a.name >> a.payloadPath;
|
||||
ds >> fps >> length >> loop;
|
||||
if (ds.status() != QDataStream::Ok || a.id.isEmpty()) return false;
|
||||
a.fps = std::max(1, int(fps));
|
||||
a.lengthFrames = std::max(1, int(length));
|
||||
a.loop = loop;
|
||||
|
||||
qint32 nTracks = 0;
|
||||
ds >> nTracks;
|
||||
if (ds.status() != QDataStream::Ok || nTracks < 0 || nTracks > 100000) return false;
|
||||
a.tracks.reserve(nTracks);
|
||||
for (qint32 i = 0; i < nTracks; ++i) {
|
||||
Project::AnimationTrack t;
|
||||
qint32 kind = 0;
|
||||
qint32 prop = 0;
|
||||
qint32 valueType = 0;
|
||||
qint32 interpolation = 1;
|
||||
ds >> t.id >> kind >> t.targetId >> prop >> valueType >> interpolation;
|
||||
if (ds.status() != QDataStream::Ok || t.targetId.isEmpty()) return false;
|
||||
t.targetKind = targetKindFromInt(kind);
|
||||
t.property = propertyFromInt(prop);
|
||||
t.valueType = valueTypeFromInt(valueType);
|
||||
t.interpolation = interpolationFromInt(interpolation);
|
||||
|
||||
qint32 nKeys = 0;
|
||||
ds >> nKeys;
|
||||
if (ds.status() != QDataStream::Ok || nKeys < 0 || nKeys > 1000000) return false;
|
||||
t.keys.reserve(nKeys);
|
||||
for (qint32 kidx = 0; kidx < nKeys; ++kidx) {
|
||||
Project::AnimationKey k;
|
||||
qint32 frame = 0;
|
||||
ds >> frame;
|
||||
k.frame = int(frame);
|
||||
switch (t.valueType) {
|
||||
case Project::AnimationValueType::Vec2: {
|
||||
double x = 0.0;
|
||||
double y = 0.0;
|
||||
ds >> x >> y;
|
||||
k.vec2Value = QPointF(x, y);
|
||||
break;
|
||||
}
|
||||
case Project::AnimationValueType::Double:
|
||||
ds >> k.numberValue;
|
||||
break;
|
||||
case Project::AnimationValueType::Bool:
|
||||
ds >> k.boolValue;
|
||||
break;
|
||||
case Project::AnimationValueType::ImagePath:
|
||||
ds >> k.stringValue;
|
||||
break;
|
||||
case Project::AnimationValueType::PngBytes:
|
||||
ds >> k.bytesValue;
|
||||
break;
|
||||
}
|
||||
if (ds.status() != QDataStream::Ok) return false;
|
||||
t.keys.push_back(k);
|
||||
}
|
||||
sortAndDedupe(t.keys);
|
||||
a.tracks.push_back(t);
|
||||
}
|
||||
|
||||
*m_dst = std::move(a);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const Project::Animation* m_src;
|
||||
Project::Animation* m_dst;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool AnimationBinary::save(const QString& absolutePath, const Project::Animation& animation) {
|
||||
if (absolutePath.isEmpty() || animation.id.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return AnimationRecord(animation).saveToFile(absolutePath);
|
||||
}
|
||||
|
||||
bool AnimationBinary::load(const QString& absolutePath, Project::Animation& animation) {
|
||||
return AnimationRecord(animation).loadFromFile(absolutePath);
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
Reference in New Issue
Block a user