Files
mtgodot-poc/docs/RENDERING-REPAIR-PLAN.md

91 KiB
Raw Permalink Blame History

新客户端渲染修复实施路线

更新日期:2026-09-09。目标客户端:本仓库 metin2-client,第一交付平台为 macOS。

最新集中交付见文末「集中修复、整套回归与 mac 实包验收」:已生成新 universal mac 包,独立资源加载与只读真实登录通过;剩余视觉保真和性能项未宣称完成。前文表格及旧执行记录保留历史状态,以最新记录为准。

本文是实施规格,2026-09-08 已开始第一批 R0/R1 修改;阶段结果见文末执行记录。未标明通过的工作仍待执行,不代表整体渲染或最终包完成。

1. 目标、依据与当前状态

第一交付目标:实际 macOS 客户端进入游戏后显示正确地图和真实人物,身体/发型/装备组合正常,人物在地面上,底部技能栏可用。之后再精细还原 40250 的材质、环境、树木、特效和性能。

画面真值优先使用用户当前 40250 Windows 客户端的同资源、同场景截图。EnriqueGF 的场景截图是可行性和场景完整性参考,不能直接当作 40250 像素真值;其主角背对镜头,也不能证明正面发型和所有动画正确。原始骨骼数值以固定版本 Granny oracle 的可复现实测为参照。

当前问题/证据 状态 实施要求
用户反馈进入游戏出现胶囊、背景异常 待真实客户端复验 检查地图、实体、相机与加载时序,不直接归因于模型格式
game_scene.gd 已加入 _sync_main_character()_main_model_attempted 工作区有补丁,尚未完成最终画面验收 检查晚到实体、失败重试、角色切换和重连的生命周期
本地 world_demo.gd 曾加载真实 A1 地图及模型 历史组件证据 只能证明这组资源链路可运行,不能证明联机 .app 正常
发型挡脸、人物悬空 用户反馈,根因仍需隔离 分别验证变换、蒙皮、深度、地形高度与动画接触点
底部技能未显示 用户反馈,已有 quickbar.gd 检查创建、布局、图标、服务端槽位恢复,不重新发明快捷栏
日志曾出现 unknown GC header 59 (last: 32,148) 历史协议阻塞,是否仍存在待复验 若重现,单列协议问题;不得跳过未知包继续解析来伪造联机通过
选角 flip_winding、材质及视觉测试已有未提交修改 实验状态 保留现有改动,记录基线,再逐项验证,不全局翻转或覆盖

历史 README / MIDREVIEW 的阶段完成描述不代表当前最终包通过。现有截图、日志在使用前检查是否存在、属于哪个构建和资源版本。

2. 两个上游项目的使用边界

本路线依据已经检查的固定提交;上游后续变化需要重新审查:

  • EnriqueGF/metin2-godot078d39a41a3ef17c9e09fb9807956cb1a2f16641
  • kcanakdag/metin2-godotime6a82d793c1d83e34f3e71200fdfc67a78bcb96d3
参考实现 本项目的应用方式 不应推导的结论
EnriqueGFtools/compare_visual_golden.py 引入区域、遮罩、误差阈值和差异图方法 截图存在或整体平均误差低不代表脸部正确
EnriqueGFtools/build_character_glb.pygodot/client/online/presentation/warrior_actor.gd 同一角色离线 GLB 对照;核对身体/发型/武器的组合和动作同步 转换包含 TRS 分解、舍弃 shear;不能假定动画无损
EnriqueGFgodot/client/online/presentation/avatar_registry.gd 借鉴有效实体数据驱动创建、切地图清理、重复数据处理 其快照协议不能直接替代我们的原版协议事件
EnriqueGFtools/prepare_legacy_ui_assets.pygodot/client/online/ui/ 核对 .sub 裁剪、原版布局和按钮状态 西班牙语素材、字体、窗口尺寸不能直接替代当前中文配置
EnriqueGFtools/audit_city_asset_dependencies.py 地图资源依赖追踪、缺失分类和报告 全部依赖可解析不等于全部已经正确渲染
godotimetools/export_playable.py 实际包资源清单、哈希与加载审计,适配 macOS Web/Linux 通过不代表 macOS 通过
godotimecontent/profiles/p0-warrior-dog.jsonclient/scripts/content/ 模型、动作、附件和依赖的统一验证清单 三个精选模型通过不代表四职业和全量换装通过
godotimetools/test_browser_accounts.py 借鉴双客户端、重连、换装及账户生命周期用例 不迁移其 SpacetimeDB 服务端来修复渲染

参考源码:

直接移植 EnriqueGF 原创代码时保留 MIT 声明和提交来源。godotime 在本次检查的提交没有项目级许可证,先借鉴方法;直接复制实现前明确授权。原始游戏资源的来源与使用范围沿用本仓库 THIRD-PARTY.md,不把上游许可证视为原版资产授权。

3. 技术路线与顺序

执行顺序:R0 基线 → R1 场景装配 → R2 模型/发型 → R3 材质/环境 → R4 UI → R5 最终包验收 → R6 性能与内容扩展。R4 可在 R1 后独立推进;R5 的资源清单工具可提前建设,最终验收必须等待 R1–R4 完成。

  1. 保留原版网络协议与当前 GR2 主链路,以最小改动修复实际缺陷。
  2. 引入独立 GLB 对照场景,不先全量转换资源或更换运行时。
  3. 对照决定具体资源采用哪条路径;结果需要记录,不能预设 GR2 或 GLB 必然更好。
  4. 角色与静态物件分别决策。静态物件可先试离线 Godot 资源缓存;人物保留完整仿射矩阵的需求单独评估。
  5. 每项修复必须在真实 GameScene 和导出包中复验。单模型预览只能作为中间证据。

4. R0:建立可重复基线

涉及现有文件:test/compare.pyproject/char_select_visual_test.gdproject/world_demo.gdproject/game_camera.gd

具体步骤

  1. 记录当前 Git 提交、未提交补丁摘要、Godot/导出模板版本、扩展库哈希、macOS/GPU、渲染后端、窗口和实际渲染分辨率。不能仅凭提交号标识脏工作区构建。
  2. 固定资产来源及哈希:身体、发型、武器、护甲、MSM/MSA、动画 GR2、纹理、地图和环境。两个上游与我们可能使用不同资源版本,必须先核对。
  3. 为每个场景保存地图名、服务器坐标、地图基点、转换后本地坐标、相机位置/目标/FOV、角色朝向、装备、动作及取样时间。
  4. 建立三种独立场景:无网络的单角色实验台;真实资产的 GameScene + FakeClient;实际服务器上的导出 .app
  5. 保存正面、侧面、背面和脚底近景,以及选角界面和游戏全景。待机/跑步/攻击每段至少取开始、中间、结束,过渡动作另测。
  6. 在现有截图工具基础上增加局部区域和差异图。脸、头发、脚底、技能栏不允许被动态遮罩排除;时间文字、随机粒子等可单列遮罩。
  7. 区分两类比较:同引擎修复前后用于自动回归;40250 对照用于保真评估。没有匹配的相机和动作时,只能做人工结构检查,不执行严格像素判定。

拟新增产物(尚未实现):test/rendering/cases.jsontest/rendering/README.mdtools/rendering/compare_regions.py。本地日志/截图/报告建议放 build/rendering/<run-id>/,不覆盖失败证据。

阈值:先在不改代码的条件下重复采样三次,测量阴影/抗锯齿噪声,再设置每区域阈值。报告包含 MAE、RMSE、变化像素比例和遮罩范围;不能为了让错误图片通过而放宽阈值。

完成条件:每条用例可由配置重复启动,并能将截图关联到确定的代码、资源、相机和构建;缺参考图的用例标为待验证。

5. R1:修复主角装配、地图定位与相机

涉及现有文件:project/game_scene.gdproject/net_world.gdproject/net/mapcoord.gdproject/ui/player_view.gdproject/game_camera.gd。必要时追踪 extension/src/net/m2_client.cpp 的事件顺序及 extension/src/metin2_world.cpp 的地图数据。

具体步骤

  1. 增加定点诊断:阶段进入、地图就绪、主角 VID 获得、实体行有效、模型创建、贴地定位、相机开始跟随。每条带角色/场景生命周期编号,避免把上一次登录数据误认为当前数据。
  2. 复核现有 _sync_main_character():明确“等待数据、加载中、成功、失败”的状态;缺 race 等必要数据时保持等待,重复事件不创建重复人物。
  3. 检查 _main_model_attempted 是否在失败后永久阻止恢复。数据或资源就绪条件变化时允许受控重试;永久资源错误显示明确失败原因,不能每帧重复加载或刷日志。
  4. 切角色、地图切换、重连时清理旧模型/相机目标/信号并重置对应状态。异步完成结果必须核对是否仍属于当前角色与场景。
  5. 统一通过 MapCoord 转换:服务器全局厘米 → 地图本地米。现有地图本地 Z 约定与上游不同,禁止直接复制对方轴翻转或地图中心公式。
  6. 验证坐标往返、合法边界及高度查询位置。(0,0) 可是合法地图基点,地图是否就绪应有明确状态,不能仅靠非零基点推断。
  7. 在地图可查询、角色位置有效后计算地面高度并绑定相机;模型就绪后移除胶囊。资源等待期间提供加载状态,不能把永久占位当正常游戏画面。

必须补充的回归

  • 主角 VID 在 setup 前已有、setup 后出现、实体行再晚一帧、多帧后出现。
  • 不发 entity_main_set,只更新实体存储,最终仍创建主角。
  • 重复主角事件只产生一个模型;缺 race 时不使用错误职业创建人物。
  • 加载失败有可见原因;条件恢复后可受控重试;旧加载不会覆盖新角色。
  • A1/C1 各一个合法出生点;基点为零的合成坐标用例;地图外坐标被明确诊断。
  • 切角色、重连、切地图后相机跟随当前人物,旧节点与旧状态不残留。

测试落点:扩展 project/gamescene_test.gdproject/app_flow_lifecycle_test.gd;新增真实资源的 GameScene 视觉用例。FakeClient 的字段/信号需匹配实际接口。

完成条件:自动回归通过;真实 GameScene 截图同时出现正确地表与真实主角。协议若中断,保留本地通过结果,但将实际联机验收标为未通过。

6. R2:修复身体、发型、武器与贴地

涉及现有文件:extension/src/metin2_model.cppextension/src/metin2_anim.cppextension/src/gr2_bridge.cppproject/ui/player_view.gdproject/ui/equip_model.gdproject/ui/race_spec.gdproject/ui/char_select_screen.gd

同资源对照与定位顺序

  1. 先选用户出错角色的确切身体/发型/装备;再建立男战士最小对照。GLB 转换必须使用同一组源文件,记录转换器版本与参数。生成文件留在隔离输出目录。
  2. 在统一实验台逐步加载:身体静态绑定姿势 → 身体动画 → 加发型 → 加武器 → 换护甲。每一步保存矩阵、包围盒、贴图绑定和截图。
  3. 检查模型初始 placement、单位缩放、轴转换、父子节点变换是否重复应用。分别核查三角形绕序和负缩放,不能以全局 flip_winding 代替定位。
  4. 对身体和发型按骨名核对 mesh binding、骨架索引、inverse bind、当前 world/skin 矩阵。验证发型是否错误重复乘身体变换,是否使用了另一套不兼容的绑定姿势。
  5. 检查附件共享的动画时间与过渡状态。身体切动作/换装时,发型不能停留在旧姿势。武器验证手部附件空间,避免在 model-local 与 world-space 之间重复转换。
  6. 先用不受光照影响的诊断材质、线框或深度视图判断几何是否错误;几何正确而发型仍盖脸时进入 R3 检查深度和混合。
  7. 数值正确后比较 GR2 CPU、GR2 GPU 与 GLB 的同时间画面。对关键骨骼矩阵、选定顶点和完整动画周期采样,保留已有 oracle 精度要求,不只检查 NaN。

