角色碰撞资源请求与局部范围初始化

本批结果

补齐 CCharacter 装载器的碰撞文件请求解析、原始几何输入和局部 culling 范围计算,并以 ARBITER 原数据跑通实际默认 collider。

  • 888 组三精度原生局部范围对照逐位一致。
  • 7,731 份本地 UNIT DAT 保留角色装载器解释下的输入,得到 225 个原始请求。
  • 224 个请求具备已校验几何,对应 222 份不同文件;1 个未就绪。
  • 7,249 份输入没有 COLLISIONFILE,按该装载器规则请求默认 collider。
  • 默认 collider 为原始 72 顶点/24 面,24 组定向扫掠全部通过。
  • 核心累计 1,433 条断言、258 项相关 Python 回归通过;Godot C# 构建零警告、零错误,coresimlint 通过。

这不是 7,731 个 CCharacter 实例。表保留所有 DAT 的潜在解释,实际运行时类必须由工厂先确定;CItem/CEquipment 不自动套用 CCharacter 装载规则。

请求路径:55E110 与 67FF70

text
if COLLISIONFILE is empty:
    path = "media/models/collider.mesh"
else:
    path = RESOURCEDIRECTORY + "/" + COLLISIONFILE + ".mesh"

replace every backslash with slash
repeatedly replace "//" with "/"

COLLISIONFILE 和 RESOURCEDIRECTORY 的缺省字符串为空,COLLISION_ADD 缺省为 0.25。

重要区别:

  • 67FF70 不转换大小写
  • 不裁剪空白,不解析 "."/"..",不保留 UNC 双斜杠。
  • 非空 COLLISIONFILE 总追加 ".mesh",不会按扩展名猜测是否已经写过。
  • 文件名为空时忽略目录,使用原默认路径。
  • 文件系统输出使用已经验证留在 MEDIA 内的规范路径;这只是缓存文件的安全存放方式,不改变原运行时请求字符串。

649520 的共享缓存按原 wstring 请求比较,不能因为 Windows 文件系统不区分大小写,就把原缓存键也改成大小写不敏感。本批几何导出可以共享同一份原文件数据,但不代表这些请求已被合并成同一个原运行时模型实例。

局部范围计算:5606C6–5608E5

先从独立碰撞模型的几何 bounds 构造两个 float32 向量:

text
lo = f32(geometry.min + (-COLLISION_ADD, 0, -COLLISION_ADD))
hi = f32(geometry.max + ( COLLISION_ADD, 1,  COLLISION_ADD))

Y 不是对称扩张:minimum 加 0,maximum 加 1。

接下来按原顺序逐项更新 X/Z:

text
maxX = max(old maxX, -minX)
maxX = max(old maxX, -minZ)
maxX = max(old maxX,  maxZ)

minX = min(old minX, -maxX)
minX = min(old minX, -maxZ)
minX = min(old minX,  minZ)

maxZ = max(old maxZ, -minX)
maxZ = max(old maxZ, -minZ)
maxZ = max(old maxZ,  maxX)

minZ = min(old minZ, -maxX)
minZ = min(old minZ, -maxZ)
minZ = min(old minZ,  minX)

此处 max/min 是严格更新:候选严格更大/更小时才替换,否则保留旧值,包括相等时的 signed zero。每一行都使用此前行已经更新过的值。

正常有序几何和非负 COLLISION_ADD 会得到以原点为中心的对称 X/Z 范围;实现仍保留原顺序,不简写成一个 abs/max 公式。负 padding、倒置端点和零符号的参考也已纳入。

最后写入 单位的 CCullingBounds.local,随后原函数调用 511F00 刷新 world bounds。原共享碰撞网格的顶点及 bounds 没有被修改。

NativeCharacterCollision.Prepare 只生成明确的模型/local 输入;真实宿主设置自己的 culling cache 后再调用 NativeUnitBounds.Refresh,不在数据读取时猜测位置、朝向或活动位。

实际输入与接线

工具:

text
python -m tools.build_native_character_collision

