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