extends SceneTree const PythonUISurface = preload("res://python_ui_surface.gd") const Python3DSurface = preload("res://python_3d_surface.gd") var ui: Control var world: Node3D func _initialize() -> void: call_deferred("run") func _py(source: String) -> bool: return Metin2PythonHost.run_line(source) == "" func _pump_until(seconds: float, condition: Callable) -> bool: var deadline := Time.get_ticks_msec() + int(seconds * 1000.0) while Time.get_ticks_msec() < deadline: if condition.call(): return true await process_frame return condition.call() func run() -> void: root.size = Vector2i(1024, 768) world = Python3DSurface.new() world.name = "Python3DSurface" ui = PythonUISurface.new() ui.name = "PythonUISurface" root.add_child(ui) root.add_child(world) var err: String = ui.run_app() if err != "": print("ERROR: run_app: ", err) quit(1) return await _pump_until(15, func(): return _py( "import __main__, os, networkModule, introLogin, introSelect, game, player, background, chr, chrmgr\n" + "__main__._stream = [o for o in __import__('gc').get_objects() if isinstance(o, networkModule.MainStream)][0]" )) await _pump_until(20, func(): return _py( "assert isinstance(_stream.curPhaseWindow, introLogin.LoginWindow)" )) var host = OS.get_environment("MT_LIVE_HOST") var login = OS.get_environment("MT_LIVE_LOGIN") var pwd = OS.get_environment("MT_LIVE_PASSWORD") var auth_port = int(OS.get_environment("MT_LIVE_AUTH_PORT")) var game_port = int(OS.get_environment("MT_LIVE_GAME_PORT")) _py("_stream.SetConnectInfo('%s', %d, '%s', %d)\n" % [host, game_port, host, auth_port] + "_stream.curPhaseWindow.Connect('%s', '%s')" % [login, pwd]) await _pump_until(30, func(): return _py( "assert isinstance(_stream.curPhaseWindow, introSelect.SelectCharacterWindow)" )) _py("_stream.curPhaseWindow.SelectSlot(0)\n_stream.curPhaseWindow.StartGame()") await _pump_until(30, func(): return _py( "assert isinstance(_stream.curPhaseWindow, game.GameWindow) and _stream.curPhaseWindow.IsShow()" )) for i in 60: await process_frame print("=== CAMERA WORLD & HEIGHT PROFILE ===") var draws: Array = Metin2PythonHost.render3d_draws() if not draws.is_empty(): var vm: PackedFloat32Array = draws[0]["view"] var cam_inv: Transform3D = Python3DSurface.d3d_transform(vm).affine_inverse() print("Camera world origin: ", cam_inv.origin) print("Camera world basis: ", cam_inv.basis) print("Camera looking direction: ", -cam_inv.basis.z) for y in range(68200, 66200, -100): var bg_h = Metin2PythonHost.evaluate("str(background.GetHeight(59700.0, %f))" % float(y)) var terrain_h = float(world.terrain.call("sample_height", 597.0, float(y) / 100.0)) * 100.0 if world.terrain else 0.0 print("Profile y=%d: bg_h=%s terrain_h=%.2f" % [y, bg_h, terrain_h]) print("Total draws: ", draws.size()) for d in draws: var tex: String = d.get("texture0", "") if tex.contains("warrior") or tex.contains("stray_dog") or tex.contains("goods"): var wm: PackedFloat32Array = d["world"] var vm: PackedFloat32Array = d["view"] var xform: Transform3D = Python3DSurface.d3d_transform(wm) var cam_xform: Transform3D = Python3DSurface.d3d_transform(Python3DSurface.multiply(wm, vm)) var min_z := 1e9 var max_z := -1e9 var min_local_z := 1e9 var max_local_z := -1e9 for pos in d["positions"]: min_local_z = minf(min_local_z, pos.z) max_local_z = maxf(max_local_z, pos.z) var wp: Vector3 = xform * pos min_z = minf(min_z, wp.z) max_z = maxf(max_z, wp.z) # Map position: (xform.origin.x, -xform.origin.y) var map_x := xform.origin.x var map_y := -xform.origin.y var actor_z := xform.origin.z var bg_h = Metin2PythonHost.evaluate("str(background.GetHeight(%f, %f))" % [map_x, map_y]) var terrain_h = float(world.terrain.call("sample_height", map_x / 100.0, map_y / 100.0)) * 100.0 if world.terrain else 0.0 print("Actor [%s]:" % tex.get_file()) print(" World origin: ", xform.origin, " (map_x=%.1f, map_y=%.1f, origin_z=%.1f)" % [map_x, map_y, actor_z]) print(" Local vert Z range: [%.2f, %.2f]" % [min_local_z, max_local_z]) print(" World vert Z range: [%.2f, %.2f]" % [min_z, max_z]) print(" Background Height: %s, Terrain Height: %.2f" % [bg_h, terrain_h]) print(" Vert_Z min vs Height: diff = %.2f cm" % (min_z - float(bg_h))) print(" Origin_Z vs Height: diff = %.2f cm" % (actor_z - float(bg_h))) print(" Camera-space origin: ", cam_xform.origin) if tex.contains("stray_dog"): print("--- DOG TERRAIN DETAIL ---") var from_map := Transform3D(Basis(Vector3(100, 0, 0), Vector3(0, 0, 100), Vector3(0, -100, 0)), Vector3.ZERO) var dog_world := xform.origin # (59700, -66400, 19851.5) print("Dog 40250 world: ", dog_world) for child in world.terrain.find_children("*", "MeshInstance3D", true, false): var mi := child as MeshInstance3D if mi.mesh is ArrayMesh: var am := mi.mesh as ArrayMesh var arrays := am.surface_get_arrays(0) var verts: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX] for v in verts: var world_v: Vector3 = from_map * (mi.transform * v) if abs(world_v.x - dog_world.x) < 300.0 and abs(world_v.y - dog_world.y) < 300.0: print("Terrain local vert: ", v, " -> mi.xform*v: ", mi.transform * v, " -> from_map: ", world_v) print("Diff Z (terrain world Z - dog world Z): ", world_v.z - dog_world.z) break quit(0)