Files
zijunchiang 5581cbe05d feat(scene): 场景选择与物理落地 + 整包发布(T-WIN-012/013/014)
T-WIN-013:API 升级与场景选择
- 契约快照同步到 rest-api 0.5.0 / data-models 0.7.0 / database 0.4.0
- catalog_types.gd 新增 SceneConfig(物理/环境/出生点/相机)与 Scene;
  CatalogBundle 增加 scenes[];from_dict 按契约 7.1 再收敛一次
  (缺项默认、越界夹紧、非法枚举回退),与服务端 normalizeSceneConfig 同规则
- 新增 src/scene/scene_runtime.gd:GLB 载入 -> 碰撞体(mesh 三角网 /
  auto 命名约定 *_col、*_colonly / none 不兜底)-> 场景级重力(Area3D
  SPACE_OVERRIDE_REPLACE,不改全局 ProjectSettings)-> 风(每物理帧
  apply_central_force,阵风正弦叠加,支持 local 坐标系)-> 环境光与雾
  -> 出生点与相机(follow/fixed/orbit);无碰撞体时兜底地面
- CatalogStore 增加 scenes/default_scene/find_scene/ensure_scene_cached/
  fetch_thumbnail(场景缓存带 sha256 指纹,避免换包后仍用旧 GLB)
- 主界面改为 2x2 四个选择器(曲目/动作/模型/场景)+ 场景参数面板
  (缩略图 + 重力/摩擦/弹性/碰撞体/风摘要)
- 游戏界面载入场景并落地物理,角色落在出生点;场景缺失/下载失败/
  解析失败一律回退内置空场景并提示,不阻塞开始跟练

T-WIN-012:「关于」清单展示当前版本号
- 版本唯一来源为 project.godot 的 application/config/version(v0.2.0)
- about_manifest() 输出版本/引擎/契约/协议/地址/隐私清单;
  无头测试无 autoload,读取 AppConfig 走 get_node_or_null 容错
- Credits 的单个 CONTRACT_VERSION 常量改为 CONTRACT_VERSIONS 字典
  (六项契约各自独立递增),并双向核对 contracts/VERSION

T-WIN-014:发布必须是单个完整包
- build/package-release.sh 重写:只产出单个 zip + SHA256SUMS,
  清理历史 .partN,超限时告警而非回退分卷;新增源产物与解压后完整性
  自检(PE 头 MZ / pck 魔数 GDPC / exe+pck 同目录同名)与体积-上限对比
- deploy/proxy 相关的 Gitea IIS 256MB 上限说明见 opendance-web

测试:tests/test_scene_config.gd(12 例)+ tests/test_about.gd(4 例);
无头测试 687 通过 / 0 失败(10 个套件),verify.sh 全部通过
2026-09-17 00:45:23 +08:00

6.0 KiB

DSH.md -- opendance-windows(OpenDance Studio 项目二:Windows 客户端)

0. 本文件的作用

本文件是 opendance-windows 仓库内的 DSH 执行约束。顶层说明书见 《DSH 开发文档:OpenDance Studio》,二者冲突时以顶层文档为准。

1. 项目定位

Windows 客户端是核心体验载体:接收 Android 辅助 App 推送的 33 点骨骼关键点, 驱动 VRM 角色,并与 Web 服务端下发的参考动作做实时 DTW 评分。

Android 辅助 App ──239 字节关键点帧──▶ 本端(WebSocket 服务端:8765)
                                          │
Web 服务端 ──GET /api/catalog/export──────┤
           ──GET /api/motions/:id/keypoints┘
                                          ▼
                                    VRM 驱动 + DTW 评分

本端是 WebSocket 的「服务端」,Android 主动连入(契约 websocket-keypoints.md 1)。

2. 技术栈

组件 选型 说明
引擎 Godot 4.7.2(MIT) 可在 Linux 上导出 Windows exe
VRM 加载 Godot 内置 GLTFDocument 见 6 的选型变更说明
多格式导入 Godot 内置 glTF 导入器 FBX 由管理端转 BVH 后下发
通信 WebSocketPeer + HTTPRequest 无第三方依赖
评分 DTW + 关节角度差异 纯 GDScript 实现
渲染 GL Compatibility 兼容性最好,便于无 GPU 导出

3. 单次会话加载范围

允许加载:

  1. 本文件 DSH.md
  2. project.yaml
  3. 当前 tasks/xxx.md
  4. 该任务文件中 load 列出的文件
  5. contracts/ 中被明确引用的锚点片段

