原版局部方向列、四元数转换与节点姿态设置

本批结果

已把给定局部RIGHT/UP/FORWARD列、POSITION和SCALE转换成原版节点姿态的数值链补入核心:

  • 1745组输入×PC24/53/64=5235次原生对照,姿态输出逐位一致
  • 覆盖三条方向属性的全部存在组合和六种设置顺序、非单位/非正交/全零矩阵。
  • 包含ECHO PASS、ACT1 TOWN及GLAIVELEAP源LAYOUT中的257组实际方向输入。
  • 原对象保存的矩阵列、位置和缩放也核对,确认没有先做轴归一化或自动补UP。
  • 1202条核心断言、129项相关Python检查通过;Godot C#构建零warning/error,coresimlint通过。

现有渲染脚本不变。本批是精确核心输入构建,不是完整BINLAYOUT加载器或整场景验收;全武器构造覆盖仍1053/1419。

既有实现与新增工作的区别

parse_layout.py的ogre_orientation_basis已经记录了正确的语义方向:原版三列分别设置,再把矩阵转成四元数并归一化。它使用Python普通浮点数学,并包含渲染用途的其他分支/回退。

本批没有重复替换那套已存在的方向规则;新增core_sim/NativeLocalPose.cs,使用X87Value保留原操作顺序、PC精度和f32写回时机,并以真实原版字段设置链独立验证。

原版字段设置路径

CPositionableObjectDescriptor构造器4B4430注册:

属性描述符setter对象实现存储位置
FORWARD4B42C0465430 → 465460Matrix4列2;对象+176
RIGHT4B4320465540 → 465570Matrix4列0;对象+164
UP4B4380465660 → 465690Matrix4列1;对象+152
POSITION4B4240与61DDF0相同存储路径对象+112;Node局部位置
SCALE分量经各分量setter464EA0对象+140;Node局部缩放

注意CRoomPiece有多个接口vtable。主对象使用构造器623D70在自身+0写入的217C48C表,而217C464是iCollision接口表。不能按同名符号拿第一个表就认定虚调用偏移。

方向setter每次只写自己的列,不归一化轴,也不从另外两列推导第三列。随后4651A0:

  1. 保留原Matrix4内容。
  2. 465050提取Matrix3。
  3. Ogre Quaternion(Matrix3)进行FromRotationMatrix转换。
  4. Node::setOrientation复制四元数并调用Quaternion::normalise。
  5. 465700刷新对象的方向列缓存,仍取自原Matrix4,不以归一化后的节点四元数重写这些列。

这解释了为何六种方向设置顺序在测试域内得到相同最终姿态:每次更新节点四元数,但输入矩阵仍保留各列原值。

默认值与不可替换规则

61DBB0的原始指令确认:

  • 初始位置为(0,0,0)。
  • 自身缩放为(1,1,1)。
  • 原方向矩阵为IDENTITY。
  • 缺少某条方向属性时,该列保留IDENTITY的对应列。

因此缺UP时保留(0,1,0),不能自动采用FORWARD×RIGHT。 GLAIVELEAP/SLICE.LAYOUT中的显式UP与叉乘结果符号相反,已纳入样本。

不能先把三轴归一化。矩阵转四元数公式里有裸常数1;例如放大两倍的90°偏航基会得到不同于单位基的旋转角。该差异是原版行为,不是移植时应“修正”的数据。

全零方向矩阵也不是单位旋转回退条件:原版走最大对角项分支,得到归一化四元数(0,1,0,0),即绕X轴180°。回归明确保留这个结果。

位置与缩放是独立输入。零/负缩放原样保留,不采用现有渲染管线的毫米级零缩放替代;碰撞可逆性由下游对应入口负责。

精确数值顺序

原Ogre函数:

  • FromRotationMatrix:1014CF70。
  • Quaternion::normalise:1014DB40。
  • Node::setOrientation:10113050。

FromRotationMatrix的trace按(m11+m00)+m22计算,在x87中保留后比较0。不能先存成float再选分支。

正trace分支保留sqrt与0.5/root的寄存器精度,四元数各分量最后存float。 非正trace分支严格选择最大对角项;相等时保持原优先顺序,不改变符号约定。

Node归一化读取已存float的四元数,按原平方和与倒平方根顺序计算,不添加向量epsilon或任意norm阈值。 1224组测试输入在不同PC精度下有不同节点姿态输出;本接口仍要求显式精度。

原生oracle的范围

tools/native_local_pose_oracle.c在独立32位进程实际执行:

text
描述符POSITION/FORWARD/RIGHT/UP setter
→ 对象列设置与4651A0
→ 原Ogre Matrix3/Quaternion转换
→ 原Node::setOrientation/setPosition/setScale

同时调用原464EA0缩放设置路径。

测试vtable只替代节点脏标记通知及Room Piece外围回调,不替代矩阵或四元数数学。观察到通知次数为2+已设置方向列数。 这不验证运行时碰撞单元移动、所有附属物件通知或整个Room Piece初始化生命周期。

最初探针因未绑定Vector3三浮点构造入口退出;补全原导入绑定后重新生成全部结果,没有使用失败前的旧输出放行。

身份:

  • EXE:186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e
  • Ogre:974c1dc77ea2818276cc7eb94a8b95b2ccf8595e28fa3f14b2cd6d516fdb67a1

源LAYOUT向量是明确的测试输入。这里没有据此证明其文本小数与实际BINLAYOUT中的所有浮点位模式、加载顺序完全一致。

加载器接线的新定位

已重新核对原50B680/50BA10/50B220:

  • 50B680递归加载对象,调用50B220设置属性。
  • 50B220先应用描述符默认记录,再按二进制属性序列派发setter。
  • 顶层50BA10先处理随机组池,再加载对象树,最后统一初始化对象。

还有一条旧笔记需要纠正:676440不是“压入带种子上下文”,其原指令只是读取完整同步RNG状态。池选择后676970将该状态写回;若传入全零状态,676970会改用GetTickCount。 本批只确认这一边界,尚未把完整加载RNG流程接入。不能拿旧笔记中的伪参数猜测作为种子来源。

YAW有独立setter4B9820 → 61DEC0,会基于已有姿态移除旧偏航再施加目标偏航;本批方向列构建器未包含这条角度分支。 ROTATION RATE setter4B9870只存向量,不能仅凭属性描述“不可烘焙”就假定这个setter会清除BAKE;其后续运行时消费仍需跟随。 统一SCALE是编辑便利项,描述中明确不保存;实际XYZ分量和相关属性标志需要在二进制装载阶段核对。

产物与下一步

后续:BINLAYOUT有序数据入口已补全量v11框架、属性解码与10图空间投影。默认属性、依赖回调、对象创建与初始化仍需继续。

  • core_sim/NativeLocalPose.cs:方向列到节点四元数、局部姿态构建。
  • build/native_local_pose_oracle/results.json:5235次对照。
  • build/native_local_pose_core.log:1202断言。
  • build/native_local_pose_python.log:129检查。

下一步从50B220继续接二进制属性序列、YAW等其他姿态入口、父节点关联和初始化顺序;再接完整同步RNG边界及空间分区。289个场景变体尚未冒充已解析,全武器目标未完成。

按godot-master的数据/表现分层,本批未改画面或默认存档;main原地工作、未提交、未attach原游戏。默认存档hash保持: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f