extends Node3D ## mtgodot-poc — M0' harness. ## ## Builds the minimal scene the plan asks for (orbit camera + DirectionalLight3D ## with shadows + WorldEnvironment/sky + placeholder cube + an empty Metin2Model), ## then reports whether the GDExtension loaded. ## ## M1 will replace the cube with Metin2Model.load_gr2(...) output. const ORBIT_SPEED := 0.01 const ZOOM_STEP := 0.3 const PITCH_LIMIT := 1.45 var _cam: Camera3D var _yaw := 0.6 var _pitch := 0.45 var _dist := 4.0 var _target := Vector3(0.0, 1.0, 0.0) var _dragging := false func _ready() -> void: _build_environment() _build_sun() _build_camera() _build_placeholder() _probe_extension() print("[mtgodot] M0' harness ready — drag: orbit, wheel: zoom, F2: screenshot") # Batch capture: MTGODOT_AUTOSHOT=/abs/path.png godot --path project var autoshot := OS.get_environment("MTGODOT_AUTOSHOT") if autoshot != "": await get_tree().create_timer(0.8).timeout var img := get_viewport().get_texture().get_image() img.save_png(autoshot) print("[mtgodot] autoshot -> ", autoshot) get_tree().quit() func _build_environment() -> void: var we := WorldEnvironment.new() var env := Environment.new() var sky := Sky.new() sky.sky_material = ProceduralSkyMaterial.new() env.background_mode = Environment.BG_SKY env.sky = sky env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY env.tonemap_mode = Environment.TONE_MAPPER_FILMIC we.environment = env add_child(we) func _build_sun() -> void: var sun := DirectionalLight3D.new() sun.rotation_degrees = Vector3(-45.0, -35.0, 0.0) sun.shadow_enabled = true add_child(sun) func _build_camera() -> void: _cam = Camera3D.new() _cam.current = true _cam.fov = 55.0 add_child(_cam) _update_camera() func _build_placeholder() -> void: var cube := MeshInstance3D.new() cube.name = "Placeholder" cube.mesh = BoxMesh.new() cube.position = _target add_child(cube) func _probe_extension() -> void: if not ClassDB.class_exists("Metin2Model"): push_error("[mtgodot] Metin2Model NOT registered — GDExtension failed to load") return var m := ClassDB.instantiate("Metin2Model") as Node3D m.name = "Metin2Model" add_child(m) print("[mtgodot] Metin2Model registered OK — ", m.call("libgr2_info")) # Optional: point MTGODOT_PROBE_GR2 at a real .gr2 to exercise libgr2 end to end. var probe := OS.get_environment("MTGODOT_PROBE_GR2") if probe != "": print("[mtgodot] probe_gr2(", probe, ") -> ", m.call("probe_gr2", probe)) func _update_camera() -> void: var offset := Vector3( cos(_pitch) * sin(_yaw), sin(_pitch), cos(_pitch) * cos(_yaw)) * _dist _cam.position = _target + offset _cam.look_at(_target, Vector3.UP) func _unhandled_input(event: InputEvent) -> void: if event is InputEventMouseButton: var mb := event as InputEventMouseButton match mb.button_index: MOUSE_BUTTON_LEFT: _dragging = mb.pressed MOUSE_BUTTON_WHEEL_UP: _dist = maxf(0.5, _dist - ZOOM_STEP) _update_camera() MOUSE_BUTTON_WHEEL_DOWN: _dist = minf(50.0, _dist + ZOOM_STEP) _update_camera() elif event is InputEventMouseMotion and _dragging: var mm := event as InputEventMouseMotion _yaw -= mm.relative.x * ORBIT_SPEED _pitch = clampf(_pitch + mm.relative.y * ORBIT_SPEED, -PITCH_LIMIT, PITCH_LIMIT) _update_camera() elif event is InputEventKey: var k := event as InputEventKey if k.pressed and k.keycode == KEY_F2: _screenshot() func _screenshot() -> void: var img := get_viewport().get_texture().get_image() var path := "user://shot_%d.png" % Time.get_ticks_msec() img.save_png(path) print("[mtgodot] screenshot -> ", ProjectSettings.globalize_path(path))