贴地专项

  1. 分开记录实体基准高度、地形采样高度、模型局部原点和脚部接触点。
  2. 平地待机时用足底接触点判断高度;行走/攻击存在抬脚,不能用动画每帧 AABB 最低点强行贴地,否则人物会抖动。
  3. 测试坡地、台阶或可行走建筑表面,以及地图边界。确认所采样的是人物实际站立表面。
  4. 阴影检查与几何检查分开:阴影偏差可造成悬空观感,但不能用加深阴影掩盖高度错误。
  5. 建议初始平地接触误差上限为 2 cm;这是待以资源与 40250 校准的项目目标,不是原版已证实精度。腾空动作不使用该判据。

路线决策

实测结果 后续动作
GR2 数值正确,仅材质/装配错误 修复现有渲染与组合,不迁移动画
GLB 在选定资源上可靠,GR2 缺陷短期难解决 为该资源提供可回退的 GLB 适配,保持相同的角色表现接口
GLB 丢 shear 或过渡不一致 不将该角色纳入迁移范围;保留失败样本
静态 GLB/原生资源缓存画面一致且加载更快 单独推进静态物件缓存,与角色动画路线解耦

GLB 扩大范围前必须覆盖四职业、实际使用的性别/护甲/发型变体,以及待机、走、跑、攻击、受击、死亡和选角动作。缺少资产时明确未覆盖,不以一个战士替代全体结论。

完成条件:出错角色的正面/侧面/背面及关键动作通过;换装、重新选角后仍正确;记录 GR2/GLB 选择依据。清理临时强制绕序、调试打印和 /tmp 截图导出时只删除本轮明确归属的诊断代码。

7. R3:材质、地形与环境校准

涉及现有文件:extension/src/m2_material.cppextension/src/static_object.cppextension/src/terrain_splat.cppextension/src/environment_builder.cppextension/src/metin2_world.cppproject/ui/char_shadow.gd

具体步骤

  1. 逐 surface 输出源 mesh、tri-group、材质槽、最终纹理和渲染状态。确认 mesh-local 材质索引没有误当成全局索引,贴图回退有明确诊断。
  2. 身体、脸等不透明面验证深度写入;发丝按资产语义选择 alpha-test;真正半透明面单独混合。不要把所有头发强制成同一模式,也不要对所有表面关闭背面剔除。
  3. 同贴图下比较标准材质和自定义 shader,逐项打开光照、顶点色、透明、雾。检查 DDS 解码、颜色空间、alpha、mipmap 和双重 gamma。
  4. 身体、头发、武器分开检查投影与受影。正面、侧面、逆光及脸部重叠处验证深度;根据场景尺寸校准 shadow bias。
  5. 地形逐层验证 tile/alpha splat 对应、UV 比例、接缝和法线。固定至少一处道路/草地边界作为回归,防止纹理层顺序交换。
  6. 列出地图物件:应有、找到资源、解析成功、实例化、可见、失败原因。资源 CRC 可解析不等于模型已画出,树木占位必须单独计数。
  7. 基于原 .msenv 对照太阳方向、角色/背景光、环境光、雾距离与天空;逐项记录 Godot 近似规则。EnriqueGF 的固定 filmic/雾参数只作实验参照。
  8. 完成角色与场景单项校准后再组合全景,避免同时改曝光、贴图与相机导致无法归因。

完成条件:脸/身体不误透明,发丝边缘和遮挡正确;道路与草地层正确;主要建筑不缺材质;角色高度与阴影分别通过;每项与 40250 的剩余差异有说明。

8. R4:选角界面与技能栏

涉及现有文件:project/ui/char_select_screen.gdproject/ui/ui_assets.gdproject/ui/quickbar.gdproject/game_scene.gd

具体步骤

  1. 对照当前中文 40250 的 uiscript.sub、字体及图集,记录选角背景、角色相机、按钮、任务栏、快捷槽的来源与尺寸。
  2. 检查 .sub 裁剪边界、大小写路径、透明通道与纹理加载失败;输出 UI 依赖清单。
  3. 从 GameScene 开始检查快捷栏是否创建、挂在正确 CanvasLayer、可见、位于窗口内;核对缩放、锚点、裁剪和其他窗口遮挡。
  4. 跟踪 quickslots_changedrestore_from_server 的调用时序:初始槽位、晚到数据、重新登录均应恢复。保留现有服务端 36 槽与本地 4 页 × 8 格映射。
  5. 将空槽、未学习技能、图标缺失、技能不可用、冷却中区分呈现。不得通过凭空填充技能图标让截图看似正常。
  6. 使用确实已学习并已配置的技能验证图标、拖放、翻页、数字键/F 键和冷却;UI 对比先用 FakeClient,真实施放在适用测试环境下执行。
  7. 验证背包/聊天打开关闭后输入焦点,避免空格攻击误触 UI 按钮。
  8. 按 1280×720、1920×1080 和实际 Retina 窗口验证逻辑尺寸/物理像素关系;中文字体宽度单独检查。

完成条件:选角画面达到已记录的参考布局;底部栏始终可见,槽位状态真实;切角色/重连能恢复;技能冷却和输入行为保持正确。

9. R5:最终 macOS 包与验收证据

涉及现有文件:build-macos-client.shscript/live_smoke_test.shproject/live_smoke_test.gdproject/asset_pack.gdproject/bake_asset_index.gd

具体步骤

  1. 扩展构建清单,记录代码版本及脏补丁摘要、构建参数、扩展/模板版本、资源及实际产物哈希。
  2. 对实际 .app 检查身体、发型、装备、动作、地图、UI 图标依赖是否存在且可加载。清单生成后仍要启动包验证,不能用工作区资源替代包内检查。
  3. 从不依赖开发者资源路径的目录启动最终包;验证资源解析不会偷偷回退到源码 checkout。用受控资源根测试覆盖此情况。
  4. 执行只读登录冒烟,再运行有画面和输入的 GameScene 验收。现有脚本不是完整视觉测试;需新增或扩展包内测试场景并保存截图。
  5. 移动、攻击、换装等会改变服务端状态的用例使用适用测试角色/服务器;不要把现有冒烟默认包含的聊天发送当作渲染验收必需动作。
  6. 两客户端用例验证角色互见、移动朝向、武器/护甲变化、断开移除及重连重建;同时检查本地与远端外观。
  7. 排除凭据、私有日志、源码资产归档和临时探针出现在普通交付包中。若通过带探针的包定位问题,移除探针后还要对最终普通包重新冒烟。
  8. 将通过记录绑定到交付 .app 的确切构建。此后任何影响画面的代码、资源、导出设置变化,都使相关验收失效,需要重测受影响范围。

现有命令与覆盖边界

以下命令在仓库根目录运行。执行前检查本机 Godot、扩展和资源配置;这些是已存在的入口,本文没有执行它们或宣称通过。

godot --headless --path project --script gamescene_test.gd
godot --headless --path project --script equip_model_test.gd
godot --headless --path project --script environment_test.gd
godot --path project --script char_select_visual_test.gd
./build-macos-client.sh release
./script/live_smoke_test.sh --no-build --no-mutations --no-reconnect
  • Headless 测试仅证明其断言覆盖的逻辑,不能证明实际 GPU 画面正确。
  • 当前选角视觉脚本有实验性诊断,使用前复核其输出和退出行为;它不是完整 40250 比较器。
  • --no-mutations 不覆盖移动、聊天、重连、换装和攻击;此处同时给出 --no-reconnect 以明确首轮只读范围。
  • 当前脚本包含特定服务器地址与端口,运行前检查是否仍是目标环境;凭据只通过既有本地配置/环境提供,不写进报告。
  • 现有产物路径为 build/export/mtgodot-poc.app;日志在 build/live-smoke/。保留每次结果时复制到独立 run-id 归档,避免脚本固定路径覆盖历史。

第一版交付检查表

  • 首次进入、切角色、重连后出现真实主角,地图和相机正确。
  • 用户出错角色的身体/发型/装备在正面及动画中正常。
  • 平地和选定坡地测试通过,脚底与阴影分别正确。
  • 底部任务栏可见,已配置技能的图标、输入、恢复、冷却正确。
  • 材质与地图关键区域通过固定场景比较,差异图有人审阅。
  • 最终普通 .app 独立启动,关键资源无开发目录回退。
  • 协议错误未导致用例提前终止;未完成的联机项明确标注。
  • 日志、截图、报告和包哈希对应同一次构建。

上述为本次内部 macOS 修复版交付标准,不代表全游戏、所有地图或所有平台均已验收。

10. R6:性能、缓存与内容扩展

在 R1–R5 通过后执行,不以优化替代正确性修复。

  1. 固定场景采集 1/8/20/50 个角色的 CPU/GPU 帧时间、p95/p99、内存、冷加载与重复加载耗时,明确前台/后台和帧率限制。
  2. 量化当前 CPU 蒙皮逐帧构建网格的成本;GPU 路径先通过数值与画面回归,再扩大启用范围。
  3. 选择一组代表性静态建筑试缓存,缓存键至少包括源资源、转换器版本、坐标和材质选项;改变任一输入必须失效重建。
  4. 测量缓存对冷加载、包体和内存的影响,存在收益再扩大导入规模。保留原始资源来源和可重建流程。
  5. 树木、效果、LOD 和其他地图分别建立缺失清单与回归场景。占位替代物不能计入原版模型已完成数量。
  6. 最后扩展 Android/iOS 独立验证;macOS 的画面和性能结果不能直接外推。

11. 任务拆分与执行记录

每项宜形成一个可独立复查的补丁;无需先完成所有工具才修复已定位的问题。已有用户改动应在相邻上下文中保留,不因路线实施而重置。

任务 ID 交付物 前置条件 当前状态
R0-01 固定资源/场景基线与区域截图配置 可用参考资源 已有 A1/C1 实景入口和部分哈希;区域差异待补
R1-01 主角晚到、重复事件、重连回归与修复 R0 最小基线 晚到/重入/重试回归通过;真实重连待验
R1-02 地图坐标、高度、相机就绪检查 R1-01 A1/C1 晚到定位实景通过;坡地/边界待验
R2-01 同战士 GR2/GLB 实验台及对照报告 R0 实验台与悬空修复已完成并有数值断言;GLB 对照仍待执行
R2-02 发型/身体/附件修复及路线决定 R2-01 发型材质根因已修复并有八职业回归;导出包复验待做
R3-01 深度/材质/环境与地形校准 R1、R2 几何稳定 材质已有改动,待验证
R4-01 选角、技能栏装配与数据恢复 R1 待执行
R5-01 包资源清单、最终 .app 冒烟与画面报告 R1R4 待执行
R6-01 性能基线与静态资源缓存试验 R5 后续

每项完成记录必须包含:问题复现、确认根因、修改文件、源资源哈希、实际执行命令、断言/截图结果、包或库哈希、未覆盖范围。失败时记录下一步所需证据;不要把“方案合理”“截图已生成”更新成通过。

与现有文档的关系:本路线聚焦用户当前渲染故障和首个 macOS 修复包;SHINSOO-WORLD-RENDERING.md 提供世界渲染背景,PARITY-GAP.mdBACKLOG.md 保留更广范围。后续只在实测完成时同步这些文档的对应状态。

