scopedvr-map-editor/tests/probe_k1d_render.gd
Hermes (scopedvr-dev) 40449932b9 K1d t_6c1027c1: Startansicht 80 m über der Kartenmitte (Pos1 bleibt ganze Karte)
fit_to_document setzt die Ansicht jetzt auf START_DISTANCE_M = 80 m über der
Kartenmitte (XZ-Mitte der Map-Bounds, Höhe = Gelände dort), yaw 45°, pitch 35°.
Die Regel ist unabhängig von Kartengröße (256/1024/4096) und Inhaltslage;
leeres Dokument ebenfalls 80 m. Statuszeile: „Ansicht: Kartenmitte, Abstand
80 m (Pos1 = ganze Karte).“ Pos1/Home bleibt fit_to_bounds = ganze Karte
(MAP-Bounds werden weiterhin gemerkt, nicht die Content-Bounds).

Ziel-Y bewusst auf der Geländehöhe: die y-Mitte der Bounds liegt bei neuen
Maps 35 m über dem Boden — damit hätte die Ansicht über den Boden gezielt und
die Objekte stünden am unteren Bildrand.

Suite test_orbit_camera auf die neue Regel umgestellt (50 Checks):
Startabstand/Richtung, 80-m-Regel über Größen und Inhaltslagen, leeres
Dokument, „Pos1 ≠ Startansicht“ (80 m → 1215,6 m, alle 8 Map-Ecken).
Dazu Renderprobe tests/probe_k1d_render.gd (2 PNGs).
2026-10-20 09:33:16 +00:00