生成:

  • build/f2/native_character_collision/definitions.json。
  • build/f2/native_character_collision/geometry/**.json。

每个定义保留:

  • 原 UNIT DAT 路径。
  • COLLISIONFILE、RESOURCEDIRECTORY、COLLISION_ADD 位值。
  • 原请求字符串。
  • 三个属性的来源文件或装载器缺省来源。

几何沿用严格 Ogre 解析、原面索引反转、材质 surface byte 和原法线生成,不经过显示 OBJ 或自动凸包。未就绪请求不能通过读入器悄悄变成默认 collider。

C# 的 F2CharacterCollision.LoadInputs 组合:

  1. 读取明确的 CCharacter 定义。
  2. 取得该请求的已校验原几何。
  3. 按指定原精度构造法线/扫掠输入。
  4. 计算局部 culling 范围,返回 NativeCharacterCollisionInputs。

回归使用 MEDIA/UNITS/PLAYERS/ARBITER/HUM_ARBITER_BASE.DAT。它请求默认 collider,以原局部范围和已实现 world bounds 刷新接入 NativeUnitCollisionBinding,再逐面执行 InitialObjectSweep。此测试明确给出活动位和姿态;不声称运行了完整角色创建或游戏 HUD。

基础 PAK 核查

默认资源 MEDIA/MODELS/COLLIDER.MESH 从 DATA.PAK 解压后的 SHA256 与提取文件完全相同:

5350715f247569327a269d99ad8384dba5c8511cbbac5751a7c7c8a5aaadf0a6

唯一未就绪请求:

text
media/levelsets/props/esthshrine_props/esthshrine_pool_01_collision_collision.mesh

来自 A1-WATERTEMPLESTATUE.DAT。已直接解压并反编译基础包内该 DAT,确认:

  • 包内 COLLISIONFILE 同样是 esthshrine_pool_01_collision_collision。
  • 反编译全文 SHA256 与本地提取 DAT 一致,不是本次提取拼错。
  • 基础 DATA.PAK.MAN 中没有这个双后缀路径。
  • 包中有单后缀 ESTHSHRINE_POOL_01_COLLISION.MESH,但本批没有自动替换。

这些证据限定于基础包与字面请求。它不证明所有可能的模组、覆盖包或后续资源回落行为。原运行时模型缺失后的完整生命周期仍需按原装载器处理。

核查产物为 build/native_character_collision_pak/result.json、statue.bindat、statue.dat;工具只读游戏文件,生成内容都位于 build 目录。

原生参考边界

native_character_local_oracle.c 在私有进程映射中执行原 5606C6–5608E5:

  • 输入提供已经读出的 COLLISION_ADD 和碰撞几何 bounds。
  • 使用原 Ogre 向量构造/加法/拷贝。
  • 执行全部顺序比较及单位 local bounds 写入。
  • 其后的 511F00 为计数边界,本函数已由前批单独原生对照。
  • 原共享几何和预填 world cache 保持不变,参考检查确实只写 local 并请求一次刷新。

未在此参考中执行完整 55E110 DAT/材质/动画装载,也没有用它证明共享模型缓存引用计数完成。

文件与验证

  • core_sim/NativeCharacterCollision.cs。
  • core_sim.io/F2CharacterCollision.cs。
  • core_sim.tests/NativeCharacterCollisionChecks.cs。
  • tools/build_native_character_collision.py。
  • tools/build_native_character_local_oracle.py、native_character_local_oracle.c。
  • tools/prove_character_collider_pak.py。
  • tests/test_native_character_collision.py。
  • build/native_character_local_oracle/results.json。
  • build/native_character_local_mismatches.json(空)。
  • build/native_character_collision_core.log、native_character_collision_python.log、native_character_collision_godot.log。

EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e

OgreMain.dll SHA256: 974c1dc77ea2818276cc7eb94a8b95b2ccf8595e28fa3f14b2cd6d516fdb67a1

继续推进

后续进展:共享碰撞模型缓存与引用生命周期已实现 acquire/release/零引用收集并接真实几何后端;完整 CCollisionModel 与实例资源替换/释放仍需继续。

下一个缺口是 649520/648D00 共享碰撞模型缓存的 acquire/release 与对象装载时序,以及装备的对应资源初始化、活动位生命周期和正式世界宿主。当前 F2 几何读取器不是原模型管理器,不用“加载了 JSON”代替生命周期完成。

已核实缓存按请求 wstring 查找,命中增加引用,648D00 找到模型条目后递减;完整缓存清理、回调及所有调用者尚未实现/验证。这些结论应复用,不再重新当作未知。

武器可构造覆盖仍为 1,053/1,419;正式攻击遮挡、攻击型 proc、动画、缺导出资产和全武器实机验收仍待完成。部分角色表的 UniverseComplete=false 未放宽。

按 godot-master 分层要求,装载输入与几何计算分离,宿主才拥有实例及状态。main 原地工作,未提交、未 attach 原游戏、未修改正式场景或 UI,快照 v29 未改。默认存档 SHA256 保持: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f