2026-09-08 第一批实施记录

  • 修改 game_scene.gd:setup 完成相关接线后才开始兜底;加载期间阻止网络事件重入;按 VID/race/资源根区分模型;失败间隔 1 秒最多尝试 3 次;丢弃构建期间失效的主角结果;避免重复动画订阅和每帧重置人物位置。
  • 发现地图路径原来只在 setup 时按已收到的主角位置选择。新增首次有效主角位置到达后的地图重选,复用已有地图重载和相机/移动同步基点定位函数。
  • 修改 ui/player_view.gd:拒绝非法 race、缺失身体文件及解析失败的源文件。模型节点在入树时才实际加载,因此使用源文件预检,不能把入树前为空的 get_info() 当成加载失败。
  • 新增 main_character_sync_test.gd:测试失败重试、晚到/无信号实体、重入、重复事件、移动不回滚、过期构建等,已执行得到 failures=0。注入失败用例的 warning 属预期。
  • gamescene_test.gdequip_model_test.gd 已通过;前者故意使用缺失地图,其预期加载错误不能当成真实地图测试通过。
  • 新增 gamescene_visual_test.gdApple M4 / Metal Forward+ / Godot 4.7.1 上已执行 A1/C1A1 加载 20 区块/601 物件,C1 加载 20 区块/565 物件,身体加发型共 4 个表面,实体定位断言通过。两者均使用 FakeClient,未连接实际服务器。
  • 截图人工观察:地图和真实人物出现;UI 仍有布局/风格问题,不能标为 R3/R4 完成。选角截图仍复现发型遮脸;单独翻转 winding 的试验未解决,未保留新的绕序修改。
  • 用例命令、输出位置和验证边界见 test/rendering/README.md。早期失败截图保留在 build/rendering/;后续脚本变化应重新执行,不复用旧报告作为新代码证据。
  • 尚未完成:GLB 对照、发型根因修复、完整视觉基线与区域阈值、真实服务器重连、最终 macOS 包验收、R6 优化。下一优先项是发型的身体/附件变换与深度隔离。

2026-09-08 第二批实施记录(R2-01 悬空 / R2-02 发型)

R2-01 人物整体悬空

  • 复现:gamescene_visual_test.gdA1、C1)与 char_bench_test.gd 的 feet 机位,角色脚底离地约一个骨盆高;tools 下的 foot_probe 预测偏差 0.146 cm 量级。
  • 确认根因:渲染桥把动画世界姿势按 Granny GrannyBuildWorldPoseOffset4x4 又乘了一次模型导出期的 InitialPlacement,而 Metin2 的动画 .gr2 根骨轨道本身已含骨盆高度,等于重复计一次(实测 IP.z:武士 99.443、刺客 92.589、修罗 103.674、萨满 95.473,单位 cm)。
  • 修改文件:libgr2/include/gr2/gr2.hlibgr2/src/gr2_skeleton.cppsample_pose 增加可选 root_offset,默认仍取 sk.initial_placementbind_pose 行为不变,oracle 数值一致性未放宽);extension/src/metin2_anim.cpp(渲染路径显式传单位阵)。
  • 断言:gamescene_visual_test.gd 新增 feet_gap(阈值 0.03 m)。A1、C1 实测均为 0.001373 mpassed: true
  • 未覆盖:只验证了 wait @0.5 s 与平地。坡地、跳跃/受击等位移较大的动作、以及 NPC/怪物模型未逐一复验。

R2-02 发型发白、看起来「挡脸」

  • 复现:char_bench_test.gdface_side 近景,额发和鬓角出现成片浅奶油色三角面。
  • 排除项(都不是原因):几何与挂接(发型权重 100% 在 Bip01 Head,骨骼按名重映射 74/74,蒙皮包围盒落在头部 z 149–178,bind_pose_self_check = 7.6e-05);贴图(hair/hair_1_1.dds 就是 gr2 材质里写的那张,256×256 DXT1,最亮纹素 RGB(90,81,90)0 个透明纹素,所以 alpha scissor 是空操作,dxt.cpp 无关);UV(全在 [0,1])。
  • 材质二分(同一机位逐项改一个开关):TRANSPARENCY_DISABLED 仍有、CULL_BACK 更糟、metallic_specular = 0 仍有、roughness = 0 干净SHADING_MODE_UNSHADED 干净
  • 确认根因:Metin2Model::_load_hair() 是全角色唯一没走 mtgodot::make_material 的表面,用的是裸 StandardMaterial3Droughness = 1.0,浅色斑是 Godot PBR 的高光响应,与身体/武器用的前向材质(shader 带 specular_disabled)光照模型不一致。
  • 修改文件:extension/src/metin2_model.cpp_load_hair() —— 改用 mtgodot::MaterialDesc{blend = AlphaTest, alpha_scissor = 0.5, two_sided = true, spec_power = 0}make_material;无贴图时用 modulate 保留原来的深棕兜底色。GPU 蒙皮路径本来就已经走 make_material,此改动使 CPU 路径与之一致。
  • 资源哈希(sha256):PC/ymir work/pc/warrior/warrior_novice.gr2 7ac881b0c60ced40c9388d6186bdf02a353a58382d12804345cb65fc3dc1fa74PC/ymir work/pc/warrior/hair/hair_1_1.gr2 2e3c00bda5397d579a2399cfb38a69974667e4f7b92dca6b2a709058a37d0a84PC/ymir work/pc/warrior/general/wait.msa 7bc14bc29a63a78708b9a76618aee9229286f255e725dd669c60ef838cec6a15。其余七个职业的身体/发型哈希记在各次 report.json 里。
  • 执行命令与结果:
./build.sh Debug
godot --headless --path project --script main_character_sync_test.gd   # failures=0
godot --headless --path project --script gamescene_test.gd             # PASS
godot --headless --path project --script equip_model_test.gd           # PASS
godot --path project --script char_select_visual_test.gd               # PASS
godot --path project --script gamescene_visual_test.gd                 # passed:true, feet_gap 0.001373
godot --path project --script gamescene_visual_test.gd -- --c1         # passed:true, feet_gap 0.001373
godot --path project --script char_bench_test.gd -- --race {0,1,2,3}   # 四职业近景人工复核,浅色斑消失
godot --path project --script model_render_test.gd                     # PASS(八职业)
  • 未覆盖:只验证了基础 *_novice 身体加 hair_1_1。5th 护甲等换装组合、hair_skin 覆写路径、GPU 蒙皮路径的实机画面未逐一复验;导出包内复验尚未完成(见下)。

新增:不启动游戏的模型渲染自动回归

  • 新增 project/model_render_test.gd 与基线 test/rendering/model_baseline.json。离线装配单角色(无地图、无网络、无服务器),固定灯光与机位,对像素算指标并断言,退出码即结论。
  • 指标与阈值、为什么用平均亮度而不是「炸白像素占比」、以及覆盖边界,见 test/rendering/README.md
  • 有效性已验证:把 _load_hair() 临时改回 StandardMaterial3D(roughness = 1) 重新编译后,八个职业全部失败(hair_mean_lum 偏离基线 0.0109…0.0371,阈值 0.006);改回 make_materialPASS。这条不是「截图已生成」式的通过。
  • 局限:基线绑定当前引擎版本、灯光和资源。改模型/发型材质/灯光都会使其失效,必须人工看过渲染结果再 --bless 并在此记录原因。

纠正前一批记录的一处误判

  • 第一批记录里「选角截图仍复现发型遮脸」中被一并当作缺陷的「武器平躺在地上」不成立:那是选角页 ui/char_select_screen.gd:517 建的 dragon_left/right_button_0{1,2,3}.sub 两个 2D 导航按钮((714,712) / (1024,712),160×44),不是武器挂点问题。武器挂接没有发现缺陷。

仍未完成

  • 导出包复验:包内没有离线渲染入口(导出模板不接受 --script 已解决(2026-09-08:新增 project/package_render_test.gd,由 client_main.gdMT_TEST_MODE=render 时挂到主场景下—— 绕开导出模板不收 --script 的限制,走真实 GameScene + Metin2World + PlayerView,不连服务器, 报告和截图落到 user://render-check/MT_RENDER_OUTPUT 可改)。

    MT_TEST_MODE=render build/export/mtgodot-poc.app/Contents/MacOS/mtgodot-poc
    

    首次在导出包内跑的结果(2026-09-08release 包):PASS —— 扩展三个类都注册、资源根解析到 Contents/Resources/assets、地图 20/20 chunk、model_built=truesurfaces=4feet_gap=0.001373, 截图是正常的草地 + 角色。R2-01/R2-02 的「导出包中复验」这一步由此补齐。 未覆盖:网络驱动的进场时序、40250 像素比对、性能。

  • 新发现的实机故障:进入游戏后 3D 画面整片灰紫、主角仍是蓝色胶囊,2D UI 与小地图正常 已修复(2026-09-08

    根因EntityStore::mut_despawn()GC_CHARACTER_DEL 命中主角 vid 时把 m_main_vid 清零了。 40250 服务器在进场那一两秒里会对主角自己的 vid 连发 GC_CHARACTER_DEL + GC_CHARACTER_ADD (视野重算,实测 10 次左右),于是主角被永久「取消主角身份」:get_main_vid() 返回 0 → _sync_main_character() 直接 return → 模型装配代码一次都没跑(main_attempts=0), 占位胶囊留在 y=0,相机跟着它钻到地形底下 → 满屏灰紫。地图其实一直是好的。

    参考端怎么做40250/ClientVS22/source/UserInterface/):主角 vid 归 CNetworkActorManager::m_dwMainVID 所有,只由 SetMainActorVID()GC_MAIN_CHARACTER)写、 只在管理器销毁时清;RemoveActor()NetworkActorManager.cpp:440)删 actor 行和角色实例, 不碰 m_dwMainVID。重新 add 时 __AppendCharacterManagerActor()kCreateData.m_isMain = __IsMainActorVID(dwVID) 把它重新认成主角,并再跑一次 SetMainCharacterIndex(dwVID)

    修法extension/src/net/entity_store.cpp,两处对齐参考端):

    1. mut_despawn() 不再清 m_main_vidm2dev 分支的 GC_CHARACTER_DEL 同样处理); 该字段现在只由 mut_spawn_main() 写、只由 reset_for_map_change() 清(对应参考端的销毁)。
    2. mut_spawn_full()vid == m_main_vid 就把 is_main 置回 true,并补一条 MainSet 变更,对应 __AppendCharacterManagerActor()m_isMain + SetMainCharacterIndex

    取证手段(保留在树里):

    • project/net_trace.gd(由 client_main.gd 挂在 AppFlow 之后)每帧比对 get_main_vid() 丢主角时打 NETTRACE: MAIN_LOST ... reason=despawn|silent 和最近 24 条事件环。 reason 直接区分「GC_CHARACTER_DEL 命中主角」和「reset_for_map_change() 静默清零」 (后者会清空 m_changes,不发 despawn 信号)。MT_NETTRACE=1 打全量。
    • game_scene.gd _diag_tick():进游戏若干秒后,_model_built_map_loaded() 任一为假就打一行 GAMESCENE_DIAG:race / pos_cm / map_path / map_base_set / main_attempts / main_sync_ready / map_report …)。MT_DIAG=1 强制输出并连打 6 次(间隔 2 s), MT_DIAG_AFTER_MS 改延时,MT_DIAG_SHOT=<png> 在最后一次存一张实机帧。

    实机复验(真服 192.168.21.203classic2026-09-08):

    • 修前 ./script/live_smoke_test.sh --no-build --no-mutations --no-reconnectFAIL: 主角色 VID 有效 / FAIL: 主角色不存在NETTRACE: MAIN_LOST was=25910 now=0 reason=despawn
    • 修后同一命令 → LIVE_SMOKE RESULT: PASSmain_vid=25933 pos_cm=(963444.0, 266221.0, 0.0) entities=19
    • 40 秒实机连打诊断:model_built=truemain_attempts=1player_pos=(418.44, 177.67, 614.21) (站在 C1 地形上,不再是 y=0),16 次采样全程稳定;MT_DIAG_SHOT 存下的帧是正常的 地形 + 建筑 + NPC + 主角模型 + HUD + 小地图。

    未覆盖:主角材质在俯视机位下偏暗的观感问题——那属于光照/色调,受 PARITY-GAP §0 门禁约束, 不在这次修复范围内。

  • R2-01 的 GLB 对照、R0 完整视觉基线与区域阈值、R4 选角/快捷栏、R5 最终包验收、R6 性能,均未开始。

