- library/objects.json: Editor-Kopie (note behalten), 83 Objekte. - library/thumbs: 27 neue Thumbs (echte GLB-Meshes), Alt-Thumbs unveraendert. - tests/probe_d2_barriers_render.gd: Map-Builder-Beweis — 3 Objekte ueber map_view_builder platziert, PNG gerendert, Fußpunkt/Groesse gemessen.
173 lines
5.9 KiB
GDScript
173 lines
5.9 KiB
GDScript
extends SceneTree
|
|
## D2-Renderprobe (Karte t_33250710): Map-Builder-Beweis — drei Absperrungen/
|
|
## Hindernisse aus dem Military-Pack werden über den ECHTEN Editor-Viewport-Bauer
|
|
## (src/viewport/map_view_builder.gd) auf flachem Boden platziert, perspektivisch
|
|
## gerendert und als PNG gespeichert (visuelle Prüfung: stehen die Objekte auf
|
|
## dem Boden?). Zusätzlich wird je Objekt gemessen: Unterkante vs. Bodenhöhe,
|
|
## Mesh-Größe vs. collision.size.
|
|
##
|
|
## BRAUCHT Rendering (nicht --headless):
|
|
## xvfb-run -a /opt/data/home/bin/godot4 --path . --rendering-driver opengl3 \
|
|
## -s res://tests/probe_d2_barriers_render.gd
|
|
## Ausgabe: PROBE_D2_RENDER_OK / PROBE_D2_RENDER_FAIL
|
|
|
|
const OUT_DIR := "/opt/data/kanban/workspaces/t_33250710/artifacts"
|
|
const OBJEKTE := [
|
|
{"id": "barrier_001", "pos": [-7.0, 0.0, 0.0], "rot_y_deg": 0.0, "scale": 1.0},
|
|
{"id": "obstacle_004", "pos": [0.0, 0.0, -1.0], "rot_y_deg": 90.0, "scale": 1.0},
|
|
{"id": "obstacle_008", "pos": [8.0, 0.0, 1.0], "rot_y_deg": 45.0, "scale": 1.0},
|
|
]
|
|
const W := 1280
|
|
const H := 720
|
|
|
|
var fehler: Array = []
|
|
|
|
|
|
func _initialize() -> void:
|
|
_run()
|
|
|
|
|
|
func _run() -> void:
|
|
await process_frame
|
|
await process_frame
|
|
DirAccess.make_dir_recursive_absolute(OUT_DIR)
|
|
var manifest: Dictionary = JSON.parse_string(
|
|
FileAccess.get_file_as_string(SvmapIO.MANIFEST_PATH))
|
|
if manifest.is_empty():
|
|
fehler.append("Bibliothek (library/objects.json) nicht ladbar")
|
|
var lib: Dictionary = manifest.get("objects", {})
|
|
print("Bibliothek: %d Objekte" % lib.size())
|
|
|
|
var vp := SubViewport.new()
|
|
vp.size = Vector2i(W, H)
|
|
vp.own_world_3d = true
|
|
vp.render_target_update_mode = SubViewport.UPDATE_ALWAYS
|
|
root.add_child(vp)
|
|
|
|
var builder := Node3D.new()
|
|
builder.set_script(load("res://src/viewport/map_view_builder.gd"))
|
|
vp.add_child(builder)
|
|
builder.call("rebuild", _doku(), manifest)
|
|
|
|
var cam := Camera3D.new()
|
|
vp.add_child(cam)
|
|
cam.position = Vector3(0.0, 7.0, 17.0)
|
|
cam.look_at(Vector3(0.0, 0.8, 0.0))
|
|
cam.current = true
|
|
|
|
print("fehlende GLBs (Platzhalter): %s" % str(builder.get("fehlende_glbs")))
|
|
for o in OBJEKTE:
|
|
if not lib.has(o["id"]):
|
|
fehler.append("%s fehlt in der Bibliothek" % o["id"])
|
|
continue
|
|
_pruefe(builder, lib[o["id"]], o)
|
|
if not builder.get("fehlende_glbs").is_empty():
|
|
fehler.append("Platzhalter statt GLB: %s" % str(builder.get("fehlende_glbs")))
|
|
|
|
await process_frame
|
|
await process_frame
|
|
await RenderingServer.frame_post_draw
|
|
await process_frame
|
|
var img := vp.get_texture().get_image()
|
|
var pfad := OUT_DIR + "/d2_barriers_map_builder.png"
|
|
img.save_png(pfad)
|
|
print("PROBE_SAVED %s (%dx%d)" % [pfad, img.get_width(), img.get_height()])
|
|
_check_bild(img)
|
|
|
|
for f in fehler:
|
|
print(" FEHLER: %s" % f)
|
|
print("PROBE_D2_RENDER_%s" % ("FAIL" if not fehler.is_empty() else "OK"))
|
|
quit(0 if fehler.is_empty() else 1)
|
|
|
|
|
|
func _pruefe(builder: Node, eintrag: Dictionary, obj: Dictionary) -> void:
|
|
var oid := str(obj["id"])
|
|
var markers: Array = builder.get("object_markers")
|
|
var marker: Node3D = null
|
|
var idx := -1
|
|
for i in OBJEKTE.size():
|
|
if str(OBJEKTE[i]["id"]) == oid and i < markers.size():
|
|
marker = markers[i]
|
|
idx = i
|
|
if marker == null:
|
|
fehler.append("%s: kein Marker im Viewport-Bauer" % oid)
|
|
return
|
|
var box := _mesh_aabb(marker, marker)
|
|
var col: Dictionary = eintrag.get("collision", {})
|
|
var csize: Array = col.get("size", [])
|
|
var dmax := 0.0
|
|
for k in 3:
|
|
dmax = maxf(dmax, absf(box.size[k] - float(csize[k])))
|
|
var echt: bool = bool(marker.get_meta("glb", false))
|
|
print("--- %s (Marker %d): echtes GLB=%s" % [oid, idx, str(echt)])
|
|
print(" Mesh-AABB lokal: pos=%s size=%s" % [str(box.position), str(box.size)])
|
|
print(" Unterkante y=%.3f (Boden %.3f, Marker y=%.3f) | size-Abweichung %.3f m"
|
|
% [box.position.y, 0.0, marker.position.y, dmax])
|
|
if not echt:
|
|
fehler.append("%s: Platzhalter statt GLB im Editor" % oid)
|
|
if absf(box.position.y) > 0.02:
|
|
fehler.append("%s: Unterkante liegt %.3f m neben dem Boden" % [oid, box.position.y])
|
|
if dmax > 0.02:
|
|
fehler.append("%s: Mesh-Größe weicht %.3f m von collision.size ab" % [oid, dmax])
|
|
|
|
|
|
## Grobe Sichtprüfung im Bild: es muss Struktur (helle Pixel) geben und das
|
|
## Objektband in der Bildmitte muss sich vom Himmel/Untergrund unterscheiden.
|
|
func _check_bild(img: Image) -> void:
|
|
var w := img.get_width()
|
|
var h := img.get_height()
|
|
var hell := 0
|
|
var band := 0
|
|
for y in range(0, h, 2):
|
|
for x in range(0, w, 2):
|
|
var c := img.get_pixel(x, y)
|
|
var lum := (c.r + c.g + c.b) / 3.0
|
|
if lum > 0.55:
|
|
hell += 1
|
|
if y > int(h * 0.35) and y < int(h * 0.85):
|
|
band += 1
|
|
print("Bild: helle Pixel %d, davon im Objektband %d" % [hell, band])
|
|
if hell < 200:
|
|
fehler.append("Renderbild zeigt fast nichts (helle Pixel %d) — Objekte unsichtbar?" % hell)
|
|
if band < 50:
|
|
fehler.append("kein Objekt im erwarteten Bildband (helle Pixel %d)" % band)
|
|
|
|
|
|
func _mesh_aabb(n: Node, bezug: Node = null) -> AABB:
|
|
var box := AABB()
|
|
var erst := true
|
|
var basis: Transform3D = Transform3D.IDENTITY if bezug == null else bezug.global_transform.affine_inverse()
|
|
for k in n.get_children():
|
|
if k is MeshInstance3D and (k as MeshInstance3D).mesh != null:
|
|
var aabb: AABB = (k as MeshInstance3D).mesh.get_aabb()
|
|
aabb = (basis * (k as MeshInstance3D).global_transform) * aabb
|
|
box = aabb if erst else box.merge(aabb)
|
|
erst = false
|
|
var kind := _mesh_aabb(k, bezug)
|
|
if kind.size != Vector3.ZERO:
|
|
box = kind if erst else box.merge(kind)
|
|
erst = false
|
|
return box
|
|
|
|
|
|
## Flaches Bodenfeld (17², 0 m) — Objekte stehen direkt auf y=0.
|
|
func _doku() -> Dictionary:
|
|
var res := 17
|
|
var raw := PackedByteArray()
|
|
raw.resize(res * res * 2)
|
|
return {
|
|
"format_version": 2,
|
|
"info": {"name": "D2 Absperrungen", "author": "Tests", "version": 1},
|
|
"terrain": {
|
|
"type": "heightmap",
|
|
"resolution": res,
|
|
"size_x_m": 64.0,
|
|
"size_z_m": 64.0,
|
|
"min_height_m": 0.0,
|
|
"max_height_m": 4.0,
|
|
"heightmap_r16_b64": Marshalls.raw_to_base64(raw),
|
|
},
|
|
"weather": "mittag",
|
|
"objects": OBJEKTE,
|
|
"spawns": [],
|
|
}
|