Fix cull mode + isolate head-collapse to a libgr2 curve bug

Rendering fixes (real):
- m2_material shaders: cull_disabled -> cull_back (inside-of-mesh faces were
  z-winning and darkening/hiding the head under the ShaderMaterial path).
- drop `* COLOR` from the shader (meshes carry no ARRAY_COLOR).

Animation: convert gr2 global poses to Godot *local* bone poses and set via
set_bone_pose (was set_bone_global_pose). MTGODOT_VERIFY=1 confirms
get_bone_global_pose(i) == conv(world[i]) for all 75 bones (<=2.6e-5).

Head/upper-armor collapse on dance_1 is NOT a Godot-route bug:
- reproduced identically in xrender-poc's bgfx demo at the same anim/t
- reproduced by the new MTGODOT_CPUSKIN=1 reference path (same LBS math as
  xrender's validated skin_mesh)
- `general/wait` / `run` render correctly in all three
=> upstream libgr2 Curve::eval (degree-2 quaternion B-spline) vs Granny.
Demo default animation switched to general/wait; documented in MIDREVIEW §4
and README. Debug scaffolding gated behind MTGODOT_VERIFY / MTGODOT_CPUSKIN.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaHYEY9rwLWt21PULiYjeJ
This commit is contained in:
Claude
2026-08-29 10:47:01 +09:00
parent 30d6a81476
commit f4917a2b3b
15 changed files with 234 additions and 15 deletions
+7 -1
View File
@@ -106,8 +106,11 @@ func _load_content() -> void:
_assets_root.path_join("PC/ymir work/pc/warrior/warrior_cheongrin.gr2"))
var tex_dir := _env("MTGODOT_TEXDIR",
_assets_root.path_join("PC/ymir work/pc/warrior"))
# Default to an idle: dance_1 (and other emotes) hit a libgr2 curve-decode
# bug that collapses the head/neck — reproduced in xrender-poc's bgfx demo
# too, i.e. upstream, not a Godot-route issue. See docs/MIDREVIEW.md.
var anim_path := _env("MTGODOT_ANIM",
_assets_root.path_join("PC/ymir work/pc/warrior/action/dance_1.gr2"))
_assets_root.path_join("PC/ymir work/pc/warrior/general/wait.gr2"))
_model = ClassDB.instantiate("Metin2Model")
_model.name = "Metin2Model"
@@ -144,6 +147,9 @@ func _frame_model() -> void:
_target = Vector3(aabb.get_center().x, aabb.position.y + h * 0.5, aabb.get_center().z)
_dist = h * 3.2
_pitch = 0.12
var yenv := OS.get_environment("MTGODOT_YAW")
if yenv != "":
_yaw = float(yenv)
print("[mtgodot] frame: aabb=", aabb, " target=", _target, " dist=", _dist)
_update_camera()