2026-09-08 后续复核:种族与动画资源配套

  • 修复 PlayerView.build() 用性别选择 pc/pc2 的错误。资源中的 playersettingmodule.py 明确规定 race 0..3 使用 pc、4..7 使用 pc2;女性刺客/萨满属于 pc。原实现使 race 1/3/5/7 初始模型性别错误,EquipModel 从 MSM 恢复正确身体后仍保留另一性别动作。现在按 race 范围选择同一资源族;对应资源缺失时返回构建失败,避免跨性别回退。
  • 新增 race_motion_assembly_test.gd:以真实 MSM 换装后的身体目录为独立依据,检查身体、发型与动作资源配套,覆盖八种族 × 六动作(wait/walk/run/attack/damage/dead)× 三个时间点。重新构建实际加载的扩展后运行结果 failures=0race_spec_test.gd 通过。有限包围盒断言不等于完整动作画面或骨骼精度验收。
  • 通过 bash script/rendering_test.sh 重建并同步 debug 扩展;CPU 绕序与选角装配通过。旧本地库复现已修复的约一米悬空,重建后 model_render_test.gd 的八种族足底、剪影下限和材质负对照绝对断言通过。
  • 像素回归仍失败,未更新基线model_render_test.gd 的 head_cover、silhouette_px、hair_mean_lum 与保存基线不一致。种族 1/3/5/7 原基线含错误性别资源;其余种族亦存在亮度或剪影差异,须继续排查合并后材质/绕序和基线生成条件。当前扩展 SHA-256:b682582b2c96d7b9bd005d75ac781dde8d35fbe309ca01792d5ae3c5f35c5409。不得沿用旧“八职业 PASS”作为当前代码像素验收结论。
  • 剩余:像素基线差异定位、装备/发型组合与完整动作视觉验收、GLB 对照、坡地/台阶、R3 世界与光照、R4 UI 联机恢复、R5 最终包、R6 性能。上述新修复尚未在最终导出包或真实登录中复验。

2026-09-08 像素基线差异闭环

  • 确认基线来自合并前 e0c18539:其 CPU 蒙皮没有应用 flip_winding。诊断参数 --legacy-winding 恢复旧索引顺序后,race 0 对旧基线 PASShair_mean_lum=0.060864(基线 0.060796),head_cover=0.324646(基线 0.324674)。这解释了合并后双面头发的光照/遮挡变化;race 1/3/5/7 还受已修正的性别资源映射影响。
  • 人工检查八种族正面和侧面近景:模型性别、脸和发型正确,无先前浅色三角斑。保留当前正确绕序与资源映射,用生产路径重建 model_baseline.json,未放宽任何像素阈值。近景为固定局部采样,部分头顶出框,不能据此宣称完整头部多角度验收。
  • model_render_test.gd 冻结采样时关闭动作过渡,报告/基线增加身体和发型 SHA-256;回归检查资源哈希。仅在全部绝对断言通过、八种族生产模式运行时允许写基线,禁止单种族或旧绕序诊断覆盖它。
  • 重建报告:build/rendering/modeltest-1788861134/report.json。三次独立复测:modeltest-1788861136modeltest-1788861139modeltest-1788861142,均 PASS。旧绕序对新基线负对照失败,证明测试仍可检测旧行为。以上仅为当前 CPU 基础模型单角色场景;GPU、换装和最终包仍待独立验收。

2026-09-08 GPU 发型切换与选角朝向

  • 修复 _load_hair() 后 GPU 合并网格未刷新的问题:换发型/颜色与移除发型现在重建网格、重新分配身体/头发材质,并更新当前姿势包围盒。CPU 移除发型也即时刷新,不依赖下一动画帧。GPU 发型采用与 CPU 一致的双面材质。
  • character_winding_test.gd 增加暂停动画期间移除/替换头发、纹理像素比对、双面材质与身体不误分类检查。CPU/GPU 均 failures=0。重新构建扩展后八种族 CPU 像素回归 PASS,基线未改。
  • 实景发现 InitialPlacement 修正后,选角仍保留旧 90 度补偿,导致人物侧向镜头;现移除该补偿。男战士 5th 护甲 + HairIndex 1001 GPU 截图 build/rendering/selection-1788861936-4253/selection.png 人工复核正面与完整入框;CPU 装配截图报告 selection-1788861960-4277 PASS。
  • 本轮不代表完整 GPU 动作/LOD、多职业高级装备或最终导出包验收通过;这些仍需继续覆盖。

2026-09-08 GPU LOD 附件丢失修复

  • 确认 _set_lod() 在 GPU 路径仅调用身体 build_mesh(),切远景时丢掉合并头发,回到 LOD0 仍不恢复,且保留旧身体/发型材质分界。现在 GPU LOD 使用 _build_gpu_mesh(),重建身体与头发后刷新材质和当前姿势包围盒。
  • 新增 gpu_lod_attachment_test.gd,使用实际相机距离驱动八种族 LOD 0→1→2→3→2→1→0。检查实际 LOD、头发表面/顶点、alpha-test 和骨矩阵纹理。修改前 144 项失败;修改后 headless 与 Metal 实际渲染后端均 failures=0。这是网格/材质状态验收,不是动画全周期像素或剔除边界验收。
  • bash script/rendering_test.sh 构建成功;GPU character_winding_test.gd 通过;八种族 CPU model_render_test.gd PASS,未调整基线。实际加载库 SHA-256:306265206b3a5cf5c8066d908613a1df3cf17074c2bc44ebee71ac2f7cd450bd
  • 仍需:GPU 完整动作的动态剔除包围盒、LOD 淡入淡出视觉检查、多职业高级装备、坡地与最终包复验。

2026-09-08 GPU 动态剔除范围修复

  • 原 GPU 路径只在首帧/LOD/换发型时更新剔除盒;后续跑步、攻击、死亡沿用旧姿势范围。现在每次 gpu_skin() 采样同步当前身体和发型包围盒。首次就绪信号仍只触发一次,避免选角镜头随待机呼吸不断重新取景;取景查询直接读取已更新的结果。
  • 新增 gpu_pose_bounds_test.gd,八种族 × 六动作 × 五个时间点(含非循环末帧),将 GPU 实际 custom_aabb 与同姿势 CPU 网格包围盒比较,不调用会刷新范围的取景接口。修改前 195/240 采样失败,修改后 headless 和 Metal 均 failures=0GPU LOD 附件回归亦通过。
  • 构建入口 bash script/rendering_test.sh 通过;库 SHA-2568e7d635dbb9b407d8b41b61c9e73330e12ecece6033ab1b3a8885f796e24a32d。包围盒计算增加逐采样 CPU 顶点遍历成本,尚未测量多角色帧时间,GPU 仍属实验模式;后续可在保留此正确性对照的前提下优化。
  • 覆盖限于基础人物和默认发型的上述离散采样;骨骼变形像素精度、混合动作、LOD 过渡残影、坡地、高级装备与最终包仍待验收。

2026-09-08 LOD 淡出残影快照修复

  • _set_lod() 的 GPU 淡出模型此前与主模型共享骨矩阵纹理,不能保持切换时姿势,且未复制动态剔除盒。现保存独立纹理快照与旧姿势 custom AABB,后续动画采样不再改变淡出模型。
  • 旧模型材质改为逐表面复制实际 active material,避免 CPU 头发单独设置的材质不在 surf_mats 缓存中而丢失。
  • 扩展 gpu_lod_attachment_test.gd:验证纹理隔离、后续采样不修改快照、剔除盒及逐表面材质。在旧库上 48 次淡出检查共 96 项失败;重建后 headless 与 Metal 均 failures=0、ghosts=48。构建、CPU 绕序/发型切换与选角装配亦通过。
  • 尚未完成逐帧淡入淡出视觉差异验收;CPU 淡出材质修复尚缺专门快照回归。坡地、高级装备与最终包保持待验。

2026-09-08 技能特效透明混合与静止粒子生命周期

  • 对照 40250 ClientVS22/extern/include/d3d8types.hD3DBLEND_SRCALPHA=5、INVSRCALPHA=64 实为 INVSRCCOLOR)。EffectPlayer._blend() 原先将 5/6 回落为 ADD,现映射 MIX,供粒子与网格特效共用。真实萨满 7budongbakbu_throw.mse 使用 5/6;此修复避免普通透明特效被误当作叠加发光。其他混合对仍保留原有近似,不代表全部 D3D 混合已还原。
  • 去除 _advance_particle() 在位置轨道为空时的提前返回:静止粒子同样累计时钟、遵守 StartTime、非循环与有限循环结束条件;无位置轨道不移动节点,无限循环保持原行为。
  • 新增 effect_render_regression_test.gd:修前 5 项失败,修后 failures=0;覆盖材质构建、无位置轨道的延迟/有限循环/非循环/无限循环,以及真实萨满 MSE 解析后的透明材质。headless 和 Metal 均通过状态断言;本测试不采集画面,不等于技能像素验收。真实资源加载测试退出时有 1 个 ObjectDB 泄漏警告,来源待定位,不能宣称资源释放验收通过。
  • skill_fx_test.gdplayer_skill_test.gddamage_effect_test.gd 全部 PASSgit diff --check 通过。本轮仅修改 GDScript,未重建扩展、导出 mac 包或联机施法验收。

当前剩余修复 / 验收清单(历史记录中的“未开始”以本节为准)

  • 技能:停止/重播时序、RGBA 寿命曲线与重复颜色乘法已修复并补回归(见后续记录);其余混合因子、粒子发射/速度/尺寸近似、贴图动画、骨骼挂点与动作事件同步,尚需对照 40250。缺四职业多等级技能的实机逐帧视觉验收。
  • R0:40250 同资源、同机位、同动作的配对截图及区域差异验收;当前本地 CPU 回归基线不是 40250 等价证明。
  • R1/R2:切角色/重连/切图边界、高级装备与武器/发型组合、混合动作、坡地/台阶、NPC/怪物、GPU 像素精度和 GLB 同资产对照。LOD 残影已有快照修复,但仍缺逐帧视觉与 CPU 专项回归。
  • R3:角色偏暗、环境光/雾/阴影、地形混合与接缝、建筑和树木材质。
  • R4:完整选角界面对齐,技能栏实际施法/冷却/拖放/翻页/重连恢复与多分辨率验收;不应将已修复的快捷栏遮挡记为完全未做。
  • R5:包含这些新修改的最终 mac 客户端打包、独立启动与真实登录验收。
  • R6:多角色性能、缓存和资源释放;GPU 每姿势包围盒的 CPU 遍历成本尚未量化,保持实验开关。

2026-09-08 技能停止与重播时序修复

  • 原延迟 SceneTreeTimer 在 stop() 后仍启动粒子,原一次性回收计时器会删除后续重播实例;原 _process() 还会把 stop 隐藏的灯光重新显示。改为播放进度统一驱动粒子启动和一次性回收,移除独立定时器;停止后不再推进 CPU 状态。
  • 构建粒子时关闭默认发射,play() 即时遵守 StartTime。重播重置粒子启动标记、位置与源循环模式,并重置网格/灯光时钟和网格帧/贴图帧缓存,重新采样零时刻。此前 play(true) 的 one_shot 标志不再污染后续 play(false)
  • stop() 关闭发射并隐藏网格/灯光,已生成粒子允许自然结束;取消自动回收,实例由拥有者释放或再次播放。这不是暂停/恢复接口。正常一次性播放仍按播放时钟回收;场景暂停或禁用处理不会消耗 CPU 播放时长。
  • 新增 effect_playback_test.gd:修前 13 项失败,修后 headless 与 Metal 均 failures=0。覆盖停止后延迟回调、旧回收与新重播隔离、网格首帧与灯光复位、循环模式恢复、当前一次性回收,以及实际 SceneTree 帧驱动下延迟启动/停止稳定性。网格使用合成双帧夹具;不代表真实 MDE/IFL 逐帧画面验收。
  • fx_test.gd 灯光测试补显式 play()(与生产调用顺序一致)。fx_testcombat_fx_testskill_fx_testplayer_skill_testdamage_effect_test 及上一轮 effect_render_regression_test 均通过。现有真实资源/战斗测试仍分别出现 ObjectDB 退出泄漏警告,留待 R6 定位;新播放测试无此警告。git diff --check 通过。
  • 本轮未修改 C++、未提交、未打包或联机施法验收。上节 R0–R6 剩余项保持待办,下一步优先检查技能颜色曲线、混合与挂点同步,并建立四职业真实施法画面对照。

