M1+M2: gr2 -> Skeleton3D/ArrayMesh/Skin + libgr2-driven animation

- gr2_bridge: gr2 Mat4 (row-major) -> Godot Transform3D (4x4 transpose);
  build_skeleton (rest from local_transform, initial_placement folded into
  roots), build_skin (bind i -> bone i, pose = inverse_world), build_mesh
  (surface per gr2 mesh, ARRAY_BONES = skeleton idx via bone_bindings,
  rigid meshes bound to bone_bindings[0]).
- dxt: DDS DXT1/3/5 decoder ported from xrender-poc.
- Metin2Model: load_gr2 -> Skeleton3D + MeshInstance3D + StandardMaterial3D
  (runtime DDS albedo, face/body split by surface name). Z-up cm -> Y-up m
  conversion on the node transform (unit_scale, flip_z, flip_winding props).
- Metin2AnimPlayer: _process samples a (separate) anim .gr2 with
  gr2::sample_pose(model.skeleton, anim, t) -> set_bone_global_pose per bone;
  Godot GPU-skins via skin_matrix = global_pose(i) * bind_pose(i) == gr2
  deformer matrix. selfcheck() NaN-scans all anims.

Verified: warrior_cheongrin renders (75 bones, 5 surfaces, 3324 verts,
textured, upright). dance01 (dur 28.3s, 74 tracks) plays, 0 NaN over 25
samples, distinct poses at t=2/9/16/23. Screenshots in test/golden/.

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 09:23:29 +09:00
parent 67109990ec
commit 27e39993f7
121 changed files with 1058 additions and 57 deletions
+46
View File
@@ -0,0 +1,46 @@
#pragma once
// gr2_bridge — libgr2 POD views -> Godot scene objects.
//
// Coordinate handling (the one place it lives; see docs/GODOT-POC-PLAN.md §02):
// - gr2 Mat4 is row-major, row-vector math, translation in elements 12..14.
// - A plain 4x4 transpose maps it to a Godot Transform3D (column-vector,
// translation in .origin). gr2_to_godot() does exactly that.
// - Z-up(cm) -> Y-up(m) and optional handedness flip are NOT applied here;
// they go on the Metin2Model node transform (make_conv()).
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/variant/aabb.hpp>
#include <godot_cpp/variant/transform3d.hpp>
#include <gr2/gr2.h>
namespace godot {
class Skeleton3D;
class Skin;
class ArrayMesh;
} // namespace godot
namespace mtgodot {
// 4x4 transpose: gr2 row-major/row-vector -> Godot Transform3D.
godot::Transform3D gr2_to_godot(const gr2::Mat4 &m);
// Z-up cm -> Y-up m (+ optional Z flip for LH->RH content).
godot::Transform3D make_conv(float unit_scale, bool flip_z);
// Build a Skeleton3D (rest pose from bone local transforms; initial_placement
// folded into root bones). Returns nullptr if the skeleton has no bones.
godot::Skeleton3D *build_skeleton(const gr2::Skeleton &sk);
// Skin whose bind list is parallel to the skeleton bones:
// bind i -> bone i, pose = gr2_to_godot(bone[i].inverse_world)
godot::Ref<godot::Skin> build_skin(const gr2::Skeleton &sk);
// One ArrayMesh with a surface per gr2 Mesh that has geometry.
// ARRAY_BONES values are skeleton bone indices (mesh slot -> bone via
// mesh.bone_bindings). Rigid meshes are bound rigidly to bone_bindings[0].
// Fills out_bounds with the untransformed vertex AABB.
godot::Ref<godot::ArrayMesh> build_mesh(const gr2::FileInfo &fi, bool flip_winding,
godot::AABB &out_bounds);
} // namespace mtgodot