From ebe3dd276bc801bf93958647aa3f779188c92318 Mon Sep 17 00:00:00 2001 From: "Hermes (scopedvr-dev)" Date: Mon, 19 Oct 2026 18:06:39 +0000 Subject: [PATCH] D5 (t_e81aed01): Render-Nachweis tools/probe_d5_render.gd (Fels/Kaktus/Stein/Decal platziert) --- tools/probe_d5_render.gd | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 tools/probe_d5_render.gd diff --git a/tools/probe_d5_render.gd b/tools/probe_d5_render.gd new file mode 100644 index 0000000..22677d7 --- /dev/null +++ b/tools/probe_d5_render.gd @@ -0,0 +1,97 @@ +extends SceneTree +## D5-Bau-Nachweis (t_e81aed01): platziert je 1 Natur-Objekt (Fels, Kaktus, Decal) +## ueber die EDITOR-eigene Aufloesung (GlbMeshStore.mesh_fuer — Registry-glb + +## Fusspunkt-Normalisierung) auf einer Bodenplatte und rendert ein PNG. +## Beweist visuell: Fels/Kaktus STEHEN auf dem Boden, das Decal LIEGT FLACH +## (2 cm Lift gegen Z-Fighting). +## +## xvfb-run -a godot4 --rendering-driver opengl3 --path . --script res://tools/probe_d5_render.gd +## Ausgabe: PROBE_D5_RENDER_OK + PNG-Pfad + +const AUSGABE := "user://probe_d5_platzierung.png" +const PLANUNG := [ + {"oid": "rock_003", "pos": Vector3(-3.2, 0.0, 0.0)}, + {"oid": "cactus_008", "pos": Vector3(0.0, 0.0, 0.0)}, + {"oid": "stone_016", "pos": Vector3(2.4, 0.0, 1.2)}, + {"oid": "decal_001", "pos": Vector3(-1.0, 0.02, 3.2)}, +] + +var vp: SubViewport +var kamera: Camera3D +var rahmen := 0 +var fehler: Array = [] + + +func _initialize() -> void: + vp = SubViewport.new() + vp.size = Vector2i(900, 600) + vp.own_world_3d = true + vp.render_target_update_mode = SubViewport.UPDATE_ALWAYS + root.add_child(vp) + + var env := Environment.new() + env.background_mode = Environment.BG_COLOR + env.background_color = Color(0.55, 0.62, 0.72) + env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR + env.ambient_light_color = Color(1, 1, 1) + env.ambient_light_energy = 0.8 + var we := WorldEnvironment.new() + we.environment = env + vp.add_child(we) + + var sonne := DirectionalLight3D.new() + sonne.rotation_degrees = Vector3(-50.0, 40.0, 0.0) + sonne.light_energy = 1.2 + vp.add_child(sonne) + + # Bodenplatte: genau das, worauf Spieler laufen (Terrain-Ersatz) + var boden := MeshInstance3D.new() + var pm := PlaneMesh.new() + pm.size = Vector2(40.0, 40.0) + boden.mesh = pm + var mat := StandardMaterial3D.new() + mat.albedo_color = Color(0.78, 0.68, 0.45) + boden.material_override = mat + vp.add_child(boden) + + for e in PLANUNG: + var oid := str(e["oid"]) + var mesh: Mesh = GlbMeshStore.mesh_fuer(oid) + if mesh == null: + fehler.append("%s: GlbMeshStore liefert kein Mesh" % oid) + continue + var mi := MeshInstance3D.new() + mi.name = "Platz_%s" % oid + mi.mesh = mesh + var pos: Vector3 = e["pos"] + mi.position = pos + vp.add_child(mi) + var ab: AABB = mesh.get_aabb() + print("%-11s pos.y=%.2f mesh-Unterkante=%.3f m Oberkante=%.3f m XZ-Zentrum=(%.3f, %.3f)" % [ + oid, pos.y, pos.y + ab.position.y, pos.y + ab.position.y + ab.size.y, + ab.position.x + ab.size.x * 0.5, ab.position.z + ab.size.z * 0.5]) + + kamera = Camera3D.new() + kamera.current = true + kamera.fov = 60.0 + vp.add_child(kamera) + + +func _process(_delta: float) -> bool: + rahmen += 1 + if rahmen < 4: + return false + if rahmen == 4: + kamera.look_at_from_position(Vector3(2.0, 2.4, 9.0), Vector3(-0.4, 0.8, 0.8), Vector3.UP) + return false + if rahmen == 8: + var pfad := ProjectSettings.globalize_path(AUSGABE) + var img := vp.get_texture().get_image() + img.save_png(pfad) + print("Bild: %s (%dx%d)" % [pfad, img.get_width(), img.get_height()]) + for f in fehler: + print(" FEHLER: %s" % f) + print("PROBE_D5_RENDER_%s" % ("FAIL" if not fehler.is_empty() else "OK")) + quit(0) + return true + return false