2026-09-08 技能 RGBA 寿命曲线修复

  • 对照 40250 Source/EffectLib/ParticleSystemData.cpp:参考实现合并 R/G/B/A 四通道关键帧时间,在并集处逐通道线性求值;ParticleInstance.cpp 使用粒子已消耗寿命比例。原 Godot 实现将 RGB 固定为各轨道末值,仅 Alpha 随寿命变化,丢失中间变色。
  • Godot 4.7 scene/resources/particle_process_material.cpp 的生成着色器使用 parameters.color *= texture(color_ramp, vec2(lifetime))。此前同一 RGB 写入基础颜色和渐变,导致通道平方(如 0.5→0.25)。现在基础颜色设白色,仅在完整 RGBA 渐变中存储颜色。
  • _color_ramp() 合并所有通道关键帧并保留线性插值;无轨道默认 1,首尾延续最近值。对无序/重复/非有限数据做归一化,重复时间保留最后值;有效资源的寿命归一化时间语义不变。未改变混合模式或放宽现有像素基线阈值。
  • 新增 effect_color_test.gd:旧实现 8 项失败,修复后通过。覆盖不同通道独立拐点、常量非白色、无 Alpha 时的 RGB 动画、缺省及异常数据。另解析 warrior palbang_4_sword、assassin beam_blow、sura pabeopsul_4_blow、shaman noejeon_making_1 四份真实 MSE,把颜色轨道经过材质构建后与独立标量插值对照,共 21 组粒子 × 41 时间点 = 861 采样,headless / Metal 均 failures=0。
  • 真实资源测试有意只装配颜色轨道,未加载原贴图或发射几何;测试检查 Gradient 与材质配置,不是最终 GPU 粒子像素、色彩空间、纹理采样精度或 40250 8-bit 颜色量化等价验收。
  • effect_playback_testeffect_render_regression_testfx_testskill_fx_testplayer_skill_testdamage_effect_testcombat_fx_test 均通过;部分既有资源/战斗测试的 ObjectDB 泄漏警告仍在,新颜色测试无泄漏警告。git diff --check 通过。
  • 剩余优先项:其他 D3D 混合/ColorOperationType、发射方向/速度/尺寸/重力等时间曲线、贴图逐粒子动画、技能挂点与动作时序,以及四职业多等级真实施法对比。R0 配对基线、R1/R2 高级装备/坡地/混合动作/GPU/NPC、R3 世界光照材质、R4 UI、R5 最终 mac 包、R6 性能与泄漏仍未完成。本轮未提交、未打包、未联机验收。

2026-09-08 粒子宽高与寿命缩放修复

  • 对照 ParticleSystemInstance.cpp / ParticleInstance.cppTimeEventSizeX/Y 是出生时的独立半宽/半高,顶点沿两个方向分别加减 half-size × ScaleX/Y。旧实现用 SizeX 单值和 1×1 Quad,忽略 SizeY,还加入 0.7–1 的随机缩放与强制最小尺寸。
  • 现 Quad 完整尺寸为各轴 half-size × 2 × 0.01 米,统一随机缩放设 1,零尺寸不再强制放大。使用 Godot 支持的 CurveXYZTexture 保留独立 ScaleX/Y,Z 为 1;线性切线替换默认平滑插值,曲线范围随数据确定,不再固定到 0–2。缺失缩放轨道采用常量 1,而不是自动缩到零。
  • 范围限制:SizeX/Y 的发射器时钟采样仍取末值,不是每个粒子出生时采样;本轮修复独立轴与静态尺寸,不声称出生尺寸时间曲线已还原。
  • 真实 palbang_4_sword 含同时间不同缩放值。Type.h 的 GetTimeEventBlendValue 在命中重复时间时取第一值,之后从最后值插值。现保留两侧,用最多 0.0001 归一化寿命的窄过渡近似跳变(纹理无法表达零宽跳变);GPU LUT 还会有采样平滑。跳变像素仍待验。此前颜色轨道的重复时间去重策略也应进一步核对,不能视为完整突变语义一致。
  • 新增 effect_scale_test.gd:修前 5 项失败,修后 headless / Metal 均 failures=0。合成用例覆盖独立宽高、去随机缩放、线性插值、超过 2 的缩放、零尺寸与缺省;四职业真实 MSE 共 21 组粒子、130 个关键帧/中点检查通过。仅跳变后的中点允许 <0.001 缩放值误差以容纳上述窄过渡,其余仍使用近似相等;未修改既有人物像素阈值。真实测试仅装配尺寸/缩放,不加载原贴图,也不是施法截图验收。
  • 颜色、播放时序、透明混合、fx/skill/player_skill/damage/combat 回归通过。既有资源与战斗测试退出泄漏警告仍存在。未修改 C++、未提交、未打包、未联机施法验收。
  • 当前未完成:技能出生尺寸与方向/速度/重力等时变参数、其他混合/颜色操作、重复关键帧精确行为、逐粒子贴图动画、挂点与动作同步;R0 同机位 40250 对比;R1/R2 高级装备/混合动作/坡地/NPC/GPU 精度;R3 光照偏暗与世界材质;R4 选角和技能栏完整交互;R5 最新 mac 包和登录验收;R6 多角色性能与资源泄漏。

2026-09-08 粒子恒速旋转与 billboard 修复

  • 核对 EffectLib ParticleSystemData.cppCW 使用 RotationSpeedCCW 使用负值,无额外随机减速;ParticleSystemInstance.cpp 读取 RotationRandomStartingBegin/End。Godot 4.7 ParticleProcessMaterial 恒定角速度属性同样使用度/秒,着色器内部再转弧度。去除提前 deg_to_rad 和恒速模式的 0.3–1 随机倍率,恢复初始角度范围;NONE 不自动旋转,但仍保留初始角度。
  • 相机朝向的粒子(BillboardType=1)改为 BILLBOARD_PARTICLES,使绘制着色器读取 INSTANCE_CUSTOM.x 的角度;普通 mesh 保留 BILLBOARD_ENABLED,不读取不存在的粒子 custom data。
  • 核对 EffectLib/Type.h:固定 Y 轴为 2,旧映射错误地使用 4,现修正粒子/网格共用映射。粒子 3=LIE、4=2FACE、5=3FACE 仍近似为普通相机朝向,未实现原几何与方向;Y 轴及无 billboard 模式下的粒子自旋也仍需独立实现/视觉验证。
  • 新增 effect_rotation_test.gd:初版负对照修前 8 项失败,修后通过;补测正确 Y 轴枚举、mesh 不受粒子路径影响,以及真实萨满 7budongbakbu_throw 中 2 组恒速粒子,headless / Metal 均 failures=0。真实测试只检查材质/旋转参数,不加载原贴图,不是最终旋转方向或逐帧像素验收。
  • 颜色、尺寸、播放、透明混合、fx/skill/player_skill/damage/combat 回归均通过。已有资源/战斗测试仍有退出泄漏警告,新旋转测试无该警告。git diff --check 通过。
  • 仍未完成TIME_EVENT 转速仍取末值,RANDOM_DIRECTION 仍未按原实现逐粒子选择等幅正/反转;铺地/交叉面/拉伸 billboard、出生尺寸与运动时间曲线、其他混合/颜色操作、贴图动画、挂点同步与实机技能对比。R0 配对基线、R1/R2 高级装备/坡地/GPU/NPC、R3 世界光照材质、R4 UI、R5 最新 mac 包、R6 性能和泄漏继续待办。本轮未提交、未打包、未联机验收。

2026-09-09 TIME_EVENT 粒子转速曲线

  • 将 RotationType=1 从取 TimeEventRotation 末值改为 CurveTexture 寿命采样,保留正负速度、过零与末帧停止;缺失轨道默认零,不使用 RotationSpeed 回退。共用线性曲线构造新增 fallback 参数,尺寸曲线仍默认 1,不改变上一轮尺寸行为。
  • 对照 EffectLib CRotationDecorator 的 speed × elapsed 积分,以及 Godot 4.7 ParticleProcessMaterial 生成着色器:带 angular_velocity_curve 时,纹理值 × 基础速度 × DELTA 直接累积至弧度 CUSTOM.x;无曲线恒速分支另做 degree_to_rad。因此曲线保留资源的度/秒值、基础乘数固定 PI/180;恒速模式继续使用度/秒,不混用两条路径。此适配与 Godot 4.7 实现绑定,升级引擎应重新核对。
  • 扩展 effect_rotation_test.gd,修前新增两项曲线存在性断言失败,修后通过。合成 180→-180→0 曲线验证过零、负速度、末帧归零,以及两秒寿命加初始 30° 后积分为 -60°;空轨道为零。真实萨满 jeungsok_blow、8chojeonbu、jeungryeok_hand 覆盖 3 组 TIME_EVENT / 3 个关键帧(这些真实样本各为单关键帧,变化曲线由合成用例覆盖)。headless 与 Metal 均 failures=0。
  • 测试验证配置、曲线采样与 CPU 数值积分,不直接读取 GPU 粒子角度;不代表实际逐帧积分误差、GPU 曲线纹理采样精度或 40250 画面方向已验收。原始多变速真实技能与长时间/多帧率视觉回归仍待补。
  • 尺寸、颜色、透明混合、播放时序及 fx/skill/player_skill/damage/combat 回归通过,git diff --check 通过;既有资源/战斗测试退出泄漏警告仍在。未提交、未打包、未联机验收。
  • 当前剩余:随机等幅正反转、铺地/交叉面/拉伸及非相机朝向粒子自旋;出生尺寸与方向/速度/重力时间曲线;其他混合/颜色操作、重复关键帧精确行为、逐粒子贴图动画与挂点同步;R0 40250 配对基线,R1/R2 高级装备/混合动作/坡地/NPC/GPU 像素,R3 光照偏暗与世界材质,R4 选角/技能栏交互,R5 最新 mac 包和真实登录,R6 性能与泄漏。

2026-09-09 特效双面显示与深度状态

  • 核对 EffectLib/EffectInstance.cpp::OnRender():粒子与网格效果共同使用 D3DCULL_NONE、ZWRITEENABLE=FALSE。现 _build_particle()_mesh_material() 显式设置 CULL_DISABLED、DEPTH_DRAW_DISABLED;保留默认深度测试,不影响角色、地形、建筑材质。
  • 新增 effect_surface_test.gd:用实际构造的粒子/网格材质绘制白色静态 Quad,Metal 分别从正面和背面读取中心像素,再放入不透明黑色物体确认遮挡。旧代码 4 项材质状态断言和 2 项背面像素断言失败,共 6 项;修后 failures=0,两个材质的双面可见及双向世界遮挡共 8 项像素检查通过。headless 仅运行状态断言,显式跳过像素检查。
  • 测试使用白色替代纹理、静态 Quad 和两个机位,隔离材质剔除行为;未覆盖真实 MDE 几何、动态粒子、透明物体排序、最终游戏画面。深度写入由属性断言验证,未把原默认透明材质描述为必然写入深度。
  • 旋转、缩放、颜色、透明混合、播放时序及 fx/skill/player_skill/damage/combat 回归均通过;已有资源/战斗退出泄漏警告仍在,新表面测试无该警告。git diff --check 通过。本轮未提交、未打包、未联机验收。
  • 材质调查记录:assets 中 ColorOperationType 计数为 2:5、3:12、4:5596、5:354、6:6、8:1(字段出现次数,非去重技能数)。当前 StandardMaterial 主要对应 4 的调制逻辑,其他颜色操作仍未实现;特别是 5/6 的加倍调制不能仅通过修改混合模式替代。本轮未改变这些颜色操作,留待独立着色器及像素回归。
  • 仍未完成:上述其他颜色操作/混合、随机正反转、铺地/交叉面/拉伸 billboard、运动与出生尺寸时间曲线、贴图动画/重复关键帧精确行为、技能挂点同步与真实施法对比;高级装备/混合动作/坡地/NPC/GPU 画面;角色偏暗及世界光照/地形/建筑植被材质;选角与技能栏完整交互;40250 配对基线、最终 mac 包与登录验收、多角色性能和资源泄漏。

