- 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
102 lines
3.0 KiB
C++
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
|