148 lines
No EOL
5.8 KiB
GDScript
148 lines
No EOL
5.8 KiB
GDScript
extends SceneTree
|
||
## Render-Probe M2 (Karte t_f9eff526): Terrain spiegeln im Symmetrie-Modus.
|
||
## Zwei Striche (Heben bei −260/−140, Malen Sand bei +150/+180) mit
|
||
## Symmetrie=Punkt über den ECHTEN terrain_tool-Pfad (demo_stroke) —
|
||
## draufsichtig gerendert müssen BOTHSEITIG Hügel + Sand erscheinen:
|
||
## - Hügel-Fenster und Zwilling-Hügel-Fenster nahezu gleich hell
|
||
## - Sand-Fenster und Zwilling-Sand-Fenster nahezu gleich farbig
|
||
## - beide deutlich verschieden zur unberührten Mitte
|
||
## PNGs landen im Profil-Scratch + /opt/data/scopedvr/work/m2_bilder/.
|
||
## Start (BRAUCHT Rendering — NICHT --headless):
|
||
## xvfb-run godot --path . --rendering-driver opengl3 -s res://tests/probe_terrain_mirror_render.gd
|
||
|
||
const RES := 65
|
||
const SIZE := 1024.0
|
||
var failures := 0
|
||
var checks := 0
|
||
var out_dir := "/opt/data/profiles/scopedvr-dev/cache/scratch"
|
||
|
||
|
||
class ToolMain:
|
||
var doc: Dictionary = {}
|
||
var sym_mode: int = SymmetryCore.Mode.POINT
|
||
var history: ActionHistory = ActionHistory.new()
|
||
func _set_status(_text: String, _err := false) -> void:
|
||
pass
|
||
func _on_terrain_stroke_committed() -> void:
|
||
pass
|
||
|
||
|
||
func _initialize() -> void:
|
||
await process_frame
|
||
await process_frame
|
||
# --- Doc + Terrain-Tool (echter Strich-Pfad inkl. Spiegel) ---
|
||
var doc := SvmapDocument.create_new("MirrorProbe", SIZE, "mittag")
|
||
var terrain: Dictionary = doc["terrain"]
|
||
terrain["resolution"] = RES
|
||
terrain["size_x_m"] = SIZE
|
||
terrain["size_z_m"] = SIZE
|
||
terrain["min_height_m"] = 0.0
|
||
terrain["max_height_m"] = 40.0
|
||
var flat := PackedFloat32Array()
|
||
flat.resize(RES * RES)
|
||
terrain["heightmap_r16_b64"] = TerrainField.encode(flat, 0.0, 40.0)
|
||
var mats := PackedByteArray()
|
||
mats.resize(RES * RES)
|
||
terrain["materials_r8_b64"] = SvmapIO.encode_materials(mats)
|
||
var tmain := ToolMain.new()
|
||
tmain.doc = doc
|
||
tmain.history.begin(doc)
|
||
var ttool := Node3D.new()
|
||
ttool.set_script(load("res://core/editor/terrain_tool.gd"))
|
||
ttool.setup(tmain, Camera3D.new(), null, null)
|
||
ttool.call("activate")
|
||
ttool.call("set_radius", 100.0)
|
||
ttool.call("set_paint_material", 1)
|
||
ttool.call("demo_stroke", -260.0, -140.0, TerrainField.Mode.RAISE, 2.0)
|
||
ttool.call("demo_stroke", 150.0, 180.0, TerrainField.Mode.PAINT, 1.0)
|
||
var heights := SvmapIO.decode_heights(terrain)
|
||
var got_mats := SvmapIO.decode_materials(terrain)
|
||
# Zellgleiche Hälften numerisch gegenprüfen (Punkt-Modus):
|
||
var bad_h := 0
|
||
var bad_m := 0
|
||
for z in RES:
|
||
for x in RES:
|
||
var mc: Vector2i = SymmetryCore.mirror_cell(SymmetryCore.Mode.POINT, x, z, RES)
|
||
if heights[z * RES + x] != heights[mc.y * RES + mc.x]:
|
||
bad_h += 1
|
||
if int(got_mats[z * RES + x]) != int(got_mats[mc.y * RES + mc.x]):
|
||
bad_m += 1
|
||
print("PROBE_MIRROR_ASYM hoehe=%d material=%d (0 = exakt zellgleich)" % [bad_h, bad_m])
|
||
_check(bad_h == 0, "Höhen-Hälften exakt zellgleich (0 Abweichungen)")
|
||
_check(bad_m == 0, "Material-Hälften exakt zellgleich (0 Abweichungen)")
|
||
|
||
# --- Draufsicht rendern (unshaded Vertex-Farben + Höhenlinien) ---
|
||
var view := Node3D.new()
|
||
view.set_script(load("res://core/viewport/terrain_mesh.gd"))
|
||
root.add_child(view)
|
||
view.call("update_heights", heights, RES, SIZE, SIZE, got_mats)
|
||
var cam := Camera3D.new()
|
||
root.add_child(cam)
|
||
cam.projection = Camera3D.PROJECTION_ORTHOGONAL
|
||
var vp := root.size
|
||
cam.size = SIZE * float(vp.y) / float(vp.x)
|
||
cam.position = Vector3(0.0, SIZE, 0.01)
|
||
cam.look_at(Vector3.ZERO, Vector3(0, 0, -1))
|
||
cam.current = true
|
||
await process_frame
|
||
await process_frame
|
||
await RenderingServer.frame_post_draw
|
||
var img := root.get_texture().get_image()
|
||
img.save_png(out_dir + "/terrain_mirror_beweis.png")
|
||
print("PROBE_SAVED %s/terrain_mirror_beweis.png" % out_dir)
|
||
|
||
# --- Fenster-Messungen ---
|
||
# Ortho-Kamera füllt exakt die BildBREITE: horizontale Ausdehnung =
|
||
# SIZE, vertikale = SIZE × (Höhe/Breite des Viewports) (KEEP_HEIGHT).
|
||
# Welt → Bild: sx = 0.5 + x/SIZE, sy = 0.5 + z/vert_extent.
|
||
var w := img.get_width()
|
||
var h := img.get_height()
|
||
var vert := SIZE * float(h) / float(w)
|
||
var hill_a := _window_mean(img, Vector2(w * (0.5 - 260.0 / SIZE), h * (0.5 - 140.0 / vert)), int(w * 0.04))
|
||
var hill_b := _window_mean(img, Vector2(w * (0.5 + 260.0 / SIZE), h * (0.5 + 140.0 / vert)), int(w * 0.04))
|
||
var sand_a := _window_mean(img, Vector2(w * (0.5 + 150.0 / SIZE), h * (0.5 + 180.0 / vert)), int(w * 0.04))
|
||
var sand_b := _window_mean(img, Vector2(w * (0.5 - 150.0 / SIZE), h * (0.5 - 180.0 / vert)), int(w * 0.04))
|
||
var mitte := _window_mean(img, Vector2(w * 0.5, h * 0.5), int(w * 0.03))
|
||
print("PROBE_HUEGEL a=%s b=%s" % [hill_a, hill_b])
|
||
print("PROBE_SAND a=%s b=%s" % [sand_a, sand_b])
|
||
print("PROBE_MITTE %s (unberührtes Gras)" % mitte)
|
||
var dh := _v3dist(hill_a, hill_b)
|
||
var ds := _v3dist(sand_a, sand_b)
|
||
print("PROBE_SYMDIFF huegel=%.4f sand=%.4f" % [dh, ds])
|
||
_check(dh < 0.02, "Hügel und Zwilling-Hügel im Bild gleich (Δ %.4f < 0.02)" % dh)
|
||
_check(ds < 0.02, "Sand und Zwilling-Sand im Bild gleich (Δ %.4f < 0.02)" % ds)
|
||
_check(_v3dist(hill_a, mitte) > 0.05, "Hügel deutlich sichtbar vs. Mitte (Δ %.4f)" % _v3dist(hill_a, mitte))
|
||
_check(_v3dist(sand_a, mitte) > 0.05, "Sand deutlich sichtbar vs. Gras-Mitte (Δ %.4f)" % _v3dist(sand_a, mitte))
|
||
|
||
if failures == 0:
|
||
print("ALL OK: %d checks" % checks)
|
||
quit(0)
|
||
else:
|
||
print("FAILED: %d/%d checks" % [failures, checks])
|
||
quit(1)
|
||
|
||
|
||
func _window_mean(img: Image, center: Vector2, radius_px: int) -> Vector3:
|
||
var s := Vector3.ZERO
|
||
var n := 0
|
||
var cx := int(center.x)
|
||
var cy := int(center.y)
|
||
for y in range(maxi(0, cy - radius_px), mini(img.get_height(), cy + radius_px)):
|
||
for x in range(maxi(0, cx - radius_px), mini(img.get_width(), cx + radius_px)):
|
||
var c := img.get_pixel(x, y)
|
||
s += Vector3(c.r, c.g, c.b)
|
||
n += 1
|
||
return s / float(maxi(n, 1))
|
||
|
||
|
||
func _v3dist(a: Vector3, b: Vector3) -> float:
|
||
return (a - b).length()
|
||
|
||
|
||
func _check(cond: bool, label: String) -> void:
|
||
checks += 1
|
||
if cond:
|
||
print(" ok: %s" % label)
|
||
else:
|
||
failures += 1
|
||
print(" FAIL: %s" % label) |