Files
metin2-client/extension/src/metin2_model.h
T
Claude eeffa2a730 M2.5: ShaderMaterial + texture resolution + fog; multi-skeleton verified
- m2_material: Metin2-style ShaderMaterial. blend_mix shader (opaque /
  alpha-blend / alpha-test-cutout via `mode` uniform) + blend_add shader
  (additive glow). modulate(tex, COORD) * COLOR, diffuse_lambert.
- Metin2Model: material_mode prop ("metin2" ShaderMaterial | "standard");
  _resolve_texture does filename-convention + case-insensitive dir scan of
  *.dds (face/hair keyword slots, stem match, fallback); set_surface_texture
  override; guess_blend heuristic (hair->cutout, cape->alpha, effect->add).
  Texture cache. Fixed opaque path to NOT alpha-discard (was hiding bodies).
- harness: AABB-based camera framing, engine depth fog (MTGODOT_FOG=1),
  MTGODOT_ANIM_T fixed-pose capture (needs explicit anim reload first),
  MTGODOT_AUTOSHOT batch screenshot.

Verified: warrior 5/5 surfaces textured under the shader (t=9 dance pose,
lit, fogged). assassin 17/17 textured via dir scan. shaman_lord (gr2 v7)
renders. Screenshots test/golden/m25-*.png, m2c-t*.png.

Gap (M2.5 gate not closed): libgr2 POD API exposes no material/texture-name/
blend data, so multi-slot -> texture/blend mapping is heuristic only. Needs
a libgr2 material API (gr2_material.cpp walking the type tree) — same gap as
xrender-poc M1 T5. Documented in docs/GODOT-POC-PLAN.md §M2.5 T2.5.6.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaHYEY9rwLWt21PULiYjeJ
2026-08-29 09:35:01 +09:00

102 lines
3.0 KiB
C++

#pragma once
#include <godot_cpp/classes/image_texture.hpp>
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/templates/hash_map.hpp>
#include <godot_cpp/variant/packed_string_array.hpp>
#include <godot_cpp/variant/string.hpp>
#include <gr2/gr2.h>
#include <memory>
#include <optional>
namespace godot {
class Skeleton3D;
class MeshInstance3D;
class ImageTexture;
} // namespace godot
namespace mtgodot {
// Loads a Metin2 .gr2 and builds a Godot subtree:
// Metin2Model (transform = Z-up/cm -> Y-up/m conversion)
// └─ Skeleton3D (rest pose from gr2 bones)
// └─ MeshInstance3D (ArrayMesh, one surface per gr2 mesh; skinned)
//
// M1: static bind-pose render. M2's Metin2AnimPlayer drives the Skeleton3D.
class Metin2Model : public godot::Node3D {
GDCLASS(Metin2Model, godot::Node3D)
public:
Metin2Model();
~Metin2Model() override;
void _ready() override;
// --- inspector properties ---
void set_gr2_path(const godot::String &p);
godot::String get_gr2_path() const { return gr2_path; }
void set_texture_dir(const godot::String &p);
godot::String get_texture_dir() const { return texture_dir; }
void set_unit_scale(double s);
double get_unit_scale() const { return unit_scale; }
void set_flip_z(bool v);
bool get_flip_z() const { return flip_z; }
void set_flip_winding(bool v);
bool get_flip_winding() const { return flip_winding; }
void set_material_mode(const godot::String &m);
godot::String get_material_mode() const { return material_mode; }
// Explicit per-surface texture path override (index -> absolute .dds path).
void set_surface_texture(int surface, const godot::String &path);
// Rebuild the subtree from the current properties.
void reload();
// One-line summary (bones / meshes / verts / bounds).
godot::String get_info() const;
// --- C++ accessors for Metin2AnimPlayer (same extension) ---
const gr2::Skeleton *gr2_skeleton() const;
godot::Skeleton3D *skeleton_node() const { return skel; }
// libgr2 sanity probe (kept from M0').
godot::String probe_gr2(const godot::String &path) const;
protected:
static void _bind_methods();
private:
godot::String gr2_path;
godot::String texture_dir;
double unit_scale = 0.01;
bool flip_z = false;
bool flip_winding = false;
godot::String material_mode = "metin2"; // "metin2" (ShaderMaterial) | "standard"
godot::PackedStringArray surface_tex_override;
std::shared_ptr<std::optional<gr2::File>> file; // shared_ptr so accessor stays valid
godot::Skeleton3D *skel = nullptr;
godot::MeshInstance3D *mi = nullptr;
godot::String last_info;
godot::HashMap<godot::String, godot::Ref<godot::ImageTexture>> tex_cache;
void _clear_children();
void _apply_materials();
godot::String _guess_texture_dir() const;
godot::Ref<godot::ImageTexture> _load_dds(const godot::String &path);
godot::Ref<godot::ImageTexture> _resolve_texture(const godot::String &dir,
const godot::String &stem, const godot::String &surface_name,
const godot::PackedStringArray &dds_files);
};
} // namespace mtgodot