新增根据帧数控制可见性

This commit is contained in:
2026-04-09 17:30:55 +08:00
parent e116a9ec79
commit d67d7dc0c5
29 changed files with 4387 additions and 408 deletions

View File

@@ -4,6 +4,7 @@
#include <QString>
#include <QPointF>
#include <QHash>
#include <QVector>
#include <algorithm>
@@ -12,6 +13,8 @@ namespace core {
class Project {
public:
static constexpr int kClipFixedFrames = 600;
void setName(const QString& name) { m_name = name; }
const QString& name() const { return m_name; }
@@ -37,10 +40,15 @@ public:
void setFps(int fps) { m_fps = std::max(1, fps); }
int fps() const { return m_fps; }
struct ToolKeyframeBool {
int frame = 0;
bool value = true;
};
struct Entity {
QString id;
QString displayName; // 显示名(空则界面用 id
bool visible = true; // Outliner 眼睛:默认显示
bool visible = true; // 默认显隐(无 visibilityKeys 时使用)
// 可移动实体形状:存为局部坐标(相对 originWorld
QVector<QPointF> polygonLocal;
// 从背景中抠洞的位置:固定在创建时的 world 坐标,不随实体移动
@@ -55,6 +63,15 @@ public:
// 抠图创建时该位置对应的原始距离乘子0.5+depth01用于校准该处 distanceScale==1.0。0 表示未校准(兼容旧工程)
double distanceScaleCalibMult = 0.0;
// 距离缩放开关:为 true 时实体不受 depth->distanceScale 影响,仅受 userScale 影响。
// 约定:对话气泡等 UI 元素默认打开。
bool ignoreDistanceScale = false;
// 父子关系:当 parentId 非空时实体会保持相对父实体的偏移world 坐标)。
// parentOffsetWorld 表示「childOrigin - parentOrigin」在 world 中的偏移。
QString parentId;
QPointF parentOffsetWorld;
struct KeyframeVec2 {
int frame = 0;
QPointF value;
@@ -82,12 +99,137 @@ public:
QVector<KeyframeDouble> userScaleKeys;
QVector<ImageFrame> imageFrames;
// 可见性轨道:布尔关键帧(显示/隐藏渲染时会被解释为“10 帧淡入淡出”。
QVector<ToolKeyframeBool> visibilityKeys;
EntityIntroContent intro;
};
void setEntities(const QVector<Entity>& entities) { m_entities = entities; }
const QVector<Entity>& entities() const { return m_entities; }
// —— 工具(精简版实体,不含 intro/图片/视频)——
struct Tool {
enum class Type { Bubble };
QString id;
QString displayName;
bool visible = true; // 编辑模式显隐
// 父子关系同实体规则。parentId 可指向实体或工具的 id。
QString parentId;
QPointF parentOffsetWorld;
// 基准位置(无关键帧时使用)
QPointF originWorld;
QVector<Entity::KeyframeVec2> locationKeys;
// 可见性轨道:布尔关键帧(显示/隐藏渲染时会被解释为“10 帧淡入淡出”。
QVector<ToolKeyframeBool> visibilityKeys;
// 类型与 payload
Type type = Type::Bubble;
// Bubble payload
QString text;
int fontPx = 18;
enum class TextAlign { Left, Center, Right };
TextAlign align = TextAlign::Center;
// 气泡底边「平直段」上三角形附着位置0=靠左0.5=居中1=靠右;主体会水平平移,使该点始终位于 originWorld 尖端正上方
double bubblePointerT01 = 0.5;
};
void setTools(const QVector<Tool>& tools) { m_tools = tools; }
const QVector<Tool>& tools() const { return m_tools; }
// —— 动画系统Blender/NLA 风格简化版,工程级)——
struct AnimationClip {
QString id;
QString name;
// Entity channels (keyed by entity id)
QHash<QString, QVector<Entity::KeyframeVec2>> entityLocationKeys;
QHash<QString, QVector<Entity::KeyframeDouble>> entityUserScaleKeys;
QHash<QString, QVector<Entity::ImageFrame>> entityImageFrames;
QHash<QString, QVector<ToolKeyframeBool>> entityVisibilityKeys;
// Tool channels (keyed by tool id)
QHash<QString, QVector<Entity::KeyframeVec2>> toolLocationKeys;
QHash<QString, QVector<ToolKeyframeBool>> toolVisibilityKeys;
};
struct NlaStrip {
QString id;
QString clipId;
int startSlot = 0; // slot index; 1 slot = kClipFixedFrames frames
int slotLen = 1; // currently fixed to 1; reserved for future
bool enabled = true;
bool muted = false;
};
struct NlaTrack {
QString id;
QString name;
bool muted = false;
bool solo = false;
QVector<NlaStrip> strips;
};
struct AnimationScheme {
QString id;
QString name;
QVector<NlaTrack> tracks;
};
void setAnimationClips(const QVector<AnimationClip>& clips) { m_clips = clips; }
const QVector<AnimationClip>& animationClips() const { return m_clips; }
void setAnimationSchemes(const QVector<AnimationScheme>& schemes) { m_schemes = schemes; }
const QVector<AnimationScheme>& animationSchemes() const { return m_schemes; }
void setActiveSchemeId(const QString& id) { m_activeSchemeId = id; }
const QString& activeSchemeId() const { return m_activeSchemeId; }
void setSelectedStripId(const QString& id) { m_selectedStripId = id; }
const QString& selectedStripId() const { return m_selectedStripId; }
const AnimationScheme* findSchemeById(const QString& id) const {
for (const auto& s : m_schemes) {
if (s.id == id) return &s;
}
return nullptr;
}
AnimationScheme* findSchemeById(const QString& id) {
for (auto& s : m_schemes) {
if (s.id == id) return &s;
}
return nullptr;
}
const AnimationClip* findClipById(const QString& id) const {
for (const auto& c : m_clips) {
if (c.id == id) return &c;
}
return nullptr;
}
AnimationClip* findClipById(const QString& id) {
for (auto& c : m_clips) {
if (c.id == id) return &c;
}
return nullptr;
}
const AnimationScheme* activeSchemeOrNull() const {
const AnimationScheme* s = findSchemeById(m_activeSchemeId);
if (s) return s;
return m_schemes.isEmpty() ? nullptr : &m_schemes.front();
}
AnimationScheme* activeSchemeOrNull() {
AnimationScheme* s = findSchemeById(m_activeSchemeId);
if (s) return s;
return m_schemes.isEmpty() ? nullptr : &m_schemes.front();
}
private:
QString m_name;
QString m_backgroundImagePath;
@@ -98,6 +240,12 @@ private:
int m_frameEnd = 600;
int m_fps = 60;
QVector<Entity> m_entities;
QVector<Tool> m_tools;
QVector<AnimationClip> m_clips;
QVector<AnimationScheme> m_schemes;
QString m_activeSchemeId;
QString m_selectedStripId;
};
} // namespace core