#pragma once #include "domain/EntityIntro.h" #include #include #include #include #include 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; } // 背景图在项目目录内的相对路径,例如 "assets/background.png" void setBackgroundImagePath(const QString& relativePath) { m_backgroundImagePath = relativePath; } const QString& backgroundImagePath() const { return m_backgroundImagePath; } // 背景在视口/预览中的显隐(默认显示) void setBackgroundVisible(bool on) { m_backgroundVisible = on; } bool backgroundVisible() const { return m_backgroundVisible; } void setDepthComputed(bool on) { m_depthComputed = on; } bool depthComputed() const { return m_depthComputed; } // 深度图在项目目录内的相对路径,例如 "assets/depth.png" void setDepthMapPath(const QString& relativePath) { m_depthMapPath = relativePath; } const QString& depthMapPath() const { return m_depthMapPath; } void setFrameStart(int f) { m_frameStart = f; } int frameStart() const { return m_frameStart; } void setFrameEnd(int f) { m_frameEnd = f; } int frameEnd() const { return m_frameEnd; } 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; // 默认显隐(无 visibilityKeys 时使用) // 可移动实体形状:存为局部坐标(相对 originWorld) QVector polygonLocal; // 从背景中抠洞的位置:固定在创建时的 world 坐标,不随实体移动 QVector cutoutPolygonWorld; // 背景空缺标识:用于项目树节点和交互,空则按 id 生成默认值 QString blackholeId; // 背景空缺可见性:与实体可见性轨道解耦,控制背景空缺是否显示 bool blackholeVisible = true; // 背景空缺修复方案:copy_background / use_original_background / model_inpaint(预留) QString blackholeResolvedBy; QPointF originWorld; int depth = 0; // 0..255 QString imagePath; // 相对路径,例如 "assets/entities/entity-1.png" QPointF imageTopLeftWorld; // 贴图左上角 world 坐标 // 人为整体缩放,与深度驱动的距离缩放相乘(画布中 visualScale = distanceScale * userScale; // distanceScale 在有 distanceScaleCalibMult 时为 (0.5+depth01)/calib,使抠图处为 1.0) double userScale = 1.0; // 抠图创建时该位置对应的原始距离乘子(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; }; struct KeyframeFloat01 { int frame = 0; double value = 0.5; // 0..1,默认 0.5 -> scale=1.0(0.5..1.5 映射) }; struct KeyframeDouble { int frame = 0; double value = 1.0; }; struct ImageFrame { int frame = 0; QString imagePath; // 相对路径 }; // v2:project.json 仅存 id + payload,几何与动画在 entityPayloadPath(.hfe)中。 QString entityPayloadPath; // 例如 "assets/entities/entity-1.hfe" // 仅打开 v1 项目时由 JSON 的 animationBundle 填入,用于合并旧 .anim;保存 v2 前应为空。 QString legacyAnimSidecarPath; QVector locationKeys; QVector depthScaleKeys; QVector userScaleKeys; QVector imageFrames; // 可见性轨道:布尔关键帧(显示/隐藏);渲染时会被解释为“10 帧淡入淡出”。 QVector visibilityKeys; EntityIntroContent intro; }; void setEntities(const QVector& entities) { m_entities = entities; } const QVector& 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 locationKeys; // 可见性轨道:布尔关键帧(显示/隐藏);渲染时会被解释为“10 帧淡入淡出”。 QVector 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& tools) { m_tools = tools; } const QVector& tools() const { return m_tools; } // —— 动画系统(Blender/NLA 风格简化版,工程级)—— struct AnimationClip { QString id; QString name; // Entity channels (keyed by entity id) QHash> entityLocationKeys; QHash> entityUserScaleKeys; QHash> entityImageFrames; QHash> entityVisibilityKeys; // Tool channels (keyed by tool id) QHash> toolLocationKeys; QHash> 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 strips; }; struct AnimationScheme { QString id; QString name; QVector tracks; }; void setAnimationClips(const QVector& clips) { m_clips = clips; } const QVector& animationClips() const { return m_clips; } void setAnimationSchemes(const QVector& schemes) { m_schemes = schemes; } const QVector& 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; bool m_backgroundVisible = true; bool m_depthComputed = false; QString m_depthMapPath; int m_frameStart = 0; int m_frameEnd = 600; int m_fps = 60; QVector m_entities; QVector m_tools; QVector m_clips; QVector m_schemes; QString m_activeSchemeId; QString m_selectedStripId; }; } // namespace core