170 lines
6.3 KiB
GDScript
170 lines
6.3 KiB
GDScript
extends SceneTree
|
|
## D6-Beweis (t_31e800f2): Map Builder platziert 3 Fahrzeuge (Panzer + Panzer-Turm
|
|
## aufgesetzt + Transportpanzer) und zeigt die Kollisionsboxen als Drahtgitter
|
|
## (Groesse aus registry `collision.size` — die Deckungsprobe als Bild).
|
|
## Zusaetzlich aufgesetzt: die Panzerkanone, damit sichtbar ist, dass Turm und
|
|
## Kanone an der richtigen Stelle sitzen (Pack-Prefab-Hoehen 1,5929 m / 0,5344 m).
|
|
##
|
|
## xvfb-run -a /opt/data/home/bin/godot4 --rendering-driver opengl3 --path . \
|
|
## -s res://tests/probe_d6_mapbuilder.gd
|
|
##
|
|
## Ausgabe: user://probe_d6_mapbuilder.png + Pruefzeilen
|
|
|
|
const AUSGABE := "user://probe_d6_mapbuilder.png"
|
|
const TURM_RING_Y := 1.5928574
|
|
const KANONE_Y := TURM_RING_Y + 0.53440094
|
|
const KANONE_Z := 1.4340973
|
|
|
|
const PLATZ := [
|
|
{"id": "tank_001", "pos": [0.0, 0.0, 0.0], "rot_y_deg": 0.0, "aufsatz": false},
|
|
{"id": "tank_tower_001", "pos": [0.0, TURM_RING_Y, 0.0], "rot_y_deg": 0.0, "aufsatz": true},
|
|
{"id": "tank_gun_001", "pos": [0.0, KANONE_Y, KANONE_Z], "rot_y_deg": 0.0, "aufsatz": true},
|
|
{"id": "apc_001", "pos": [9.5, 0.0, 0.0], "rot_y_deg": 0.0, "aufsatz": false},
|
|
]
|
|
|
|
const MapDocument := preload("res://core/svmap/map_document.gd")
|
|
|
|
var vp: SubViewport
|
|
var bauer: Node3D
|
|
var kamera: Camera3D
|
|
var rahmen := 0
|
|
var fehler: Array = []
|
|
|
|
|
|
func _initialize() -> void:
|
|
vp = SubViewport.new()
|
|
vp.size = Vector2i(1600, 1000)
|
|
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.85
|
|
var we := WorldEnvironment.new()
|
|
we.environment = env
|
|
vp.add_child(we)
|
|
|
|
var sonne := DirectionalLight3D.new()
|
|
sonne.rotation_degrees = Vector3(-48.0, 35.0, 0.0)
|
|
sonne.light_energy = 1.2
|
|
vp.add_child(sonne)
|
|
|
|
# Bodenplatte (Terrain-Ersatz): zeigt, dass die Fahrzeuge AUF dem Boden stehen.
|
|
# Bewusst ein flacher Kasten (nicht PlaneMesh) + unshaded: keine Z-Artefakte
|
|
# und keine Beleuchtungsflecken im llvmpipe-Renderer.
|
|
var boden := MeshInstance3D.new()
|
|
var bm_boden := BoxMesh.new()
|
|
bm_boden.size = Vector3(90.0, 0.4, 90.0)
|
|
boden.mesh = bm_boden
|
|
boden.position = Vector3(0.0, -0.19, 0.0) # Oberkante y=+0.01: Sand liegt 1 cm ueber der Builder-Bodenebene (kein Z-Kampf)
|
|
var bmat := StandardMaterial3D.new()
|
|
bmat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
|
bmat.albedo_color = Color(0.76, 0.66, 0.44)
|
|
boden.material_override = bmat
|
|
vp.add_child(boden)
|
|
|
|
# --- der ECHTE Map Builder (gleiche Datei wie der Editor benutzt) ---
|
|
var manifest: Dictionary = JSON.parse_string(
|
|
FileAccess.get_file_as_string("res://library/objects.json"))
|
|
var doc: Dictionary = MapDocument.create_new("D6-Beweis", 256.0, "mittag")
|
|
doc["objects"] = PLATZ
|
|
bauer = load("res://core/viewport/map_view_builder.gd").new()
|
|
vp.add_child(bauer)
|
|
await process_frame
|
|
bauer.rebuild(doc, manifest)
|
|
|
|
var lib: Dictionary = manifest.get("objects", {})
|
|
print("Bibliothek: %d Objekte" % lib.size())
|
|
for i in PLATZ.size():
|
|
var oid := str(PLATZ[i]["id"])
|
|
var marker: Node3D = bauer.object_markers[i]
|
|
if marker == null:
|
|
fehler.append("%s: kein Marker" % oid)
|
|
continue
|
|
var entry: Dictionary = lib.get(oid, {})
|
|
if entry.is_empty():
|
|
fehler.append("%s: nicht in der Bibliothek" % oid)
|
|
continue
|
|
var col: Dictionary = entry.get("collision", {})
|
|
var groesse: Array = col.get("size", [0.0, 0.0, 0.0])
|
|
# Kollisionsbox als Drahtgitter (Fusspunkt: Unterkante y=0 der Platzierung).
|
|
# 12 Kanten als PRIMITIVE_LINES — `wireframe` gibt es in der
|
|
# Compatibility-Pipeline nicht (Godot-3-Remap-Warnung).
|
|
var box := MeshInstance3D.new()
|
|
var im := ImmediateMesh.new()
|
|
var mat := StandardMaterial3D.new()
|
|
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
|
mat.albedo_color = Color(0.0, 1.0, 0.2)
|
|
im.surface_begin(Mesh.PRIMITIVE_LINES, mat)
|
|
var h := Vector3(float(groesse[0]), float(groesse[1]), float(groesse[2])) * 0.5
|
|
var ecken: Array = []
|
|
for sx in [-1.0, 1.0]:
|
|
for sy in [-1.0, 1.0]:
|
|
for sz in [-1.0, 1.0]:
|
|
ecken.append(Vector3(sx * h.x, sy * h.y, sz * h.z))
|
|
for a in ecken.size():
|
|
for b in range(a + 1, ecken.size()):
|
|
var d: Vector3 = ecken[a] - ecken[b]
|
|
# Kante = genau eine Achse unterschiedlich
|
|
var gleich := 0
|
|
if abs(d.x) < 1e-4:
|
|
gleich += 1
|
|
if abs(d.y) < 1e-4:
|
|
gleich += 1
|
|
if abs(d.z) < 1e-4:
|
|
gleich += 1
|
|
if gleich == 2:
|
|
im.surface_add_vertex(ecken[a])
|
|
im.surface_add_vertex(ecken[b])
|
|
im.surface_end()
|
|
box.mesh = im
|
|
box.material_override = mat
|
|
box.position = Vector3(0.0, float(groesse[1]) * 0.5, 0.0)
|
|
marker.add_child(box)
|
|
|
|
var echt := bool(marker.get_meta("glb"))
|
|
var mi := marker.get_child(0) as MeshInstance3D
|
|
var mesh_aabb := mi.mesh.get_aabb() if (mi != null and mi.mesh != null) else AABB()
|
|
print("%-16s echtesGLB=%-5s aufsatz=%-5s registry=%.3f/%.3f/%.3f mesh_aabb=%.3f/%.3f/%.3f mesh_min_y=%.3f box_fuss=%.3f"
|
|
% [oid, str(echt), str(PLATZ[i]["aufsatz"]),
|
|
float(groesse[0]), float(groesse[1]), float(groesse[2]),
|
|
mesh_aabb.size.x, mesh_aabb.size.y, mesh_aabb.size.z, mesh_aabb.position.y,
|
|
marker.position.y])
|
|
if not echt:
|
|
fehler.append("%s: rendert nur den Platzhalter (GLB fehlt)" % oid)
|
|
if abs(mesh_aabb.position.y) > 0.01:
|
|
fehler.append("%s: Mesh-Unterkante %.3f (soll 0)" % [oid, mesh_aabb.position.y])
|
|
|
|
# Turm muss exakt auf dem Turm-Ring des Panzers sitzen (Pack-Prefab-Wert).
|
|
var turm: Node3D = bauer.object_markers[1]
|
|
if turm != null and abs(turm.position.y - TURM_RING_Y) > 0.001:
|
|
fehler.append("Turm sitzt bei y=%.4f (Pack-Prefab: %.4f)" % [turm.position.y, TURM_RING_Y])
|
|
|
|
kamera = Camera3D.new()
|
|
kamera.fov = 42.0
|
|
kamera.current = true
|
|
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(16.0, 9.0, 20.0), Vector3(3.0, 2.4, 0.0), 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_D6_MAPBUILDER_%s" % ("FAIL" if not fehler.is_empty() else "OK"))
|
|
quit(1 if not fehler.is_empty() else 0)
|
|
return true
|
|
return false
|