scopedvr-map-editor/tests/test_klick_exakt.gd
slyphax 66df3bdc1e Editor: Klick wählt das exakt getroffene Objekt (Mauer statt Nachbar-Gebäude) + Malen-Hinweis statt Stärke-Regler
- _pick_object: Kugel-Näherung nur noch Vorfilter, entschieden wird über den
  echten Mesh-Treffer (SelectionMath.ray_mesh_distance, Faces gecacht).
  Ohne Mesh-Treffer bleibt die alte Kugel-Wahl als Fallback.
- Terrain-Panel: im Malen-Modus ist der Stärke-Regler weg (Malen setzt das
  Material zellenweise, Stärke hatte nie eine Wirkung); stattdessen steht die
  feinste Mal-Einheit der Karte da (size / (resolution - 1)).
- Tests: test_klick_exakt 6/6 (reproduziert Gebäude-Kugel vor Mauer),
  test_mal_hinweis 4/4. Vorhandene Suiten unverändert (4 schon vorher rote
  Umgebungs-Tests: assets_panel, identitaet_client, model_sources,
  playtest_identitaet).

Ersetzt die nie gepushten Arbeitskopie-Änderungen der Karten t_12feca66 /
t_c487a430 / t_271a77bd (Material-IDs per lerp gemischt, Sub-Zellen ohne
Client/Server-Unterstützung).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 23:27:48 +02:00

83 lines
3.5 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
## Editor-Tests (headless): exakter Klick-Treffer (Mauer-Klick-Fix 24.10.).
## Ausführen:
## godot --headless --path . -s res://tests/test_klick_exakt.gd
## Exit 0 = alles grün.
##
## K1 ray_mesh_distance trifft eine Box im richtigen Abstand, verfehlt sie
## daneben, respektiert Drehung + Scale der Transform.
## K2 Sly-Szenario: großes Gebäude (20×10×20) direkt neben schmaler Mauer
## (6×2×0,4). Der Strahl trifft die Mauer, liegt aber auch IN der
## Gebäude-Kugel und tritt dort früher ein → Kugel allein wählt das
## Gebäude (alter Fehler), der exakte Test wählt die Mauer.
var failures := 0
var checks := 0
func _initialize() -> void:
_run_all.call_deferred()
func _check(cond: bool, label: String) -> void:
checks += 1
print("%s: %s" % ["ok " if cond else "FAIL", label])
if not cond:
failures += 1
func _box_faces(size: Vector3) -> PackedVector3Array:
var m := BoxMesh.new()
m.size = size
return m.get_faces()
func _run_all() -> void:
# K1: Grundfälle
var faces := _box_faces(Vector3(2, 2, 2))
var xf := Transform3D(Basis(), Vector3(0, 1, 0))
var d := SelectionMath.ray_mesh_distance(xf, faces, Vector3(0, 1, 10), Vector3(0, 0, -1))
_check(absf(d - 9.0) < 0.001, "K1 Box frontal: Abstand 9 m (ist %.3f)" % d)
d = SelectionMath.ray_mesh_distance(xf, faces, Vector3(3, 1, 10), Vector3(0, 0, -1))
_check(d < 0.0, "K1 daneben: kein Treffer")
var xf_rot := Transform3D(Basis(Vector3.UP, deg_to_rad(45.0)), Vector3(0, 1, 0))
d = SelectionMath.ray_mesh_distance(xf_rot, faces, Vector3(1.2, 1, 10), Vector3(0, 0, -1))
_check(d > 0.0, "K1 gedreht 45°: Ecke ragt bis x≈1,41 → Treffer bei x=1,2")
var xf_scale := Transform3D(Basis().scaled(Vector3.ONE * 3.0), Vector3(0, 1, 0))
d = SelectionMath.ray_mesh_distance(xf_scale, faces, Vector3(0, 1, 10), Vector3(0, 0, -1))
_check(absf(d - 7.0) < 0.001, "K1 Scale 3: Abstand in Welt-Metern 7 (ist %.3f)" % d)
# K2: Gebäude neben Mauer
var building_size := Vector3(20, 10, 20)
var wall_size := Vector3(6, 2, 0.4)
var building_xf := Transform3D(Basis(), Vector3(0, 5, 0))
var wall_xf := Transform3D(Basis(), Vector3(0, 1, 12)) # Mauer 2 m vor der Gebäudefront (z = 10)
var origin := Vector3(0, 1.5, 40)
var dir := Vector3(0, 0, -1)
# Kugel-Näherung wie map_view_builder: halbe Diagonale + max(0.6, half.y·0.25)
var bh := building_size * 0.5
var wh := wall_size * 0.5
var b_rad := bh.length() + maxf(0.6, bh.y * 0.25)
var w_rad := wh.length() + maxf(0.6, wh.y * 0.25)
var b_center := Vector3(0, 5, 0)
var w_center := Vector3(0, 1, 12)
var b_sphere := _sphere_t(origin, dir, b_center, b_rad)
var w_sphere := _sphere_t(origin, dir, w_center, w_rad)
_check(b_sphere >= 0.0 and b_sphere < w_sphere,
"K2 Ausgangslage: Gebäude-Kugel wird VOR der Mauer-Kugel getroffen (alter Fehler reproduziert)")
var b_exact := SelectionMath.ray_mesh_distance(building_xf, _box_faces(building_size), origin, dir)
var w_exact := SelectionMath.ray_mesh_distance(wall_xf, _box_faces(wall_size), origin, dir)
_check(w_exact > 0.0 and (b_exact < 0.0 or w_exact < b_exact),
"K2 exakt: Mauer (%.2f m) vor Gebäude (%.2f m) → Mauer wird gewählt" % [w_exact, b_exact])
print("test_klick_exakt: %d/%d ok" % [checks - failures, checks])
quit(1 if failures > 0 else 0)
func _sphere_t(origin: Vector3, dir: Vector3, center: Vector3, radius: float) -> float:
var to_c := center - origin
var tca := to_c.dot(dir)
var d2 := to_c.length_squared() - tca * tca
if tca <= 0.0 or d2 > radius * radius:
return -1.0
return tca - sqrt(radius * radius - d2)