#pragma once // m2_material — Metin2-style forward material as a Godot ShaderMaterial. // // M2.5 infrastructure. The gr2 fixed-function texture-stage / blend data is not // yet exposed by libgr2's POD API (see docs/GODOT-POC-PLAN.md §M2.5 gap list), // so callers pick the blend mode heuristically for now. The shader itself // already covers the common Metin2 cases: modulate(tex, vertex_color) + light, // alpha-test, alpha-blend, additive. #include namespace godot { class ShaderMaterial; class Texture2D; class ImageTexture; } // namespace godot namespace mtgodot { enum class BlendMode { Opaque, // blend_mix, ALPHA=1, no discard Alpha, // blend_mix, sorted, uses texture alpha AlphaTest, // blend_mix, discard below alpha_scissor (cutout) Add, // blend_add (glow / effects) }; struct MaterialDesc { godot::Ref albedo; // null -> flat white BlendMode blend = BlendMode::Opaque; float alpha_scissor = 0.5f; bool two_sided = true; // winding not yet verified per-model // GPU skinning: LBS with FULL per-bone affine matrices in the vertex shader // (no Skeleton3D -> no quaternion orthonormalization -> shear preserved). // `bones_tex` is RGBAF, size 3 x bone_count; columns 0..2 = the 3 columns of // the row-vector skin matrix (see metin2_model.cpp::gpu_skin). bool skinned = false; godot::Ref bones_tex; // Sphere-map specular — port of CGrannyMaterial::__ApplySpecularRenderState // (EterGrnLib/Material.cpp:305). Opaque armor gets a metallic highlight from a // camera-space reflection-vector lookup into a shared sphere map, added on top // of the lit diffuse: // out.rgb = tex.rgb*modulate + (tex.a * spec_power) * sphere(reflect_uv) // Client enables this per skin-part only when the equipped body-armor item's // item_proto `bSpecular > 0` (power = bSpecular/100); the base body is flat. // Dormant here until an equipment layer feeds a power: spec_power <= 0 -> // the shader branch is skipped and output is byte-identical to before. godot::Ref spec_map; // shared sphere map; null -> disabled float spec_power = 0.0f; // fSpecularPower (D3DRS_TEXTUREFACTOR.a); 0 = off }; godot::Ref make_material(const MaterialDesc &d); // Drop the extension-owned shader cache before GDExtension teardown. Materials // still alive in the scene keep their own Ref, so this only removes the static // lifetime that otherwise survives Godot's leak check. void cleanup_material_shaders(); } // namespace mtgodot