2026-09-09 粒子双倍/四倍颜色调制

  • 实现 D3DTOP_MODULATE2X(5) / MODULATE4X(6)。新增 fx/particle_color_material.gdRGB 为 clamp(texture.rgb × particle.rgb × 2/4, 0, 1),之后交给 framebuffer 混合;Alpha 仍为 texture.a × particle.a,不随 RGB 倍率放大。操作 4 保留既有 StandardMaterial 路径,未改变其他颜色操作的近似。
  • 新材质复用现有混合模式选择(mix/add),保留粒子旋转、相机 billboard、固定 Y 和独立轴缩放;双面绘制、不写深度但保留深度测试。铺地/交叉面等原有近似未因此还原。着色器按混合模式缓存,仅粒子操作 5/6 进入新路径,网格特效材质不受影响。
  • 新增 effect_color_operation_test.gd:临时恢复原材质选择作负对照,5 项配置/真实资源断言失败;恢复修复后通过。真实 noejeon_4_blow 中一组 operation=5、Y billboard 粒子正确选择新材质(真实检查不加载原纹理)。
  • Metal 像素检查:操作 4/5/6 × mix/add 共 6 组,用白纹理、已知顶点色及 Alpha=0.5、黑背景绘制。检查 RGB 饱和发生在透明混合之前且 Alpha 不放大;将输出 sRGB 转线性后,RGB 向量误差 <0.025,全部通过。白纹理隔离颜色运算,不覆盖真实非白贴图色彩空间、彩色背景或 40250 非线性色彩管线等价性。
  • 扩展 effect_surface_test.gd,加入两种新材质的正反面可见和世界遮挡;四种材质共 16 项 Metal 像素断言通过。headless 仅验证颜色操作配置,明确跳过像素。动态 GPUParticles 的旋转/缩放与复杂透明排序仍需场景级画面验收。
  • 颜色、尺寸、旋转、透明混合、播放时序与 fx/skill/player_skill/damage/combat 回归均通过,git diff --check 通过。已有资源/战斗退出泄漏警告仍在,新颜色操作/表面测试无该警告。未提交、未打包、未联机验收。
  • 当前未完成SELECTARG1/2、ADDSIGNED 等其他颜色操作及更多混合对;随机正反转、铺地/交叉面/拉伸、出生尺寸和运动时间曲线、贴图逐粒子动画、关键帧突变精度与挂点同步;R0 40250 同机位基线;R1/R2 高级装备/混合动作/坡地/NPC/GPU 像素;R3 角色偏暗与世界材质光照;R4 选角/技能栏完整交互;R5 最新 mac 包/登录;R6 多角色性能和泄漏。

2026-09-09 集中修复、整套回归与 mac 实包验收

本轮已完成的代码修复

  1. 补齐现有资源中的颜色操作SELECTARG1(2) 取粒子色、SELECTARG2(3) 取纹理色、ADDSIGNED(8) 为两色相加减 0.5RGB 饱和后混合,Alpha 仍单独相乘。与已有 4/5/6 共覆盖此前资产调查发现的六类 ColorOperationType;不等于所有 D3D 混合因子已支持。扩展白/彩色纹理 × 六操作 × mix/add 共 24 项 Metal 像素检查,保留原误差阈值。
  2. 发射生命周期EffectPlayer 的 one_shot 仅控制特效实例回收,GPUParticles3D 不再用一次粒子寿命终止发射。短寿命粒子在 MSE 发射窗口内持续补充,窗口结束仍停止。新增实时后端测试:粒子寿命 0.05s、发射窗口 0.8s,在 0.3s 仍发射,0.9s 后停止。
  3. 附着粒子:读取原字段 AttachEnable,映射 local_coords,区分粒子随发射器移动和留在世界空间。该配置修复不代表完整技能骨骼挂点/坐标轴与动作事件同步已验收。
  4. 技能栏冷却:按技能 ID 保存会话内冷却,服务端槽位刷新、同技能多格、换格/重新放入不再清空冷却遮罩;冷却结束包清理共享状态。技能 UI 回归覆盖重复技能格、服务端恢复、提前解锁及原有翻页/移动端行为;未实现跨断线恢复的服务端剩余冷却同步。
  5. DDS 真正缺图根因:真实 shamanblackcore.dds 为 ARGB1555,旧解码器无法读取。现按 DDS 位掩码和行 pitch 解码非压缩 16/24/32 位 RGB,包括 A1R5G5B5、R5G6B5、A4R4G4B4、24 位和不同 32 位通道顺序;无有效 alpha 标志按不透明处理。拒绝重叠/非连续掩码、无效 pitch、截断输入;DXT1/3/5 保留。新增 C++ 用例经 AddressSanitizer/UndefinedBehaviorSanitizer 通过。此为 level-0 解码,不表示全 DDS 格式/mipmap 支持。
  6. 资源生命周期UiAssets 的 Metin2World 解码辅助节点现同步释放,返回 Image 独立存活;真实 DDS 连续 20 次解码节点计数不增加。combat_fx_test 的两个孤立假节点改为正确归属树(这是测试夹具修复,不误记为生产泄漏修复)。

综合回归证据

  • 新增入口 bash script/rendering_batch_test.sh,保留每项完整日志,检查退出码、成功标记和 GDScript 错误;遇到失败继续收集其余结果。最初运行发现测试夹具干扰和 macOS Bash 空数组兼容性问题,修复后重跑。
  • 最终:21 项 headless + 7 项 Metal = 28 项全部通过,日志目录 build/rendering/batch-20260909-072138-35505/。覆盖特效、技能栏、施法校验、战斗、场景与登录生命周期、换装规则、八种族动作装配、CPU 像素基线、GPU LOD 附件与动态包围盒;本次最终批次未出现退出泄漏警告。
  • bash script/rendering_test.sh 重建/同步 debug 扩展与选角装配通过,人物 CPU 像素基线未调整。native 测试:clang++ -std=c++17 -Wall -Wextra -fsanitize=address,undefined -Iextension/src extension/tests/dxt_decode_test.cpp extension/src/dxt.cpp -o build/dxt_decode_test && build/dxt_decode_test
  • git diff --check 通过。全部原有未提交修改已保留,本轮未提交。

新 mac 包与实际进场

  • 独立产物:build/export-rendering-20260909/mtgodot-poc.app,保留 build/export/ 原包。build-macos-client.sh 新增 MT_MAC_OUTPUT_DIR,便于输出到独立目录;这次是 release、arm64+x86_64 universal、ad-hoc 签名,codesign deep/strict 检查通过。实际运行验证平台为 Apple M4,Intel 平台未实测。
  • release 扩展 SHA-256683aad5a97a0e6b0c959b082bb3774950d12565b4a6e0c216a1f53eb9a1d2120PCK SHA-2569cd602618e76debad02aa6a5544ac61df498ded1e3cebe5351d962800d0be023
  • 包内离线 PASS:清除 MT_ASSETS,资源实际来自 Contents/Resources/assets;真实 A1 地图、角色、DDS 和专用技能材质均加载成功。报告/截图 build/rendering/package-20260909/report.json / game.png,脚底 gap=0.001373m,20 块地形加载、0 块失败。额外技能检查为资源/材质装配,不是施法画面。
  • 真实只读登录 PASS:用新包连接现有 40250 服务,9.633 秒完成登录和主角/世界/属性/背包/技能/快捷栏接口检查。build/rendering/live-20260909/live-smoke-report.jsonmutations=false、reconnect=false;未发送测试移动/聊天/施法/换装。测试脚本支持独立 APP/输出路径,遇到已有客户端会退出提示,不再杀掉用户运行中的客户端。
  • 实际游戏截图build/rendering/live-20260909/game.png。第二次普通自动登录后只观察,C1 主角就绪(main_attempts=1),后续多次采样位置稳定;人工复核画面包含主角、地表、NPC/建筑、小地图与底部技能图标。没有用离线截图替代此实机证据。
  • 未解决警告:包内离线整场景测试退出时仍出现 3 个 shader RID 释放警告,尽管测试报告 PASS;不能宣称全场景资源释放已完成。源码已有世界 shader 清理函数,具体退出时序/引用来源仍待定位,未用屏蔽日志处理。

仍需继续的工作(不能记为本轮完成)

  • 技能完整运行时:随机等幅正反转、铺地/交叉面/拉伸 billboard、非相机朝向自旋、运动与出生参数时间曲线、逐粒子贴图动画、重复关键帧突变精度、骨骼挂点/动作事件与真实四职业多等级施法验收;更多 D3D blend 对仍为近似。
  • 人物/环境保真:全高级装备与武器发型组合、混合动作、坡地/台阶、NPC/怪物/GPU 画面;角色与环境光照、雾/阴影、地形/水/建筑/植被和完整选角/UI 与 40250 的受控对照。SpeedTree 仍为 proxy,不能以本轮 DDS 支持替代完整树模型还原。
  • 交互与稳定性:多分辨率/完整技能栏输入拖放、跨重连状态与切图/换角色实机压力测试;本轮只读登录没有覆盖这些外部状态变更。
  • 性能/发布:多角色帧时间和 GPU 包围盒 CPU 成本、完整场景 shader RID 警告;Intel Mac、正式签名/公证、全量游戏流程验收。
  • 验收所缺证据:光照与材质精调仍需要 40250 同资源/角色/坐标/相机/动作/环境的参考帧,不能仅凭现有不同机位截图任意调亮后宣称一致。R5 的“有新包/独立加载/只读登录”本轮已完成,R0–R6 的全部保真门禁尚未完成。

2026-09-09 后续修复:动作挂点、逐粒子贴图、NPC 绕序及水面

本节为以上历史批次之后的最新进展。五组问题尚未全部关闭;配置/数值测试、Metal 像素测试、实包退出和 40250 配对验收分别记账。

