scopedvr-map-editor/tests/probe_kollision_alle_ident.gd
2026-10-31 15:37:09 +01:00

136 lines
4.4 KiB
GDScript

extends SceneTree
## Identitäts-Vertrag, KOLLISION ALLER OBJEKTE (Military-Pack-Umstellung,
## 25.10.2026): JEDES Registry-Objekt läuft durch die Kollider-Bauer aller drei
## Projekte —
## A) Editor (KolliderBau.kollider)
## B) Client (SvmapWorld._collider)
## C) Server (SvmapLoader._object_body)
## und druckt je Objekt `IDENT k_<oid>=<Formen>|<Typ-Zähler>|<Gesamt-AABB>`
## (Maßstab 1.7, damit scale-Fehler auffallen). Der Runner
## (run_kollision_alle_identitaet.sh) verlangt IDENTISCHE Zeilen in allen drei
## Projekten — Editor-Playtest, Quest-Client und Server kollidieren dann überall
## gleich (Trimesh, Verbund, Stamm, none).
## Das Skript liegt im EDITOR-Repo und wird je Projekt gefahren:
## godot4 --headless --path <projekt> -s <abs>/tests/probe_kollision_alle_ident.gd
## Bewusst load() statt class_name (läuft in DREI Projekten).
const MASSSTAB := 1.7
const PROJEKTE := {
"ScopedVR Map Editor": "editor",
"ScopedVR": "client",
"ScopedVR Match-Server": "server",
}
func _initialize() -> void:
var projekt := ""
var fa := FileAccess.open("res://project.godot", FileAccess.READ)
if fa != null:
for zeile in fa.get_as_text().split("\n"):
var t := zeile.strip_edges()
if t.begins_with("config/name="):
projekt = t.trim_prefix("config/name=").strip_edges().trim_prefix("\"").trim_suffix("\"")
break
var art: String = PROJEKTE.get(projekt, "")
if art.is_empty():
printerr("IDENT FAIL: unbekanntes Projekt ", projekt)
quit(1)
return
var lib_text := FileAccess.get_file_as_string("res://library/objects.json")
var parsed: Variant = JSON.parse_string(lib_text)
if not (parsed is Dictionary):
printerr("IDENT FAIL: library/objects.json unlesbar")
quit(1)
return
var objs: Dictionary = (parsed as Dictionary).get("objects", {})
var skript: Script = null
var instanz: Object = null
match art:
"editor":
skript = load("res://core/svmap/kollider_bau.gd")
"client":
skript = load("res://scripts/world/svmap_world.gd")
instanz = skript.new()
"server":
skript = load("res://src/maps/svmap_loader.gd")
if skript == null:
printerr("IDENT FAIL: Loader-Skript nicht ladbar in ", projekt)
quit(1)
return
var oids: Array = objs.keys()
oids.sort()
for oid in oids:
var col: Dictionary = (objs[oid] as Dictionary).get("collision", {})
var body: Node = null
match art:
"editor":
body = skript.call("kollider", col, MASSSTAB)
"client":
body = instanz.call("_collider", col, MASSSTAB)
"server":
body = skript.call("_object_body", {"id": str(oid), "pos": [0.0, 0.0, 0.0],
"rot_y_deg": 0.0, "scale": MASSSTAB}, {"collision": col})
print("IDENT k_%s=%s" % [oid, _beschreibe(body)])
if body != null:
body.free()
print("IDENT_PROJEKT=", projekt)
if instanz != null:
(instanz as Node).free()
quit(0)
## Formen-Zahl | Typ-Zähler | Gesamt-AABB (Fußpunkt-Koordinaten, 1 mm gerundet).
func _beschreibe(body: Node) -> String:
var n := 0
var typen := {}
var mn := Vector3(INF, INF, INF)
var mx := -Vector3(INF, INF, INF)
for k in body.get_children():
if not (k is CollisionShape3D):
continue
var cs := k as CollisionShape3D
if cs.shape == null:
continue
n += 1
var t := cs.shape.get_class()
typen[t] = int(typen.get(t, 0)) + 1
var box := _shape_aabb(cs.shape)
var xf := Transform3D(Basis.from_euler(cs.rotation), cs.position)
for ix in [0, 1]:
for iy in [0, 1]:
for iz in [0, 1]:
var ecke := box.position + Vector3(box.size.x * ix, box.size.y * iy, box.size.z * iz)
var w := xf * ecke
mn = mn.min(w)
mx = mx.max(w)
var ts: Array = typen.keys()
ts.sort()
var tz: PackedStringArray = []
for t in ts:
tz.append("%s:%d" % [t, int(typen[t])])
if n == 0:
return "0||leer"
return "%d|%s|%.3f,%.3f,%.3f|%.3f,%.3f,%.3f" % [n, ",".join(tz), mn.x, mn.y, mn.z, mx.x, mx.y, mx.z]
func _shape_aabb(s: Shape3D) -> AABB:
if s is BoxShape3D:
var sz := (s as BoxShape3D).size
return AABB(-sz * 0.5, sz)
if s is CylinderShape3D:
var c := s as CylinderShape3D
return AABB(Vector3(-c.radius, -c.height * 0.5, -c.radius), Vector3(c.radius * 2.0, c.height, c.radius * 2.0))
if s is SphereShape3D:
var r := (s as SphereShape3D).radius
return AABB(Vector3(-r, -r, -r), Vector3(r * 2.0, r * 2.0, r * 2.0))
if s is ConcavePolygonShape3D:
var f := (s as ConcavePolygonShape3D).get_faces()
if f.is_empty():
return AABB()
var a := AABB(f[0], Vector3.ZERO)
for v in f:
a = a.expand(v)
return a
return AABB()