126 lines
4.8 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends SceneTree
## RENDER-PROBE Karte t_6c1027c1 (K1d: Startansicht = Kartenmitte, 80 m) — 2 Bilder 1600×900:
## (a) Karte „frisch geladen“ (main._refresh_all → fit_to_document): Blick aus
## 80 m auf die Kartenmitte, Lager im Nahbereich groß im Bild
## (b) Pos1 (Home) → GANZE Karte im Bild (Startansicht ist bewusst NICHT Pos1)
## Ausführen (KEIN --headless, sonst rendert nichts):
## xvfb-run -a /opt/data/home/bin/godot4 --path . --rendering-driver opengl3 -s res://tests/probe_k1d_render.gd
## PNGs landen in /opt/data/scopedvr/work/k1d_bilder/ (Scratch-Ordner stirbt nach der Karte).
## Echte GLB-Modelle: die Probe braucht ein Mesh-Lager als Sibling
## (../scopedvr-client/assets/objekte) — ohne das zeigen die Marker Platzhalter.
const OUT_DIR := "/opt/data/scopedvr/work/k1d_bilder/"
var main: Control
var camera: Camera3D
var subvp: SubViewport
func _initialize() -> void:
_run.call_deferred()
func _shot(name: String) -> void:
await process_frame
await RenderingServer.frame_post_draw
var img := subvp.get_texture().get_image()
var path := OUT_DIR + name
var err := img.save_png(path)
print("PROBE_PNG=%s (%dx%d, err=%d)" % [path, img.get_width(), img.get_height(), err])
func _find_subvp(node: Node) -> SubViewport:
if node is SubViewport:
return node as SubViewport
for c in node.get_children():
var f := _find_subvp(c)
if f != null:
return f
return null
func _key(code: Key) -> void:
var ev := InputEventKey.new()
ev.keycode = code
ev.physical_keycode = code
ev.pressed = true
subvp.push_input(ev)
## Lager um die Kartenmitte: Gebäude/Container/Fässer im Nahbereich (das soll
## die 80-m-Ansicht „groß und greifbar“ zeigen) + ein paar Objekte weiter
## draußen für den Karten-Kontext.
func _lager_doc() -> Dictionary:
var doc: Dictionary = SvmapDocument.create_new("K1d-Beweis", 1024.0, "mittag")
doc["objects"] = [
{"id": "barracks_001", "pos": [-16.0, 0.0, -8.0], "scale": 1.0, "rot_y_deg": 20.0},
{"id": "building_001", "pos": [14.0, 0.0, -14.0], "scale": 1.0, "rot_y_deg": -35.0},
{"id": "container_001", "pos": [3.0, 0.0, 7.0], "scale": 1.0, "rot_y_deg": 90.0},
{"id": "container_004", "pos": [7.5, 0.0, 7.0], "scale": 1.0, "rot_y_deg": 90.0},
{"id": "barrel_001", "pos": [-4.0, 0.0, -2.0], "scale": 1.0, "rot_y_deg": 0.0},
{"id": "barrel_003", "pos": [-3.0, 0.0, -4.0], "scale": 1.0, "rot_y_deg": 0.0},
{"id": "barrel_005", "pos": [-4.6, 0.0, -4.6], "scale": 1.0, "rot_y_deg": 0.0},
{"id": "barrier_001", "pos": [10.0, 0.0, 22.0], "scale": 1.0, "rot_y_deg": 0.0},
{"id": "apc_001", "pos": [22.0, 0.0, 6.0], "scale": 1.0, "rot_y_deg": 140.0},
{"id": "air_defense_001", "pos": [-24.0, 0.0, 16.0], "scale": 1.0, "rot_y_deg": 70.0},
{"id": "container_007", "pos": [-80.0, 0.0, -90.0], "scale": 1.0, "rot_y_deg": 15.0},
{"id": "barracks_001", "pos": [130.0, 0.0, 90.0], "scale": 1.0, "rot_y_deg": -60.0},
]
return doc
func _run() -> void:
var scene: PackedScene = load("res://scenes/main.tscn")
main = scene.instantiate()
root.add_child(main)
await process_frame
await process_frame
camera = main.get("camera")
subvp = _find_subvp(main)
var container: SubViewportContainer = subvp.get_parent()
container.stretch = false
subvp.size = Vector2i(1600, 900)
await process_frame
var doc := _lager_doc()
main.set("doc", doc)
main.call("_refresh_all")
await process_frame
await process_frame
var target: Vector3 = camera.get("target")
print("ZAHLEN (a) target=", target, " distance=", camera.get("distance"),
" yaw=", camera.get("yaw_deg"), " pitch=", camera.get("pitch_deg"))
print("ZAHLEN (a) Kamera-Position=", camera.position,
" Höhe über Kartenmitte=", camera.position.y - target.y)
print("ZAHLEN (a) Status: ", main.status_label.text)
# Wie viele Objekt-Marker liegen wirklich im Bild?
var builder = main.get("view_builder")
var markers: Array = builder.get("object_markers")
var drin := 0
var rect: Rect2 = Rect2(Vector2.ZERO, Vector2(subvp.size))
for m in markers:
if not (m is Node3D) or not is_instance_valid(m):
continue # unbekannte Objekt-ID → kein Marker (Platzhalter-Zweig)
var mp: Vector3 = (m as Node3D).global_position
var p := camera.unproject_position(mp)
if rect.has_point(p) and camera.is_position_in_frustum(mp):
drin += 1
print("ZAHLEN (a) Objekt-Marker im Bild: %d / %d" % [drin, markers.size()])
await _shot("k1d_a_startansicht_80m.png")
_key(KEY_HOME)
await process_frame
await process_frame
print("ZAHLEN (b) nach Pos1: target=", camera.get("target"),
" distance=", camera.get("distance"), " Status: ", main.status_label.text)
var ecken := 0
for x in [-512.0, 512.0]:
for y in [-10.0, 80.0]:
for z in [-512.0, 512.0]:
if camera.is_position_in_frustum(Vector3(x, y, z)):
ecken += 1
print("ZAHLEN (b) Map-Ecken im Frustum: %d/8" % ecken)
await _shot("k1d_b_pos1_ganze_karte.png")
print("PROBE_FERTIG")
quit(0)