已修复

  1. MSA 动作特效挂点数据链:两条 MSA 解析入口均保留 AttachingBoneName、AttachingEnable、FollowingEnable、IndependentFlagnative get_events() 和新增 motion_event_detailed 携带完整信息,旧四参数信号保留。GameScene 优先订阅详细信号,声音/射箭仍只分派一次。普通 EFFECT(type=1) 保留完整资源路径,不再取 basename 猜文件;使用实际动画混合后的全仿射骨骼矩阵区分跟随、世界快照、独立效果和根节点附着。偏移保持源码 bone * translation * actor 的行向量组合顺序,再转换厘米/Z-up;缺失骨骼不再误放到角色根。跟随节点在动画之后更新,角色销毁时随之回收。尚不覆盖 EFFECT_TO_TARGET(type=10)、远端角色全部动作事件、所有 MSE 内部坐标/旋转与真实施法同步。
  2. 逐粒子贴图动画:移除粒子共用 AnimatedTexture 时钟,原纹理装成 Texture2DArray;使用粒子自身 CUSTOM.y 年龄、CUSTOM.z 出生随机种子,在绘制 shader 中选择帧。支持 NONE/CW/CCW/RANDOM_FRAME/RANDOM_DIRECTION、随机起始帧及延时阈值;倒放从第 0 帧开始,随机方向的倒放从末帧开始,依据 ParticleSystemInstance.cpp。随机序列不承诺与旧客户端 RNG 逐帧相同;寿命目前仍是既有单发射器近似,不等于出生参数时间曲线已还原。
  3. NPC/怪物外表面被剔除:截图复核发现 MobView 未应用 PC 已使用的 flip_winding。真实野狗(101)、熊(110)、炼金师(20001) 的 CPU/GPU 六组几何法线负对照修前均失败(面积加权绕序/法线比值约 +0.93~+0.97),修后约 -0.93~-0.97、全部通过。不以三个样本代表所有 NPC/怪物资源。
  4. 水面明确的源码差异MapOutdoorWater.cpp 使用纹理 RGB 与顶点 diffuse alpha,不含原实现附加的 tint/Fresnel/PBR 光照。现恢复直接取纹理色与顶点透明度;AreaTerrain.cpp / MapOutdoor.cpp 的 raw 水深除以 400、上限 0.8 替代原 60cm/最低 0.12 的公式,正确考虑 HeightScale。按 2m 水格保留内部岸线采样,避免跨整行合并丢失岸线;四角透明度均为零时不生成水格。保留 30 帧/70ms 序列。仍有近似:水面高度浮动、远雾处混合状态及整体色彩空间尚未与受控参考帧对齐。

回归与证据

  • 原整套回归扩为 25 headless + 8 Metal = 33 项 PASS,日志 build/rendering/batch-20260909-163241-59569/;本批未出现退出泄漏警告。新增 motion_effect_anchor / mob_winding / water_reference / effect_texture_animation 测试。
  • motion_effect_anchor_test.gd 使用真实武士骨架与 gigongcham.msa,验证骨骼位置、角色转向下的厘米偏移、跟随/快照/独立/缺骨行为及文件解析元数据;不是完整施法截图验收。
  • effect_texture_animation_test.gd 用同一材质、两个不同年龄的实例在 Metal 读取像素,覆盖五种动画类型和三个年龄点;新生实例保持起始帧,老实例按自身年龄选帧。合成纹理用于隔离帧选择,不冒充真实四职业技能画面;随机起始帧与随机分布尚无统计验收。
  • 水深 native 单测经 ASan/UBSan 通过;最终水格剔除优化后另跑 water_reference_test.gd15 块水面、212587 个顶点对照、11898 个浅岸样本通过。RGBA8 顶点色容差为 1/255;最外地图边界 1149 个顶点不调用跨区块 sample_height 对照(其邻块不存在),明确计入 skipped。它验证几何/材质,不是水面配对像素。
  • formats_msa_test 重编通过;script/rendering_test.sh 重建并同步 debug 扩展、选角检查通过;git diff --check 通过。未提交,既有未提交内容保留。
  • rendering_scenario_test.gd 为独立、离线、真实资源场景报告:A1、CPU/GPU 两路径额外 1/8/16 个角色、run→attack→wait、野狗/熊/炼金师,1280×720 / 1920×1080 / 2560×1440 技能栏边界与截图,以及 world_reset + warp 信号驱动的 A1→C1。输出 build/rendering/scenarios-followup/report.json 与三张 scene-*.png。技能栏没有来自服务器的技能数据,不能将空槽截图称为完整技能交互验收。每组 90 个窗口帧间隔受垂直同步影响,不是 GPU timestamp、无帧率上限基准或大量角色性能保证。

shader RID 警告:已定位范围,未修复

  • 原包复跑 exit-export-repeat.log 重现 3 个 ParticlesShaderRD / Shader RID 警告。源码编辑器相同离线进场没有该警告。
  • 实包诊断中仅跳过粒子预检,地图/角色仍完整加载:exit-no-effects.log 不再出现警告。由此定位到粒子 process material 路径,不是凭“正好三个”推测为地形/树/水 shader。
  • 等待实际渲染帧、提前解除 emitter 的 process_material 引用、提前清理世界缓存均未消除实包警告;无效的生产清理改动已撤回,也没有屏蔽日志或将跳过粒子作为正式通过方式。保留正常进树/渲染/释放的包内预检。
  • 引用计数诊断显示释放 emitter 后仅剩测试数组/循环局部引用;仍需进一步检查 release 模板的粒子 shader 缓存/销毁路径,不能据此断言已经修复或一定属于某个上游缺陷。

当前剩余清单

  1. 技能:运动/出生参数时间曲线、径向发射、随机等幅自旋、铺地/交叉面/拉伸等特殊 billboard、内部坐标/旋转一致性、目标特效与远端事件、更多 D3D blend 对、四职业多等级真实施法/挂点画面验收。
  2. 人物:全部高级护甲/武器/发型组合、混合动作逐帧像素、坡地/台阶足部贴合、更多 NPC/怪物及 GPU 最终画面;本轮 crowd 的有限数值/稳定性覆盖不等于全部通过。
  3. 环境保真:40250 同资源/相机/动作/天气参考帧下的角色亮度、光照/阴影/雾、地形、水面、建筑植被;SpeedTree 仍为近似树。完整树几何需要可在目标平台使用、授权明确的导出/替代资源链,不能拿现有代理模型宣称还原。
  4. 交互与性能:真实重连/切图/换角色压力、跨重连冷却状态、多分辨率鼠标/键盘拖放与技能释放全流程、更大人数与长期帧时间/内存测量。离线 warp 测试不是联网重连验收。
  5. 释放与发布:实包三 shader RID 警告尚在;Intel Mac、正式签名/公证和全流程发布验收未完成。

本轮最终实包与发布门禁

  • 新产物 build/export-rendering-followup-20260909/mtgodot-poc.appuniversal releasead-hoccodesign deep/strict 通过);保留之前的 build/export-rendering-20260909/build/export/。本轮未重新联网登录,不能沿用上一批次登录结果宣称本包已完成联网验收。
  • 包内离线加载报告 build/rendering/package-followup-final/report.json 为 PASS:20 块地形、18 块非全透明水面、2 个专用技能材质,脚底 gap=0.001373m。完整日志 build/rendering/package-followup-final.log 在报告之后仍出现 3 shader RID 警告,因此干净退出门禁未通过
  • release dylib SHA-25663c5c0a639d5c531c0f28d64f9283c536a491156a1d29fa1b6ae931fdc826303PCKbcae262f6e91d82e79a7c0618750f1bed1325c5e7f7a043bb25d751d32918816
  • 新增 script/package_render_test.sh:需同时满足进场成功标记、进程正常返回、完整日志无 script/renderer 错误或泄漏,才返回成功。可用 MT_RENDER_APP 指定包、MT_RENDER_OUTPUT 指定报告目录。本包检查日志保存在 build/rendering/package-followup-gate/client.log,已知泄漏必须让检查失败,而不是过滤日志或只读 JSON 的 passed 字段。
  • 最终离线场景报告 failures=[],截图复核的 2560×1440 帧中野狗、熊、炼金师表面可见;早期小分辨率帧仍会记录模型刚创建时的 LOD 渐变,不是固定姿态参考帧。16 个额外角色样本:CPU median=16.666ms / P95=16.872ms / max=47.342msGPU median=16.705ms / P95=17.862ms / max=51.997ms。动作切换仍有长帧,不能宣称“稳定 60fps”或“GPU 更快”。

2026-09-10 续修检查点(以此节更新上述历史剩余清单)

已实现与专项验证

  • 目标特效MSA type10 携带 FishingEffectFlag;快捷栏成功施法时捕获目标,不随后续选中对象改变。跟随效果用目标变换,快照效果使用目标位置加世界偏移,目标消失后释放跟随节点。钓鱼落点尚未接入,明确不在施法者脚下误生成;快照中心仍为可见网格包围盒近似,不是完整旧版包围球还原。
  • 坐标与水平面特效:MSE 粒子/网格/灯光移动、Bezier 控制向量、发射方向与 MDE 顶点统一为厘米 Z-up → 米 Y-up。LIE 使用水平面 shader,Metal 俯视可见/侧视边缘负控通过。
  • 双面/三面粒子:新增 particle_faces.gd,同一粒子模拟驱动两个/三个面,面角通过 UV2 传入 shader;对应旧版 TwoSideRenderer 的 ±30°、ThreeSideRenderer 的 0°/±60°,不创建随机状态不同的重复发射器。effect_faces_test.gd 验证面数、角度、材质,以及两个相机方向下的 Metal 投影宽度。拉伸、复杂挂接与各技能实景仍需验收,不能把有限投影测试当作全技能像素一致。
  • 动作缓存:不可变 GR2 解码缓存上限 32 条/64MiB section 数据;MSA 元数据缓存上限 128 条,按路径/修改时间失效。各角色时间、事件游标与姿态独立,清空缓存不破坏活动动作;失败不缓存。删除 reload 中同一 MSA 的第二次解析。专项测试两角色三种动作为 GR2 17 hits/3 misses、MSA 17 hits/3 misses。GR2 上限不含解析对象开销及活动角色仍持有的淘汰条目;MSA 为条数上限,不是总内存硬上限。
  • 水面移动:删除按顶点空间位置变化的正弦波,改为旧版整片共享垂直位移;初始 300ms 静止,随后 1000–3000ms 线性过渡,目标在随机 0..−15cm 与零之间切换。water_motion_test.cpp 在 ASan/UBSan 下验证端点、时长及长帧不越界;随机时序未与 Windows 同步,不宣称逐帧一致。远处水面关混合/雾过渡仍未实现。
  • 真实重连历史证据build/rendering/reconnect-20260909/live-smoke-report.json PASS24.009 秒、mutations=false、reconnect=true,两次进入游戏并重建主角。该实包早于最新坐标/多面/水面修改,不可当作当前最终包的联网验收;不是断网故障或长期重连压力测试。

回归证据与边界

  • 扩展重建、debug dylib 同步签名、选角测试通过;新增测试纳入 script/rendering_batch_test.sh
  • 最新完整回归 39 项通过29 headless + 10 Metal):build/rendering/batch-20260910-213214-9037/,汇总 followup-final-batch-20260910.log。包含目标、坐标、缓存、多面、LIE 以及既有模型/材质/技能回归。
  • 离线场景报告 build/rendering/scenarios-20260910/report.json failures=[],覆盖真实 A1→C1、1/8/16 个额外角色 CPU/GPU、三分辨率布局;仅是短窗口帧间隔,不是冷启动/长期性能证明。本轮进程诊断可能与此轮场景采样时间相邻,性能结论以随后隔离复测为准。
  • 释放仍未关闭particle-exit-primed.log 证明提前调用 get_rid 再配置仍有三个泄漏,未作为生产修复。进一步按 gravity/scale/shape、保留若干帧后释放、恢复默认形状等情况拆分诊断;只认可成功标记与退出后的完整日志,外部 --script 探针没有执行标记,不能计为测试通过。

仍未完成

  1. 实包粒子 shader RID 泄漏的真正修复及干净退出门禁。
  2. 拉伸 billboard、径向与出生参数时间曲线、随机等幅正反转、钓鱼落点、准确目标包围球中心、远端完整事件、更多混合模式,以及四职业多等级真实施法画面对照。
  3. 光照/阴影/雾/地形/建筑/植被和远处水面,与 40250 同资源/坐标/相机/动作/环境的受控像素对照;缺少这些成对帧,不能靠任意调亮验收。SpeedTree 仍是近似模型,需要授权明确且可用的几何转换或替代资源。
  4. 冷加载、首次材质/动画编译、全高级装备/混合动作/坡地/更多 NPC 验收,长期多角色帧时间及完整交互/断网恢复/重复切图压力。
  5. Intel Mac、正式签名/公证及全流程发布验收。当前代码及旧批次改动均未提交。

