feat: complete native client rendering parity updates

This commit is contained in:
shen
2026-09-27 19:44:41 -07:00
parent 9a9dc6d224
commit 25dd65f2ce
99 changed files with 21121 additions and 921 deletions
+140
View File
@@ -0,0 +1,140 @@
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)