多面射线遍历与原版碰撞法线缓存

本批继续在 main,未创建 worktree、未提交或 attach 原版游戏。按 godot-master 的数值/表现分层,把原版运算接入 CoreSim;没有用近似场景射线冒充完整LOS。

已实现与范围

  • 467000全脸遍历,以及4672E0显式候选列表的顺序、过滤、最近命中状态。
  • 469570碰撞法线缓存构造及原版Ogre normalise。
  • 5757F0导入网格时的反序面索引:原始index2、index1、index0。
  • 使用原生构造的法线,在墙/桥/石头整份样本网格上进行混合缓存精度与查询精度对照。
  • 数值核能处理这些完整输入;真实场景分区候选生产、材质面标志和全部碰撞模型的运行时接线仍未完成。

构造覆盖仍是 1053/1419(14成长),319个host/NG+待补、47个基础等级缺口。没有因数学核通过而虚增可用攻击型proc,全武器目标未完成。

本批原生对照

源EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e。 OgreMain.dll SHA256: 974c1dc77ea2818276cc7eb94a8b95b2ccf8595e28fa3f14b2cd6d516fdb67a1

独立32位工具执行未修改的发行代码页:

  • 467600→467000:全脸路径。
  • 4672E0:指定候选列表路径。
  • 469570:原版法线构造;预先提供足够输出容量,避免调用游戏全局分配器。
  • 向量减法/叉乘/normalise及其常量均取原版Ogre代码。

原生法线2261组×3精度=6783次,多面/候选2980组×3精度=8940次,全部零差异。 原生控制字分别为007F、027F、037F,运行时线程的实际控制字仍是独立接线要求,不猜默认。

构建:

text
python -m tools.build_native_multi_ray_oracle
dotnet run --project core_sim.tests

该入口会依次重建当前原生单面工具、法线样本与多面样本。结果保存输入/测试程序hash,防止代码改变后误用旧样本。

不能合并的两个遍历契约

全脸路径

467600构造射线包围盒,467000将最近距离重置为99999,再按面序号遍历。 面过滤读该面的surface标志,后续进入面包围盒、单面、交点、内部判断和距离比较。

显式候选列表路径

4672E0保留调用方传入的最近距离,不重置。它保留候选列表顺序,也不去重。

原始机器码还存在一个确定的索引不对称:

  • 过滤读 surface[candidateOrdinal];
  • 几何与命中结果使用 candidateFaceIndex,并报告 surface[candidateFaceIndex]。

467336的字节 0F B6 14 0F 对应 ordinal 访问,不应把它改成“看起来更合理”的面索引。 示例:三面标志10/20/30,候选[2,0],跳过标志10。原版会跳过首候选面2,却可能命中并报告面0的标志10。

返回值也不等于本次有新交点:候选为空、传入最近距离7时,返回Hit=true但没有更新输出。 核心结果因此分开保存 HitUpdated,不拿布尔返回值伪造交点。

距离比较发生在float写回之前

原版先比较Ogre length留在x87中的值与已保存的float最近距离,再将新值写回float。 这不等于比较两个float距离。本批移除了前批未承载这一差别的简化比较helper,统一使用数值核。

已加入原生反例:两个完全相同的三角形,射线长度为sqrt(5)。

查询精度最终surface
PC24第一个面
PC53第二个面
PC64第二个面

三个模式最终保存的float距离完全相同。原因是PC53/64的精确距离比前一轮向上舍入后的float小,后一个面得以覆盖。 所以“同距离永远保留最早面”只能用于实际参与比较的两个操作数相等,不能用于几何距离或最终float看起来相等的情况。

原版法线构造

5757F0先把Ogre面索引倒序传给468C00,再由469570生成法线:

text
edge0 = f32_vector(C - B)
edge1 = f32_vector(B - A)
normal = f32_vector(edge0 × edge1)
length = Ogre length(normal)
若 length > DOUBLE(1e-8):
    reciprocal = x87(1 / length)
    normal.xyz = f32(normal.xyz * reciprocal)
否则:
    保持原向量不变

C−B不能擅自替换成数学上等价的C−A,float写回后并非处处等价。 Ogre门限是10388B78处的 double 1e-8,不是float 1e-8;极小的非零法线可能保持未归一化。 核心 CollisionFaceFromOgre 同时保留倒序索引与这一运算顺序。

缓存生成精度与后续查询精度是两回事:法线一旦写入float缓冲区便成为输入。 本批真实网格样本分别以三种精度生成法线,再在三种查询精度下测试,不要求两者必须相同。

原版网格样本边界

仍使用墙2面、桥42面、石头12面,共56个原始面。原生法线用于这些完整样本网格的168组射线设置,随后各执行三种查询精度。

这些样本的法线已不再是前批探针简化公式;但surface仍使用显式探针面标签,用来判断选中的面。 原版材质→surface分类、整场景实例变换和分区候选生产没有因此自动完成。

验证

  • build/native_multi_ray_core.log1156条核心断言通过
  • 平面9063、内部9081、单面5595、多面8940、法线6783次原生对照均零差异。
  • build/native_multi_ray_python.log81项相关来源/字节/F2/既有检查通过。
  • Godot C#零warning/error;coresimlint通过。
  • 本批无新渲染行为,不以历史游戏截图证明完整LOS或闪电链已经可用。
  • 默认存档SHA256仍为 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f

下一段继续:真实碰撞模型导入、材质面标志、分区候选、实例变换和实际线程精度,继而把查询/伤害回调接到攻击型proc。完整目标保持不变。