本次最终实包与隔离复测

  • 新包 build/export-rendering-20260910/mtgodot-poc.app,保留所有旧包;universal release、ad-hoccodesign --verify --deep --strict 通过。dylib SHA-256 b4d8fc1f0d4aba0183531769c409545e8639842fc734563cacb6233e4c728fdbPCK d88fb74b9db9d8ae9a0a4caa47de40c69d350864dd97046c454b82079e39aa7d
  • build/rendering/package-20260910/report.json / game.png 完成包内离线进场,client.log 有 PKGRENDER PASS退出后仍有三个 shader RID 泄漏,故 package_render_test.sh 正确返回失败。这不是发布验收通过。
  • 进一步最小实验 particle-exit-20260910-{gravity,scale,shape,held,restored}.log:仅改重力、仅改缩放退出干净;设置发射形状后泄漏;持有五帧后释放、恢复默认形状后释放均仍泄漏。范围已缩小到形状配置触发的粒子 shader 路径,具体引擎内部根因未确认。无效清理方案未加入生产。
  • 所有构建、实包诊断结束后独立重跑 scenarios-20260910-isolated/report.jsonfailures=[]。16 个额外角色 CPU median/P95/max = 16.669/16.932/17.051msGPU = 16.704/16.965/17.133msCPU 八角色组仍有 23.809ms 最大帧。每组仅 90 个受 VSync 影响的窗口帧间隔,包含两次动作切换;不能据此保证全部冷加载或长期无卡顿,也不能将所有改善归因于单一缓存改动。

2026-09-11:干净退出构建路径、随机旋转与目标中心

粒子退出警告:新 arm64 Release 包通过,官方模板仍复现

  • 独立工程 test/rendering/particle-leak/ 不包含客户端扩展、autoload、地图、网络或特效播放器。八个粒子材质切换三种发射形状,WeakRef 确认存活数为零,最后执行 RenderingServer.force_sync。官方 4.7.1 Release 模板仍报告三个 Shader RID 泄漏;官方 Debug 模板退出干净。日志 particle-isolated-release.log / particle-isolated-debug.log
  • 从上游固定提交 a13da4feb8d8aefc283c3763d33a2f170a18d541 构建 arm64 Releasedebug_symbols=yes optimize=speed lto=none vulkan=no(Metal 可用)。加入计数日志的诊断构建干净退出,移除全部诊断修改、恢复原版源码后也干净退出,日志 particle-engine-baseline.log。不能据此断言某一个编译选项或某行上游源码就是根因;已确认的是当前安装模板与这条构建路径的行为差异。
  • 新增 script/build_macos_release_engine.sh 固定源码提交并拒绝带修改的引擎工作树,不替换系统模板。build-macos-client.sh 支持显式 MT_MAC_ENGINE 并验证架构,替换包内引擎后重新签名。示例:先运行 bash script/build_macos_release_engine.sh arm64,再以 MT_MAC_ARCHES=arm64MT_MAC_ENGINE="$PWD/build/godot-particle-diagnostic/bin/godot.macos.template_release.arm64" 和独立 MT_MAC_OUTPUT_DIR 调用 bash build-macos-client.sh release
  • 新产物 build/export-rendering-clean-exit-20260911/mtgodot-poc.appApple Silicon/arm64,实际 release=true、debug=false、editor=false;不是以 Debug 模板替代 Release。ad-hoc 签名 deep/strict 通过,保留所有旧包。
  • script/package_render_test.sh 连续两次通过:build/rendering/clean-exit-20260911-1/client.logclean-exit-20260911-2/client.log。包内地图、模型、DDS、三种专用特效材质正常装配,退出完整日志无 shader/RID 错误。没有跳过粒子或过滤警告。
  • 关闭范围:这条新 arm64 构建路径的整场景退出门禁已通过;安装的官方 Release 模板仍未修复。默认不指定 MT_MAC_ENGINE 仍使用官方模板,不应发布未经退出门禁检查的包。Intel/universal 自编译模板、其他图形后端及正式签名/公证尚未验证。

特效修复与回归

  • 随机等幅旋转:依据 ParticleSystemData.cpp 的 RANDOM_DIRECTION,在出生时选择正/反方向且保持完整角速度;去掉仅正向随机减速。绘制 shader 使用粒子年龄与稳定种子,保留初始角度,不在 process material 重复积分。覆盖普通/Y/水平/多面 billboard 路由;effect_random_rotation_test.gd 包含两个符号种子的 Metal 镜像画面负控。
  • 目标中心:GR2 解析保留 BoneBindings 的 OBBMin/OBBMax(验证 float3 与有限范围),通过主体骨骼 world pose 变换八个角点得到包围盒中心,首次查询后缓存,模型 reload 时失效;不合并独立发型、武器、场景特效或 LOD ghost。fly_target_bounds_test.gd 覆盖八种角色的 CPU/GPU 一致性、远处外接网格不改变中心、后续动作不移动缓存中心、角色整体位移正确传播。缺少有效 OBB/pose 的资源仍走显式旧近似回退;首次建球时机、所有装备/NPC 和 Windows 实际目标帧还需进一步对照。
  • 43 项回归通过build/rendering/batch-20260911-062535-40433/31 headless + 12 Metal),汇总 clean-exit-final-batch.log。原有目标特效、坐标、动画缓存、多面、LIE、水面及模型检查保留。未提交 Git,原有工作树修改保留。

性能与仍未完成的内容

  • rendering_scenario_test.gd 新增 MT_RENDER_SAMPLE_FRAMES90..36000),每 30 帧切换动作,记录同步模型构建耗时、群体动作切换耗时、帧间隔、Godot static memory 与动画缓存。模型构建记录不是清空 OS/驱动缓存后的“真正冷启动”;static memory 也不是进程 RSS 或显存。
  • 拉伸 billboard、径向/出生参数运动曲线、钓鱼水面落点及其完整动作事件、更多混合方式、远端完整事件和四职业真实施法仍未完成。
  • 光照/阴影/雾/地形/建筑/植被、远处水面与 40250 同条件配对画面仍未完成。树木仍是代理几何,缺少授权明确的完整转换资源链;未用任意调色代替受控验收。
  • 冷启动/首次 shader 编译、全装备/坡地/NPC、长期联网重连切图和多角色压力、Intel 及发布验收仍待完成;不能把本次退出通过等同于渲染全部完成。

持续采样与最终包补充

  • build/rendering/soak-20260911/report.json309.124 秒,CPU/GPU × 1/8/16 个额外角色,每组 3000 帧、99 次群体动作切换,共 18000 帧/594 次,功能 failures=[]。各组帧间隔最大值依次为 18.458、27.558、17.846、18.025、18.093、18.218msCPU 八角色动作切换耗时最大 23.262ms,仍是明确长帧。各组 Godot static memory 增量约 0.070.14MB,包含测试数组和缓存,不是 RSS/显存或长期无泄漏证明。同步模型 build 最大 7.36ms,OS/资源缓存未清空。
  • 在这次采样结束后,进一步把每次 reload 的骨骼匹配诊断扫描和三类普通日志改为 MTGODOT_ANIM_DIAGNOSTICS=1 才执行;解析失败、错误与警告完全保留。旧五分钟样本是优化前的数据,不用于冒称优化后的长期性能。animation-diagnostics-enabled.log 验证显式开启仍输出诊断并通过缓存测试。
  • 最终重新构建并签名同名 arm64 测试包;43 项回归再次通过,日志目录 build/rendering/batch-20260911-063306-41283/build/rendering/clean-exit-20260911-final/client.log 再次通过完整包内进场与干净退出门禁。
  • 最终包 SHA-256engine b930ded944fd51ce911e1958289f9bc46c51cad17aa62ef30ca61c69330fb50adylib 6f60298bdd9e45c5047e9327de24546506a0b1ba90feb8db40b82e1ff4416b3dPCK e3e38b68b12b670ce44687870785ae519c90ff283e56112190a597b594a2b779
  • 日志优化后的短场景复查 build/rendering/scenarios-quiet-20260911/report.json failures=[],仍覆盖 CPU/GPU、多角色、三分辨率和 A1→C1;只有每组 90 帧,不能替代优化后重新进行的持续或冷启动性能验收。

首个联网内测版开发入口

联网闭环、基础战斗、首测地图与稳定性门禁的文件级任务、接口设计、状态机、测试证据和执行命令见 首个 Mac 联网内测版:开发实施文档。该文档中的新增接口与脚本为待实现设计,不代表已经完成验收;树木及其他渲染完成状态仍以本路线的实际测试记录为准。

2026-09-11:真实静态树木模型接入

已完成(更新“树木仍为代理”的历史状态)

  • 在同工作区 metin2-godot-main/generated/content-v1/40250-full-unpacked-final 找到原始 SpeedTreeRT 提取产物及来源清单。87 个 SPT 原文件 SHA-256、87 个 GLB SHA-256 全部匹配当前资源;不是依名称套用其他树种,也不是新生成的近似几何。
  • script/stage_tree_geometry.mjs 校验来源、转换坐标约定、节点结构、内嵌贴图和双向哈希后,放入 assets/TreeGeometry(约 59MB)。Mac 不运行或分发 SpeedTree DLL。
  • get_tree_mesh 优先读取经校验的 GLB,保留真实枝干/叶状枝/叶片、UV、原始树高和树皮/composite 贴图。顶点已经为米/Y-up,不再套代理的统一 12m 高度或二次坐标转换。继续按 treefile 共享 ArrayMesh/MultiMesh,保留原 AreaData 位置和原烘焙阴影策略。
  • 叶片保留双面 alpha scissor0.3);观察 build/rendering/native-trees-metal/beech-0.png / beech-90.png 的两个角度,树冠与枝干可见,没有把整张 atlas 画成不透明方块。
  • 原代理代码仅为缺失/损坏资源的诊断回退:打印警告、mesh/instance 标记 tree_geometry=proxy,地图报告新增 native_trees_placedproxy_trees_placed。包内验收要求真实树数大于零且代理数为零,回退不能被当成完成。

验证与覆盖

  • native_tree_test.gd 对87树种逐项核对哈希、GLB装载、surface数量、纹理、透明裁剪、双面材质及有限包围盒;另对榉树做两个机位的 Metal 轮廓检查并保存截图。
  • 六图实际加载:A1=368、A3=389、B1=317、B3=431、C1=296、C3=291,共 2,092 棵真实树,代理回退 0。各图已有少量建筑模型缺失不是树木缺失,本轮未改变它们。
  • 全量回归 45 项通过32 headless + 13 Metal),build/rendering/batch-20260911-134729-54151/,汇总 native-tree-batch.log。原角色、技能、水面、缓存和 GPU 检查保留。
  • script/audit_native_trees.mjs 扫描当前资源:118 个 SPT 中87个有原生导出;PRT引用84种,其中实际静态 AreaData 放置81种,missing_placed=[]。报告 build/rendering/native-tree-coverage.json
  • 新包 build/export-native-trees-20260911/mtgodot-poc.appApple Silicon/arm64 Release,使用上一节验证过的自编译引擎。codesign deep/strict 通过;build/rendering/native-trees-package/client.log 的完整进场/退出门禁通过,包内 A1 native=368、proxy=0,没有新增 shader RID 警告。旧包保留,未提交 Git。

树木相关剩余边界

  • 本轮完成的是真实静态模型;原版 SpeedTree 的逐叶相机朝向、风场/枝叶动画和原生 LOD 切换仍未恢复,不能宣称完整 SpeedTree 视觉行为已一致。
  • Zone/ymir work/zone/b/tree 中31个旧 SPT 未转换,当前 PRT 未引用这些路径,不借相似名字冒充匹配。一个未被静态 AreaData 使用的旧 PRT 指向不存在的 christmastree.spt;真实季节变体 christmastree1/2/3.spt 已转换,活动树生成/等级特效属于另一个流程。
  • 六图以外的逐地图 GPU 画面、与40250同条件像素对照,以及更大规模树木长期性能仍待验收。