- _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>
83 lines
3.5 KiB
GDScript
83 lines
3.5 KiB
GDScript
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)
|