Files
opendance-windows/tasks/016-aabb-transforms.md
T
zijunchiang 14ee5f7ec9 fix(scene): 包围盒沿树累乘变换(修掉入树前的 ERROR 与范围偏位)+ 21 项"真机等价"测试
缺陷:SceneRuntime._compute_aabb() 用 MeshInstance3D.global_transform 计算包围盒,
而它在 _assemble() 里调用 —— 那时运行时节点**还没入树**。两个后果:
- Godot 打一行 `ERROR: Condition "!is_inside_tree()" is true. Returning: Transform3D()`
  (真机每次载入场景都刷一条,与"日志里不应有 ERROR"的排查约定冲突);
- 树外的 global_transform 返回单位变换,算出来的只是各网格的**局部** AABB,
  于是重力/风 Area3D 的碰撞盒与兜底地面会偏小或偏位 —— 对把网格摆在非原点位置的
  场景(导出 GLB 的常见情况)**风力可能作用不到物体上**。

修复:改为从根往下累乘 `Node3D.transform`,不依赖是否入树;挂在单位变换父节点下时
结果与旧语义一致。

新增 21 项无头"真机等价"测试(不需要设备):
- tests/test_scene_config.gd:在代码里手写合法 GLB(三角形 + BIN chunk)落盘后
  **真实加载**:warnings 为空、按 colliders 三种取值分别核对碰撞体(mesh / auto 命中
  *_col / auto 未命中的兜底地面 / none)、重力 Area3D 的 gravity 与方向、
  SPACE_OVERRIDE_REPLACE、风力与 WIND_FORCE_SCALE 对齐,以及**带位移网格**
  (translation [10,0,0])时重力区域与兜底地面都跟着位移(该缺陷的回归测试);
- tests/test_pose_server.gd:**时间维度**验证保活 —— 只发应用层 ping、不发关键点帧的
  客户端在 17 秒(> 15 秒静默超时)后仍保持连接("画面里没人就掉线、疯狂重连"的根因
  对照),而完全不发数据的客户端被 close(1001) 断开。

新增 build/check-version.sh:强制 project.godot 的 application/config/version
(「关于」清单与 .pck 的来源)与 project.yaml、发布版本号三者一致;已接入
build/package-release.sh(打包前)与 build/verify.sh(第 0/4 步),并做过反向验证。
此前没有任何机制保证这两个文件同步 —— 界面显示的版本可能和产物名不一致。

版本升至 0.2.1(project.godot / project.yaml / README / 产物名 / tag 全部一致),
About 清单与 .pck 内版本号均为 0.2.1(已用 strings 核对)。

验证:build/verify.sh 四步全通过(版本一致性 → 编译 → 751 项断言 → 180 帧启动无错误);
产物 opendance-windows-v0.2.1.zip(LZMA,26,627,191 B)
sha256 d49885327047f5a52de6c732431ea080cd1a030ae1c581a8bc0ec0d99bfb792c。
2026-09-17 03:59:29 +08:00

70 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# T-WIN-016 场景包围盒沿树累乘变换
- **状态**: done
- **依赖**: T-WIN-013
- **文档**: v1.6 §5.6(场景 GLB 正常加载 / 物理参数生效)
## 缺陷
`SceneRuntime._compute_aabb()` 用 `MeshInstance3D.global_transform` 计算包围盒,
而它在 `_assemble()` 里被调用 —— 那时运行时节点**还没被加入场景树**。两个后果:
1. Godot 会打一行错误日志(每次载入场景都会刷):
```
ERROR: Condition "!is_inside_tree()" is true. Returning: Transform3D()
```
这与「日志里不应出现 ERROR」的排查约定冲突,真机排查时是噪声;
2. `get_global_transform()` 在树外返回单位变换,于是算出来的只是各网格的**局部**
AABB。包围盒用于①重力/风 `Area3D` 的碰撞盒尺寸与位置 ②兜底地面的尺寸与位置 ——
对于把网格摆在非原点位置的场景(导出 GLB 的常见情况),区域会**偏小或偏位**,
**风力/重力可能作用不到物体上**。
## 修复
改为从根节点往下**累乘 `Node3D.transform`**:
```gdscript
static func _compute_aabb(node: Node, parent_transform: Transform3D = Transform3D.IDENTITY) -> AABB:
var local := Transform3D.IDENTITY
if node is Node3D:
local = (node as Node3D).transform
var here := parent_transform * local
...
```
不依赖是否入树;运行时被挂在单位变换的父节点下时,结果与旧的"全局变换"语义一致。
## 回归测试(`tests/test_scene_config.gd`)
在代码里**手写一个合法 GLB**(三角形:POSITION 3×VEC3 + indices 3×u16 + BIN chunk)
落盘后真实加载,覆盖:
* 真实 GLB 加载 `warnings` 为空、不使用兜底地面;
* `colliders` 三种取值:`mesh` → 按网格生成;`auto` 命中 `*_col` → 生成;
`auto` 未命中 → 兜底地面并标记;`none` → 不生成也不兜底;
* 重力 `Area3D` 的 `gravity` / `gravity_direction` 与服务端配置一致,
且用 `SPACE_OVERRIDE_REPLACE`(不改全局 `ProjectSettings`);
* 风力按基础风向量生效(`wind_force_at()` 与 `WIND_FORCE_SCALE` 对齐);
* **带位移网格**(节点 `translation [10,0,0]`)时,重力区域碰撞盒与兜底地面
都跟着位移(x≈10)—— 这一条正是上述缺陷的回归测试。
## 顺带补充:保活的**时间维度**验证(`tests/test_pose_server.gd`)
用真实的 15 秒静默超时跑一次对照:
* 只发应用层 `ping`、**不发任何关键点帧**的客户端,17 秒后**仍保持连接**
(「画面里没人就掉线、然后疯狂重连」的根因对照);
* 连上后什么都不发的客户端被 `close(1001, "timeout")` 断开(超时机制仍有效,
且按契约 §7 对端不会重连)。
## 验收
| 项 | 结果 |
|--|--|
| 无头测试 | **751 项断言全部通过**(原 720) |
| `build/verify.sh` | 全通过(新增第 0 步版本一致性) |
| 日志 | 真实 GLB 加载路径不再出现 `!is_inside_tree()` ERROR |
| 版本号 | `project.godot` = `project.yaml` = 发布版本 = **0.2.1**(`build/check-version.sh` 强制) |