原版局部方向列、四元数转换与节点姿态设置
本批结果
已把给定局部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 | 对象实现 | 存储位置 |
|---|---|---|---|
| FORWARD | 4B42C0 | 465430 → 465460 | Matrix4列2;对象+176 |
| RIGHT | 4B4320 | 465540 → 465570 | Matrix4列0;对象+164 |
| UP | 4B4380 | 465660 → 465690 | Matrix4列1;对象+152 |
| POSITION | 4B4240 | 与61DDF0相同存储路径 | 对象+112;Node局部位置 |
| SCALE分量 | 经各分量setter | 464EA0 | 对象+140;Node局部缩放 |
注意CRoomPiece有多个接口vtable。主对象使用构造器623D70在自身+0写入的217C48C表,而217C464是iCollision接口表。不能按同名符号拿第一个表就认定虚调用偏移。
方向setter每次只写自己的列,不归一化轴,也不从另外两列推导第三列。随后4651A0:
- 保留原Matrix4内容。
- 465050提取Matrix3。
- Ogre Quaternion(Matrix3)进行FromRotationMatrix转换。
- Node::setOrientation复制四元数并调用Quaternion::normalise。
- 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位进程实际执行:
描述符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。