原版网格与 Room Piece 几何扫掠
本批交付
在已有世界候选和最近命中控制层下,补齐明确输入域内的网格扫掠和物件坐标封装:
- 全网格 4676A0、候选索引 467C80,以及 4682D0 的包围盒扩张。
- 683860 带半径平面分类、6839B0 无限直线与平面交点、683710 线段最近点、683B70 三边最近点。
- 622670 的物件前置门、局部半径和坐标转换。
- F2 原始碰撞资产到扫掠几何的适配。
2,982 组完整网格输出、2,982 组辅助函数输出、2,115 组完整物件输出,在 x87 24/53/64 位精度下逐位对照一致。参考直接执行原 EXE 和随游戏附带 Ogre 的实际指令,没有用预设碰撞结果代替几何运算。
核心累计 1,401 条断言、224 项相关 Python 回归通过。真实 ROCK_03_COLLISION 的 14 个面均完成定向扫掠测试。候选收集 → 世界选择 → 实际静态/Room 几何的受控组合测试也通过;这不是整张地图接入完成的声明。
两条网格路径必须保留的差异
| 行为 | 全网格 4676A0 | 候选索引 467C80 |
|---|---|---|
| 几何面 | 当前 faceID | candidate[ordinal] |
| skip surface 读取 | 当前 faceID | ordinal |
| 面内交点距离比较 | 严格小于 nearest | 小于等于 nearest |
| 边缘距离用于 nearest | 从 start 到最近边点 | 从 end 到最近边点 |
| 边缘法线 dot 参照 | 当前 faceID 的法线 | ordinal 对应法线 |
| 输出 surface | 当前面 | 实际 candidate faceID |
不能把索引版改成“遍历子集后调用同一套面处理”。它的 ordinal 读取和 endDistance 复用由原始汇编及随机/重排输入对照确认。
候选数组保留顺序和重复索引,不排序、不去重。当前明确输入域要求候选数量不超过提供的面缓冲区长度;超过时,原版可能继续读 ordinal 对应的额外容量槽。本实现没有这些槽的原始值,因此显式报 native_sweep_mesh_storage_extent,不补零、不截断。此域扩展仍待真实世界构造侧的容量证据。
实际几何流程
- 全模型和逐面 3D 包围盒与查询盒重叠检查;查询盒缺省为 start/end min/max 各向外扩 radius。
- start 做普通平面分类,end 做带 radius 分类;两侧相同或 end 分类为 0 时跳过。
- shiftedEnd = f32(end - f32(normal * radius))。
- 以 start → shiftedEnd 求无限直线交点。6839B0 不限制 t 在 [0,1],不能复用有限线段射线函数。
- 若交点在三角形内,按上表的最近距离规则接受。
- 否则,从 start 求三条边上的最近点,再检查 end 到该点的距离是否不大于 radius。
- 边缘候选法线是 normalize(f32(end - closest));dot 必须严格小于当前 edgeDot(初值 1),且 end 的普通平面分类为 0。
- 更新 nearest、接触点、法线、adjusted = f32(point + f32(normal * radius)) 和 surface。
edgeDot 是整次函数的状态,不在每个面开始时重置。
辅助输出:原辅助顶点存在时,按原 Ogre 顺序先逐次 f32 向量加法,再以 1/3 的保留精度乘各分量;不存在时输出 (1,1,1)。没有把它命名成未经证实的颜色/光照数据。
x87 与退化输入
平面 dot 的实际加法顺序为 Z、Y、X,epsilon 是原 float32 的 1e-5。直线交点的 X 保留中间 x87 值,Y/Z 则重载已经存储的 float32 delta 和 t。
线段最近点的分母使用已存储的 f32(B-A) 求平方长度,分子保留原差值;退化的零长度边可以得到 0/0 的原 NaN,而不是自动选端点。
三角形三边顺序 AB、BC、CA。首个平方距离存为 f32,后续候选以保留 x87 距离与已存储 best 作严格比较。等距保留前边;首个 NaN 也不能用一般有限数最小值逻辑覆盖。
返回值为 nearest != 99999。调用方预填 nearest=1,即使完全没有写入结果,也可能返回 true。未更新时必须保留调用方向量/surface,而不是清零。
测试中特别使用 end.z=.75、radius=.25 得到精确 t=1/2,以验证距离恰好为 1 的两路径差异。普通十进制输入会留下 float32 交点残差,不足以证明精确等距行为。
Room Piece 622670
前置门与 622950 相同:
- 必须有碰撞模型。
- ignoreVisibility 可以绕过 enabled/本地可见/派生可见门,但不绕过 VisualBake/AlwaysBake/CollisionBake 排除。
- 仅比较 end 与物件原点的 X/Z 距离是否在 50 以内,不检测 start 或 Y。
- 排除门在构建矩阵之前,因此已排除的零缩放对象不应触发求逆错误。
通过门以后:
- 原点取 61DCE0(useDerived=true) 的结果;有节点且有父对象时使用派生位置,否则用保存位置。
- 矩阵使用派生四元数 × 保存的局部 scale。
- start/end 先减原点,再乘逆矩阵。
- localRadius = f32(radius / storedScale.Z),不取绝对值,不取最大缩放。
- 局部 nearest 从 99999 开始,执行 4682D0 → 4676A0。
- 命中点和 adjusted 乘正矩阵再加原点;法线只旋转,不使用逆转置、不重新归一化。
- 成功时 NOPATH 覆盖 surface DWORD 为 100。
- 4682D0 的辅助向量是局部 scratch,不能覆盖世界查询的共享辅助输出。
NativeRoomSweep 是显式输入数值内核,派生姿态等 getter 的生命周期/重复读取时序仍由后续真实宿主负责;原生对照中的模型/姿态/可见性 getter 是明确的固定输入边界。未证明回调中修改四元数等任意副作用的等价。
非有限/奇异变换仍沿用明确诊断,不声称复制原版非法指针、任意 NaN 状态或未提供容量槽的结果。
真实碰撞数据接入
NativeCollisionAsset.BuildSweepGeometry:
- 复用原 Ogre 索引反转和已验证法线计算。
- 总包围盒使用全部原始顶点,包括没有被面索引引用的点。
- 每面包围盒来自该面的三个顶点。
- 新建 5757F0 碰撞模型通过 468BC0 追加位置,没有追加辅助顶点,因此该路径的辅助缓冲区明确缺省。这不意味着合并后的世界网格也没有辅助数据。
14 面岩石测试使用真实 F2 缓存和核心原法线;原生网格参考中的同一资产切片法线是明确标注的 probe-generated reverse-cross normal,两者证据范围没有混淆。
验证与产物
- core_sim/NativeSweepGeometry.cs、NativeRoomSweep.cs。
- core_sim.io/F2NativeCollision.cs 的 BuildSweepGeometry。
- core_sim.tests/NativeMeshSweepChecks.cs:逐位比较、等距/过滤/副作用和受控组合测试。
- tools/build_native_mesh_sweep_oracle.py、native_mesh_sweep_oracle.c。
- tools/build_native_room_sweep_oracle.py、native_room_sweep_oracle.c。
- tests/test_native_mesh_sweep.py:原文件及脚本哈希、三精度输入全集、边缘和可见性门回归。
- build/native_mesh_sweep_oracle/results.json、build/native_room_sweep_oracle/results.json。
- build/native_mesh_sweep_mismatches.json、build/native_room_sweep_mismatches.json 均为空。
- build/native_mesh_sweep_core.log、native_mesh_sweep_python.log、native_mesh_sweep_godot.log、native_mesh_sweep_visual.log。
EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e。
OgreMain.dll SHA256: 974c1dc77ea2818276cc7eb94a8b95b2ccf8595e28fa3f14b2cd6d516fdb67a1。
Godot C# 构建零警告、零错误,coresimlint 通过,现有 21 项 GPU/资源生命周期回归重新运行通过。GPU 回归不是本批扫掠像素一致性验收,正式地图没有替换为新世界查询。
仍需继续
后续进展:初始碰撞对象查询与完整几何世界扫掠已补齐 5E05E0/5125F0 并接通三路实际几何;下面是本篇交付时的剩余状态。
下一段是原版初始碰撞对象查询 5E05E0、静态四叉树构造与世界对象上下文,再把完整查询接给导航贡献与攻击遮挡。已有组合测试明确跳过初始查询;若误调用会抛错,没有“总是未命中”的假实现。
武器可构造覆盖仍为 1,053/1,419;攻击型 proc、动画、缺导出资产和全武器实机验收未完成。本批不能据此增加武器完成数。
按 godot-master 的分层要求,几何在核心层,资产由 IO 层提供,表现层不拥有碰撞结算。快照 v29 未变。main 原地工作,未提交、未 attach 原游戏,默认存档 SHA256 未变: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f。