禁止加载:

  • opendance-web / opendance-android 的任何源码
  • 非当前任务的 tasks/*.md
  • 完整 contracts/ 目录(只引用锚点)
  • .godot/ 导入缓存、build/windows/ 导出产物、_scratch/ 下的模型素材

4. 上下文预算

项 建议值
单任务文件数 <= 6
单任务代码行数 <= 1500
单任务 Token <= 12K
契约引用 只引用锚点,不加载全文

5. 目录结构

opendance-windows/
├── project.godot            # Godot 工程(autoload、渲染、窗口配置)
├── export_presets.cfg       # ★ 必须位于项目根目录(Godot 硬性要求)
├── project.yaml / DSH.md
├── tasks/                   # 001~011 单任务说明书
├── contracts/               # 只读契约快照(来源 opendance-web;六项契约各自独立版本)
├── scenes/main.tscn         # 仅一个挂载 main.gd 的根节点
├── src/
│   ├── autoload/            # AppConfig / CatalogStore / PoseReceiver / ScoreService
│   ├── net/                 # 239 字节编解码、WebSocket 服务、REST 客户端、NDJSON 解析
│   ├── model/               # VRM 加载、humanoid 骨骼名映射
│   ├── anim/                # 关键点 -> 骨骼重定向
│   ├── score/               # 特征提取、DTW、评分规则
│   └── ui/                  # 主界面 / 游戏界面 / 结算 / 关于 / 来源展示
├── tests/                   # 无头测试框架 + 用例
└── build/                   # 导出脚本(sh / ps1)

6. 界面构建方式与选型变更(重要)

界面全部用 GDScript 代码构建,不手写 .tscn。 .tscn 是带 uid 与节点路径的手写格式,在没有编辑器的情况下极易出错且难以验证; 用代码构建可随脚本一起被无头导入和单元测试覆盖。scenes/main.tscn 因此只保留一个根节点。

VRM 加载改用 Godot 内置 glTF 导入器(文档 5.1 原选型为 godot-vrm GDExtension):

  • V-Sekai/godot-vrm 不发布任何预编译产物(无 GitHub Release,仅源码, 需用 godot-cpp 本地编译原生库),这会同时破坏「Linux 上导出 Windows exe」的验收路径。
  • VRM 本身就是 glTF 2.0 二进制容器:实测内置导入器可完整解析 154 骨骼的 VRoid 模型, 且 GLTFState.json 可直接读取 extensions.VRMC_vrm / extensions.VRM 判定版本。
  • humanoid 骨骼按名字映射(src/model/humanoid_map.gd),覆盖 VRoid / VRM 0.x / Mixamo 三种命名。

若日后接入 godot-vrm,只需替换 VrmLoader.load_from_file,对外接口不变。

7. 硬性约束

  1. 绝不让视频帧离开设备:本端只接收 33 个关键点的坐标,不接收也不存储图像。
  2. 契约先行:改交互先改 opendance-web 的 contracts/,再回本仓库同步快照。
  3. 不假设骨骼轴向约定:重定向基于「静止朝向 -> 目标朝向」的最小旋转, pose = parent_global_basis⁻¹ x desired 这一合成公式由真实 VRM 实测标定, 有回归测试守护,不得凭记忆改写。
  4. 上传/下载的模型不入库:真实 VRM 样例仅放在 _scratch/(已 gitignore), 版权原因不得提交。
  5. 不使用 SMPL 拓扑,统一 MediaPipe 33 点。
  6. 关于页面与 THIRD_PARTY.md 必须与 contracts/open-source-credits.md 一致。

8. 验证(每任务执行)

GODOT=/path/to/godot           # Godot 4.7.x

# 1. 全部脚本编译通过(无输出即通过)
$GODOT --headless --path. --import 2>&1 | grep -E "SCRIPT ERROR|Parse Error"

# 2. 无头单元测试 + 真实 API 集成(API 不可达时相关用例显式跳过)
$GODOT --headless --path. --script res://tests/run_tests.gd

# 3. 应用可启动(autoload + UI 构建无报错)
$GODOT --headless --path. --quit-after 180

# 4. Windows 导出(需先安装 export templates)
bash build/export-windows.sh release

9. 上下文超标时的拆分策略

超标原因 拆分方式
单任务涉及多个界面 按界面拆(主/游戏/结算/关于)
单文件过大 按类拆,先生成接口再分实现
契约全文加载 改用锚点引用
需要参考其他项目 只读契约,不读源码
UI 与逻辑耦合 UI 只调用已定义接口(GameScreen 不做 DTW 计算)
测试与实现同任务 测试单独任务,共享接口摘要