scopedvr-map-editor/tools/probe_d5_render.gd

97 lines
3 KiB
GDScript

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