From ffb27645d49b9a04b1e278bd55f57774a2d5cb02 Mon Sep 17 00:00:00 2001 From: "Hermes (godot-vr)" Date: Sat, 17 Oct 2026 18:05:01 +0000 Subject: [PATCH] =?UTF-8?q?Terrain-Relief=20lesbar=20machen=20(t=5F3ebe408?= =?UTF-8?q?6)=20=E2=80=94=20Sly-Befund=2017.10.=20"Erhoehungen/Vertiefunge?= =?UTF-8?q?n=20kaum=20erkennbar,=20alles=20knallgruen=20und=20hell".?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) Hoehen-Faktor kontrastreicher kalibriert: S-Kurve t*t*(3-2t) statt linear, Spanne 0.72..1.15 -> 0.58..1.28 (Senken deutlich dunkler, Huegel deutlich heller). 2) Pseudo-Licht (Hillshade) in den Vertex-Farben: Terrain-Normale x feste Sonnenrichtung RELIEF_SUN=(0.45,0.75,0.5) (seitlich-hinten fuer den Default-Blick), Licht-Faktor 0.62..1.22, Gesamtklemme 0.40..1.35. Formel/Konstanten IDENTISCH zum Match-Client (svmap_terrain.gd im scopedvr-client-Repo) — relief_color() static, Material-Farben unangetastet. 3) Editor-only Extra: Hoehenlinien alle 1 m + kraeftige 5-m-Indexlinie (terrain_contour.gdshader, UV2.x = Vertex-Hoehe, fwidth-stabil, unshaded). Der Quest-3-Vertrag "kein Custom-Shader" gilt fuer das SPIEL — der Editor darf teurer sein (DECISIONS-Konvention); der Match-Client bleibt StandardMaterial3D. Flach-Map-Schutz: Linien erst ab 2 m Gesamtrelief, UI-Toggle "≈ Hoehenlinien" (Default AN, nach jedem Rebuild re-synchronisiert). Material unshaded: das gebackene Shading ist exakt das sichtbare (kein Doppel-Shading mit dem Viewport-Sonnenlicht), deterministisch fuer die Render-Probe. Tests: test_terrain +19 Relief-Checks (99 gesamt): Tal/Huegel-Delta > 0.10 UND > 1.2x alte Kalibrierung, Hang-zur-Sonne heller, Stufen-Kanten-Delta > 0.08 UND > 1.2x alt, Materialfarben (Gras/Sand/Fels) unterscheidbar, Alpha=1.0, Kontur-Toggle + Flach-Map-Schutz. Suite 11 Suiten ALL OK: test_ux 137, shortcuts 198, editor_svmap 31, terrain 99, library 94, glb_view 28, spawns 52, testrun 14, bauen 87, gui_smoke 68, rmb_vorrang 32. (Hinweis: gui_smoke-F5 schlug in DIESEM Workspace nur wegen des scopedvr-client-Symlinks neben dem Editor auf — TestRun.find_client findet den Client dann wirklich; ohne Symlink = Standard-Layout: 68/68.) Renderprobe (xvfb, opengl3): tests/probe_terrain_relief_render.gd — Stufen-Delta 0.149 (>0.10), Hoehenlinien Pixel-Diff 0.167, Huegel/Senke-Fenster-Delta 0.225 — ALL OK 3/3; PNGs im Scratch (terrain_relief_stufe.png, terrain_relief_demo.png). Housekeeping: fehlende .png.import-Sidecars der M2-Natur-Thumbs nachgereicht. --- library/thumbs/baum_kiefer_1.png.import | 40 ++++ library/thumbs/baum_kiefer_2.png.import | 40 ++++ library/thumbs/baum_kiefer_3.png.import | 40 ++++ library/thumbs/baum_kiefer_4.png.import | 40 ++++ library/thumbs/baum_laub_1.png.import | 40 ++++ library/thumbs/baum_laub_2.png.import | 40 ++++ library/thumbs/baum_laub_3.png.import | 40 ++++ library/thumbs/baum_laub_4.png.import | 40 ++++ library/thumbs/baum_tot_1.png.import | 40 ++++ library/thumbs/blume_gross_1.png.import | 40 ++++ library/thumbs/blume_gross_2.png.import | 40 ++++ library/thumbs/blumen_strauss.png.import | 40 ++++ .../thumbs/fels_moos_block_gross.png.import | 40 ++++ .../thumbs/fels_moos_block_hoch.png.import | 40 ++++ library/thumbs/fels_moos_platte.png.import | 40 ++++ library/thumbs/fels_moos_scharte.png.import | 40 ++++ library/thumbs/fels_trocken_block.png.import | 40 ++++ library/thumbs/fels_trocken_klein.png.import | 40 ++++ library/thumbs/fels_trocken_platte.png.import | 40 ++++ library/thumbs/gras_bueschel.png.import | 40 ++++ library/thumbs/gras_hoch.png.import | 40 ++++ library/thumbs/gras_kurz.png.import | 40 ++++ library/thumbs/gras_wispy_hoch.png.import | 40 ++++ library/thumbs/pflanze_farn.png.import | 40 ++++ library/thumbs/pflanze_klee.png.import | 40 ++++ library/thumbs/pflanze_niedrig.png.import | 40 ++++ library/thumbs/pflanze_teppich.png.import | 40 ++++ .../thumbs/pflanze_teppich_gross.png.import | 40 ++++ .../thumbs/pilz_austernseitling.png.import | 40 ++++ library/thumbs/pilz_fliegenpilz.png.import | 40 ++++ library/thumbs/pilz_morchel.png.import | 40 ++++ library/thumbs/pilz_pfifferling.png.import | 40 ++++ library/thumbs/pilz_shiitake.png.import | 40 ++++ library/thumbs/pilz_steinpilz.png.import | 40 ++++ library/thumbs/stein_kiesel_1.png.import | 40 ++++ library/thumbs/stein_mittel_1.png.import | 40 ++++ library/thumbs/stein_mittel_2.png.import | 40 ++++ library/thumbs/strauch_blumen.png.import | 40 ++++ library/thumbs/strauch_gruen.png.import | 40 ++++ src/ui/main.gd | 34 +++ src/viewport/terrain_contour.gdshader | 41 ++++ src/viewport/terrain_contour.gdshader.uid | 1 + src/viewport/terrain_mesh.gd | 127 ++++++++-- tests/probe_terrain_relief_render.gd | 219 ++++++++++++++++++ tests/probe_terrain_relief_render.gd.uid | 1 + tests/test_terrain.gd | 138 +++++++++++ 46 files changed, 2106 insertions(+), 15 deletions(-) create mode 100644 library/thumbs/baum_kiefer_1.png.import create mode 100644 library/thumbs/baum_kiefer_2.png.import create mode 100644 library/thumbs/baum_kiefer_3.png.import create mode 100644 library/thumbs/baum_kiefer_4.png.import create mode 100644 library/thumbs/baum_laub_1.png.import create mode 100644 library/thumbs/baum_laub_2.png.import create mode 100644 library/thumbs/baum_laub_3.png.import create mode 100644 library/thumbs/baum_laub_4.png.import create mode 100644 library/thumbs/baum_tot_1.png.import create mode 100644 library/thumbs/blume_gross_1.png.import create mode 100644 library/thumbs/blume_gross_2.png.import create mode 100644 library/thumbs/blumen_strauss.png.import create mode 100644 library/thumbs/fels_moos_block_gross.png.import create mode 100644 library/thumbs/fels_moos_block_hoch.png.import create mode 100644 library/thumbs/fels_moos_platte.png.import create mode 100644 library/thumbs/fels_moos_scharte.png.import create mode 100644 library/thumbs/fels_trocken_block.png.import create mode 100644 library/thumbs/fels_trocken_klein.png.import create mode 100644 library/thumbs/fels_trocken_platte.png.import create mode 100644 library/thumbs/gras_bueschel.png.import create mode 100644 library/thumbs/gras_hoch.png.import create mode 100644 library/thumbs/gras_kurz.png.import create mode 100644 library/thumbs/gras_wispy_hoch.png.import create mode 100644 library/thumbs/pflanze_farn.png.import create mode 100644 library/thumbs/pflanze_klee.png.import create mode 100644 library/thumbs/pflanze_niedrig.png.import create mode 100644 library/thumbs/pflanze_teppich.png.import create mode 100644 library/thumbs/pflanze_teppich_gross.png.import create mode 100644 library/thumbs/pilz_austernseitling.png.import create mode 100644 library/thumbs/pilz_fliegenpilz.png.import create mode 100644 library/thumbs/pilz_morchel.png.import create mode 100644 library/thumbs/pilz_pfifferling.png.import create mode 100644 library/thumbs/pilz_shiitake.png.import create mode 100644 library/thumbs/pilz_steinpilz.png.import create mode 100644 library/thumbs/stein_kiesel_1.png.import create mode 100644 library/thumbs/stein_mittel_1.png.import create mode 100644 library/thumbs/stein_mittel_2.png.import create mode 100644 library/thumbs/strauch_blumen.png.import create mode 100644 library/thumbs/strauch_gruen.png.import create mode 100644 src/viewport/terrain_contour.gdshader create mode 100644 src/viewport/terrain_contour.gdshader.uid create mode 100644 tests/probe_terrain_relief_render.gd create mode 100644 tests/probe_terrain_relief_render.gd.uid diff --git a/library/thumbs/baum_kiefer_1.png.import b/library/thumbs/baum_kiefer_1.png.import new file mode 100644 index 0000000..2edc585 --- /dev/null +++ b/library/thumbs/baum_kiefer_1.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bekr25gy5ed4g" +path="res://.godot/imported/baum_kiefer_1.png-63fa9fc5f750b88e87a565cbd6905f6f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/baum_kiefer_1.png" +dest_files=["res://.godot/imported/baum_kiefer_1.png-63fa9fc5f750b88e87a565cbd6905f6f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/baum_kiefer_2.png.import b/library/thumbs/baum_kiefer_2.png.import new file mode 100644 index 0000000..8b86250 --- /dev/null +++ b/library/thumbs/baum_kiefer_2.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://7vgvoqtii7hu" +path="res://.godot/imported/baum_kiefer_2.png-c53e0cee25e5a5f5c10c2b2f1d57bdcc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/baum_kiefer_2.png" +dest_files=["res://.godot/imported/baum_kiefer_2.png-c53e0cee25e5a5f5c10c2b2f1d57bdcc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/baum_kiefer_3.png.import b/library/thumbs/baum_kiefer_3.png.import new file mode 100644 index 0000000..abfcdff --- /dev/null +++ b/library/thumbs/baum_kiefer_3.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://8om6nu2ja3ck" +path="res://.godot/imported/baum_kiefer_3.png-50b991462e7bef268bab9478e07f764f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/baum_kiefer_3.png" +dest_files=["res://.godot/imported/baum_kiefer_3.png-50b991462e7bef268bab9478e07f764f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/baum_kiefer_4.png.import b/library/thumbs/baum_kiefer_4.png.import new file mode 100644 index 0000000..6ecf8ca --- /dev/null +++ b/library/thumbs/baum_kiefer_4.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dv6e20l5mu8oc" +path="res://.godot/imported/baum_kiefer_4.png-67f1452c8a89076cbe5b74c993cfe51a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/baum_kiefer_4.png" +dest_files=["res://.godot/imported/baum_kiefer_4.png-67f1452c8a89076cbe5b74c993cfe51a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/baum_laub_1.png.import b/library/thumbs/baum_laub_1.png.import new file mode 100644 index 0000000..5259a22 --- /dev/null +++ b/library/thumbs/baum_laub_1.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dil3w1u7yjyc1" +path="res://.godot/imported/baum_laub_1.png-20d3a1ff28ba445efaac4a88abb97e0a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/baum_laub_1.png" +dest_files=["res://.godot/imported/baum_laub_1.png-20d3a1ff28ba445efaac4a88abb97e0a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/baum_laub_2.png.import b/library/thumbs/baum_laub_2.png.import new file mode 100644 index 0000000..b0034cb --- /dev/null +++ b/library/thumbs/baum_laub_2.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cun1qf2ebjhqh" +path="res://.godot/imported/baum_laub_2.png-0793b608137808b6eb32f164203ab7c1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/baum_laub_2.png" +dest_files=["res://.godot/imported/baum_laub_2.png-0793b608137808b6eb32f164203ab7c1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/baum_laub_3.png.import b/library/thumbs/baum_laub_3.png.import new file mode 100644 index 0000000..340f755 --- /dev/null +++ b/library/thumbs/baum_laub_3.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dosxtq0ul18aq" +path="res://.godot/imported/baum_laub_3.png-253191da75b1d5655b1e27fd9f2f67e3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/baum_laub_3.png" +dest_files=["res://.godot/imported/baum_laub_3.png-253191da75b1d5655b1e27fd9f2f67e3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/baum_laub_4.png.import b/library/thumbs/baum_laub_4.png.import new file mode 100644 index 0000000..87836d9 --- /dev/null +++ b/library/thumbs/baum_laub_4.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cwkhas005384" +path="res://.godot/imported/baum_laub_4.png-a9acc504f63d9a8be7ede49a0b4b1e0b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/baum_laub_4.png" +dest_files=["res://.godot/imported/baum_laub_4.png-a9acc504f63d9a8be7ede49a0b4b1e0b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/baum_tot_1.png.import b/library/thumbs/baum_tot_1.png.import new file mode 100644 index 0000000..f353b2b --- /dev/null +++ b/library/thumbs/baum_tot_1.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c2nbtw0tcr0ef" +path="res://.godot/imported/baum_tot_1.png-156324a889f6086cb356cc0ec888c096.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/baum_tot_1.png" +dest_files=["res://.godot/imported/baum_tot_1.png-156324a889f6086cb356cc0ec888c096.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/blume_gross_1.png.import b/library/thumbs/blume_gross_1.png.import new file mode 100644 index 0000000..25d642b --- /dev/null +++ b/library/thumbs/blume_gross_1.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dobpajhax3r1u" +path="res://.godot/imported/blume_gross_1.png-308f1974500e408f1d953e3f93c77045.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/blume_gross_1.png" +dest_files=["res://.godot/imported/blume_gross_1.png-308f1974500e408f1d953e3f93c77045.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/blume_gross_2.png.import b/library/thumbs/blume_gross_2.png.import new file mode 100644 index 0000000..a5cca8d --- /dev/null +++ b/library/thumbs/blume_gross_2.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://0jq32bxhc27s" +path="res://.godot/imported/blume_gross_2.png-1ba5812eac7282851f8a8dc8607877dd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/blume_gross_2.png" +dest_files=["res://.godot/imported/blume_gross_2.png-1ba5812eac7282851f8a8dc8607877dd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/blumen_strauss.png.import b/library/thumbs/blumen_strauss.png.import new file mode 100644 index 0000000..18f5759 --- /dev/null +++ b/library/thumbs/blumen_strauss.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dcf24xp36e3ut" +path="res://.godot/imported/blumen_strauss.png-3a5603db42ec49cc7b34f28ab23dcf55.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/blumen_strauss.png" +dest_files=["res://.godot/imported/blumen_strauss.png-3a5603db42ec49cc7b34f28ab23dcf55.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/fels_moos_block_gross.png.import b/library/thumbs/fels_moos_block_gross.png.import new file mode 100644 index 0000000..a751013 --- /dev/null +++ b/library/thumbs/fels_moos_block_gross.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c7iqeeigbdhfx" +path="res://.godot/imported/fels_moos_block_gross.png-abf3be56ad7a7866483a1f73c1fd4283.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/fels_moos_block_gross.png" +dest_files=["res://.godot/imported/fels_moos_block_gross.png-abf3be56ad7a7866483a1f73c1fd4283.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/fels_moos_block_hoch.png.import b/library/thumbs/fels_moos_block_hoch.png.import new file mode 100644 index 0000000..d5f96a9 --- /dev/null +++ b/library/thumbs/fels_moos_block_hoch.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://co4xxnmbl0kkq" +path="res://.godot/imported/fels_moos_block_hoch.png-1903f8cff7610dcf9a024c55fe950790.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/fels_moos_block_hoch.png" +dest_files=["res://.godot/imported/fels_moos_block_hoch.png-1903f8cff7610dcf9a024c55fe950790.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/fels_moos_platte.png.import b/library/thumbs/fels_moos_platte.png.import new file mode 100644 index 0000000..de00c8f --- /dev/null +++ b/library/thumbs/fels_moos_platte.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://tl3nw48rllvx" +path="res://.godot/imported/fels_moos_platte.png-0d81d28e18820363fc1d159121ef5466.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/fels_moos_platte.png" +dest_files=["res://.godot/imported/fels_moos_platte.png-0d81d28e18820363fc1d159121ef5466.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/fels_moos_scharte.png.import b/library/thumbs/fels_moos_scharte.png.import new file mode 100644 index 0000000..739f71a --- /dev/null +++ b/library/thumbs/fels_moos_scharte.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dcu40haa865h2" +path="res://.godot/imported/fels_moos_scharte.png-c96d806e4efb1bdf4374f6274e8f3228.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/fels_moos_scharte.png" +dest_files=["res://.godot/imported/fels_moos_scharte.png-c96d806e4efb1bdf4374f6274e8f3228.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/fels_trocken_block.png.import b/library/thumbs/fels_trocken_block.png.import new file mode 100644 index 0000000..4a6b437 --- /dev/null +++ b/library/thumbs/fels_trocken_block.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ldxgkd1uespu" +path="res://.godot/imported/fels_trocken_block.png-2221485325d3a97dd25b35d811e4a707.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/fels_trocken_block.png" +dest_files=["res://.godot/imported/fels_trocken_block.png-2221485325d3a97dd25b35d811e4a707.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/fels_trocken_klein.png.import b/library/thumbs/fels_trocken_klein.png.import new file mode 100644 index 0000000..cf06436 --- /dev/null +++ b/library/thumbs/fels_trocken_klein.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b5w0k4w8co8qo" +path="res://.godot/imported/fels_trocken_klein.png-1f0ff23792e17ca19d6cf65f2460441f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/fels_trocken_klein.png" +dest_files=["res://.godot/imported/fels_trocken_klein.png-1f0ff23792e17ca19d6cf65f2460441f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/fels_trocken_platte.png.import b/library/thumbs/fels_trocken_platte.png.import new file mode 100644 index 0000000..1697317 --- /dev/null +++ b/library/thumbs/fels_trocken_platte.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bs17uiop8yv07" +path="res://.godot/imported/fels_trocken_platte.png-c7eed86389c42647bb16ceb5da34f82a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/fels_trocken_platte.png" +dest_files=["res://.godot/imported/fels_trocken_platte.png-c7eed86389c42647bb16ceb5da34f82a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/gras_bueschel.png.import b/library/thumbs/gras_bueschel.png.import new file mode 100644 index 0000000..6ea9a16 --- /dev/null +++ b/library/thumbs/gras_bueschel.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2m2o8gc6ixmf" +path="res://.godot/imported/gras_bueschel.png-1836a46087d1947b3ac874d1ac41f190.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/gras_bueschel.png" +dest_files=["res://.godot/imported/gras_bueschel.png-1836a46087d1947b3ac874d1ac41f190.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/gras_hoch.png.import b/library/thumbs/gras_hoch.png.import new file mode 100644 index 0000000..8ab2467 --- /dev/null +++ b/library/thumbs/gras_hoch.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b4wr2h1bx1wmv" +path="res://.godot/imported/gras_hoch.png-f2572bc45a2b3e56d4ec24eddcabaa67.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/gras_hoch.png" +dest_files=["res://.godot/imported/gras_hoch.png-f2572bc45a2b3e56d4ec24eddcabaa67.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/gras_kurz.png.import b/library/thumbs/gras_kurz.png.import new file mode 100644 index 0000000..88f701d --- /dev/null +++ b/library/thumbs/gras_kurz.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bu748yjap8n0q" +path="res://.godot/imported/gras_kurz.png-93c7b9a1d4f60cd3e8fd981264c1f60b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/gras_kurz.png" +dest_files=["res://.godot/imported/gras_kurz.png-93c7b9a1d4f60cd3e8fd981264c1f60b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/gras_wispy_hoch.png.import b/library/thumbs/gras_wispy_hoch.png.import new file mode 100644 index 0000000..959eaca --- /dev/null +++ b/library/thumbs/gras_wispy_hoch.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b7rp5rbp1n1aq" +path="res://.godot/imported/gras_wispy_hoch.png-25f4847ec842e4eb94e3a929fd6bf2e1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/gras_wispy_hoch.png" +dest_files=["res://.godot/imported/gras_wispy_hoch.png-25f4847ec842e4eb94e3a929fd6bf2e1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/pflanze_farn.png.import b/library/thumbs/pflanze_farn.png.import new file mode 100644 index 0000000..22fe707 --- /dev/null +++ b/library/thumbs/pflanze_farn.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://brkpauumrgey4" +path="res://.godot/imported/pflanze_farn.png-0391a2c21283b338a9d9fd8a69d202e5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/pflanze_farn.png" +dest_files=["res://.godot/imported/pflanze_farn.png-0391a2c21283b338a9d9fd8a69d202e5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/pflanze_klee.png.import b/library/thumbs/pflanze_klee.png.import new file mode 100644 index 0000000..2e0892b --- /dev/null +++ b/library/thumbs/pflanze_klee.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://7w232tkr266u" +path="res://.godot/imported/pflanze_klee.png-8cc6e7866226218c35616bb921211717.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/pflanze_klee.png" +dest_files=["res://.godot/imported/pflanze_klee.png-8cc6e7866226218c35616bb921211717.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/pflanze_niedrig.png.import b/library/thumbs/pflanze_niedrig.png.import new file mode 100644 index 0000000..3c08248 --- /dev/null +++ b/library/thumbs/pflanze_niedrig.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dqbmu5waimk8c" +path="res://.godot/imported/pflanze_niedrig.png-799f8dde85067da22616609ac6505eb3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/pflanze_niedrig.png" +dest_files=["res://.godot/imported/pflanze_niedrig.png-799f8dde85067da22616609ac6505eb3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/pflanze_teppich.png.import b/library/thumbs/pflanze_teppich.png.import new file mode 100644 index 0000000..bb3dcb0 --- /dev/null +++ b/library/thumbs/pflanze_teppich.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b0a6guq12328w" +path="res://.godot/imported/pflanze_teppich.png-493c68157528e299df7b0f003bc5a635.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/pflanze_teppich.png" +dest_files=["res://.godot/imported/pflanze_teppich.png-493c68157528e299df7b0f003bc5a635.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/pflanze_teppich_gross.png.import b/library/thumbs/pflanze_teppich_gross.png.import new file mode 100644 index 0000000..ee32dee --- /dev/null +++ b/library/thumbs/pflanze_teppich_gross.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bqhff3gwo2s26" +path="res://.godot/imported/pflanze_teppich_gross.png-4062f8decf70d9965ff3063665ee7fd1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/pflanze_teppich_gross.png" +dest_files=["res://.godot/imported/pflanze_teppich_gross.png-4062f8decf70d9965ff3063665ee7fd1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/pilz_austernseitling.png.import b/library/thumbs/pilz_austernseitling.png.import new file mode 100644 index 0000000..6e6f915 --- /dev/null +++ b/library/thumbs/pilz_austernseitling.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bw4pbl8imhuit" +path="res://.godot/imported/pilz_austernseitling.png-0445bb62324cf11ed2874738704b240b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/pilz_austernseitling.png" +dest_files=["res://.godot/imported/pilz_austernseitling.png-0445bb62324cf11ed2874738704b240b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/pilz_fliegenpilz.png.import b/library/thumbs/pilz_fliegenpilz.png.import new file mode 100644 index 0000000..2be44c3 --- /dev/null +++ b/library/thumbs/pilz_fliegenpilz.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://8jud57lt10rf" +path="res://.godot/imported/pilz_fliegenpilz.png-b90e0493693808ec02e39f6c2d00a9c9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/pilz_fliegenpilz.png" +dest_files=["res://.godot/imported/pilz_fliegenpilz.png-b90e0493693808ec02e39f6c2d00a9c9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/pilz_morchel.png.import b/library/thumbs/pilz_morchel.png.import new file mode 100644 index 0000000..df365e3 --- /dev/null +++ b/library/thumbs/pilz_morchel.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1a5h13e1c5fv" +path="res://.godot/imported/pilz_morchel.png-bf8ce51f74c672dc50be0e0820f99be8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/pilz_morchel.png" +dest_files=["res://.godot/imported/pilz_morchel.png-bf8ce51f74c672dc50be0e0820f99be8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/pilz_pfifferling.png.import b/library/thumbs/pilz_pfifferling.png.import new file mode 100644 index 0000000..299ba65 --- /dev/null +++ b/library/thumbs/pilz_pfifferling.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://t0spfmct6jma" +path="res://.godot/imported/pilz_pfifferling.png-7eb1d305532e3fc8065d7d3c4efaafe8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/pilz_pfifferling.png" +dest_files=["res://.godot/imported/pilz_pfifferling.png-7eb1d305532e3fc8065d7d3c4efaafe8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/pilz_shiitake.png.import b/library/thumbs/pilz_shiitake.png.import new file mode 100644 index 0000000..2f432c0 --- /dev/null +++ b/library/thumbs/pilz_shiitake.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b6o3qe0rmdbpm" +path="res://.godot/imported/pilz_shiitake.png-3139005a24a48561c169bdd4d33da8f0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/pilz_shiitake.png" +dest_files=["res://.godot/imported/pilz_shiitake.png-3139005a24a48561c169bdd4d33da8f0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/pilz_steinpilz.png.import b/library/thumbs/pilz_steinpilz.png.import new file mode 100644 index 0000000..2276a07 --- /dev/null +++ b/library/thumbs/pilz_steinpilz.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://rqrno7e7dcv3" +path="res://.godot/imported/pilz_steinpilz.png-7cbf0ef5a02883777af85b85a0e0aef6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/pilz_steinpilz.png" +dest_files=["res://.godot/imported/pilz_steinpilz.png-7cbf0ef5a02883777af85b85a0e0aef6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/stein_kiesel_1.png.import b/library/thumbs/stein_kiesel_1.png.import new file mode 100644 index 0000000..1000db3 --- /dev/null +++ b/library/thumbs/stein_kiesel_1.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://41ys2t3cppap" +path="res://.godot/imported/stein_kiesel_1.png-ecf3c57815a58b2b599f41daef74100d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/stein_kiesel_1.png" +dest_files=["res://.godot/imported/stein_kiesel_1.png-ecf3c57815a58b2b599f41daef74100d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/stein_mittel_1.png.import b/library/thumbs/stein_mittel_1.png.import new file mode 100644 index 0000000..5a27686 --- /dev/null +++ b/library/thumbs/stein_mittel_1.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://swb0rowk3igl" +path="res://.godot/imported/stein_mittel_1.png-8ce538fd08d6450471753f23d4adb1df.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/stein_mittel_1.png" +dest_files=["res://.godot/imported/stein_mittel_1.png-8ce538fd08d6450471753f23d4adb1df.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/stein_mittel_2.png.import b/library/thumbs/stein_mittel_2.png.import new file mode 100644 index 0000000..8f1fe7c --- /dev/null +++ b/library/thumbs/stein_mittel_2.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cfjl3y7uqggu0" +path="res://.godot/imported/stein_mittel_2.png-02ccf2cfac1cb1a3af23c1250e1ba00f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/stein_mittel_2.png" +dest_files=["res://.godot/imported/stein_mittel_2.png-02ccf2cfac1cb1a3af23c1250e1ba00f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/strauch_blumen.png.import b/library/thumbs/strauch_blumen.png.import new file mode 100644 index 0000000..836a399 --- /dev/null +++ b/library/thumbs/strauch_blumen.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bggn1yfjjt8je" +path="res://.godot/imported/strauch_blumen.png-d3dfee5ec55cc2eaf807f30d5bbcb7fc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/strauch_blumen.png" +dest_files=["res://.godot/imported/strauch_blumen.png-d3dfee5ec55cc2eaf807f30d5bbcb7fc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/library/thumbs/strauch_gruen.png.import b/library/thumbs/strauch_gruen.png.import new file mode 100644 index 0000000..fa8762c --- /dev/null +++ b/library/thumbs/strauch_gruen.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d20nrtxj341xe" +path="res://.godot/imported/strauch_gruen.png-f4494d8e2a05ea89bb9fa5321faed2da.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://library/thumbs/strauch_gruen.png" +dest_files=["res://.godot/imported/strauch_gruen.png-f4494d8e2a05ea89bb9fa5321faed2da.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/src/ui/main.gd b/src/ui/main.gd index a435bb5..7381ca3 100644 --- a/src/ui/main.gd +++ b/src/ui/main.gd @@ -51,7 +51,9 @@ var library_buttons: Dictionary = {} # id → Button var undo_btn: Button var redo_btn: Button var grid_btn: Button # „⊞ Gitter“ (Karte t_7ac70014) +var contours_btn: Button # „≈ Höhenlinien“ (Karte t_3ebe4086) var _grid_on := false # Nutzer-Wahl für das Maßstabsgitter +var _contours_on := true # Nutzer-Wahl für die Höhenlinien (Karte t_3ebe4086) # Auswahl-Panel var sel_label: Label @@ -218,6 +220,18 @@ func _build_ui() -> void: grid_btn.toggled.connect(_on_grid_toggled) top_h.add_child(grid_btn) + # Höhenlinien-Toggle (Karte t_3ebe4086): Relief-Lesbarkeit — Linien alle + # 1 m plus kräftigere 5-m-Marke. Standardmäßig AN; auf flachen Maps + # (< 2 m Gesamtrelief) schaltet das Terrain-Mesh selbst auf aus. + contours_btn = Button.new() + contours_btn.text = "≈ Höhenlinien" + contours_btn.toggle_mode = true + contours_btn.button_pressed = true + contours_btn.custom_minimum_size = Vector2(150, 40) + contours_btn.tooltip_text = "Höhenlinien (1 m, alle 5 m kräftiger) im Terrain ein-/ausblenden. Bei flachem Terrain (< 2 m) sind sie automatisch aus." + contours_btn.toggled.connect(_on_contours_toggled) + top_h.add_child(contours_btn) + # Map-Fertig-Check (Karte t_c4162c5d): Klartext-Liste VOR dem Export. check_btn = Button.new() check_btn.text = "✅ Karte prüfen" @@ -2091,6 +2105,7 @@ func _refresh_all() -> void: func _refresh_viewport() -> void: view_builder.call("rebuild", doc, manifest) _sync_grid_to_builder() + _sync_contours_to_builder() camera.call("frame_map", float(doc.get("terrain", {}).get("size_x_m", 1024.0))) @@ -2112,6 +2127,25 @@ func _sync_grid_to_builder() -> void: grid_btn.set_pressed_no_signal(_grid_on) +# --- Höhenlinien (Karte t_3ebe4086) ------------------------------------------ + +func _on_contours_toggled(pressed: bool) -> void: + _contours_on = pressed + _sync_contours_to_builder() + + +## Nach jedem Viewport-Rebuild: Nutzer-Wahl wieder anwenden — der Rebuild +## baut das Terrain-Mesh (und damit dessen Shader-Uniform) neu. +func _sync_contours_to_builder() -> void: + if view_builder == null: + return + var terrain_view = view_builder.get("terrain_view") + if terrain_view != null and is_instance_valid(terrain_view): + terrain_view.call("set_contours", _contours_on) + if contours_btn != null and contours_btn.button_pressed != _contours_on: + contours_btn.set_pressed_no_signal(_contours_on) + + func _refresh_tree() -> void: content_tree.clear() var root := content_tree.create_item() diff --git a/src/viewport/terrain_contour.gdshader b/src/viewport/terrain_contour.gdshader new file mode 100644 index 0000000..ea89812 --- /dev/null +++ b/src/viewport/terrain_contour.gdshader @@ -0,0 +1,41 @@ +shader_type spatial; +// Terrain-Konturlinien (EDITOR-ONLY, Karte t_3ebe4086): Höhenlinien alle 1 m +// plus kräftigere 5-m-Indexlinie, per-pixel aus UV2.x (= Vertex-Höhe in m). +// Der Quest-3-Perf-Vertrag (kein Custom-Shader) gilt fürs SPIEL — der Editor +// darf teurer sein (DECISIONS.md-Konvention). Der Match-Client rendert +// weiter StandardMaterial3D + Vertex-Farben. +// +// Farbquelle = Vertex-Farbe (COLOR) mit eingebackenem Relief-Shading +// (Höhen-Kurve × Pseudo-Licht) — der Shader ist unshaded, damit das +// gebackene Shading exakt das ist, was gemessen/gezeigt wird (kein +// Doppel-Shading mit dem Viewport-Sonnenlicht). +render_mode unshaded, cull_back; + +// Linienstärken (0 = aus). Terrai-Mesh setzt linien_an auf 0, wenn die +// Map flacher als 2 m ist (sonst wäre ALLES auf der 0-m-Linie dunkel). +uniform float linien_an : hint_range(0.0, 1.0) = 1.0; +uniform float linien_staerke : hint_range(0.0, 1.0) = 0.5; +uniform float index_staerke : hint_range(0.0, 1.0) = 0.75; + +void fragment() { + vec3 col = COLOR.rgb; + float h = UV2.x; + // Flachflächen-Schutz: liegt eine GANZE Platte exakt auf einer Meter-Marke + // (z. B. Tal = 0.0 m, Plateau = 5.0 m), wäre jedes Pixel „auf der Linie“ + // → die Fläche würde komplett dunkel übermalt. Deshalb: Linienstärke an + // der lokalen Steigung dämpfen — exakt flach (dh = 0) → keine Linie, + // sanfte bis steile Hänge → volle Linie. + float dh = max(abs(dFdx(h)), abs(dFdy(h))); + float relief = smoothstep(0.0, 0.0005, dh); + // Abstand (in m) zur nächsten ganzen Meter-Marke: + float d1 = abs(fract(h + 0.5) - 0.5); + float w1 = fwidth(h) * 1.2; // Linie ~1–2 px breit, auflösungsstabil + float linie = 1.0 - smoothstep(w1 * 0.5, w1 * 1.5, d1); + // 5-m-Indexlinie (Abstand in m zur nächsten 5er-Marke): + float d5 = abs(fract(h / 5.0 + 0.5) - 0.5) * 5.0; + float w5 = fwidth(h) * 2.2; + float linie5 = 1.0 - smoothstep(w5 * 0.5, w5 * 1.5, d5); + col = mix(col, col * 0.55, linie * linien_an * linien_staerke * relief); + col = mix(col, col * 0.32, linie5 * linien_an * index_staerke * relief); + ALBEDO = col; +} diff --git a/src/viewport/terrain_contour.gdshader.uid b/src/viewport/terrain_contour.gdshader.uid new file mode 100644 index 0000000..c6426a4 --- /dev/null +++ b/src/viewport/terrain_contour.gdshader.uid @@ -0,0 +1 @@ +uid://clp2k02n02ruk diff --git a/src/viewport/terrain_mesh.gd b/src/viewport/terrain_mesh.gd index 0677b7c..ce6dbb3 100644 --- a/src/viewport/terrain_mesh.gd +++ b/src/viewport/terrain_mesh.gd @@ -9,19 +9,50 @@ extends Node3D ## kein Flackern, res 129 ≈ 16,6k Punkte = flott). ## ## Farben (Karte t_603ec1a8): Vertex-Farben aus der Material-Schicht -## (terrain.materials_r8_b64, v2) — Gras/Sand/Fels, plus Höhen-Faktor -## (dunkler im Tal, heller oben), damit Steigungen weiterhin ablesbar sind. -## Ohne Material-Schicht (Legacy-v1) gilt alles Gras = Default. KEIN Custom- -## Shader: vertex_color_use_as_albedo bleibt die einzige Material-Pipeline -## (Quest-3-Perf-Vertrag, keine Shader-Varianten). +## (terrain.materials_r8_b64, v2) — Gras/Sand/Fels, plus Höhen-Faktor. +## +## Relief-Lesbarkeit (Karte t_3ebe4086, Sly-Befund „Erhöhungen/Vertiefungen +## kaum erkennbar"): TWO-PUNCH im Vertex-Farben-Pfad: +## 1. Höhen-Faktor deutlich kontrastreicher: steilere S-Kurve statt linear +## und breitere Faktor-Spanne (Senken dunkler, Hügel heller). +## 2. Pseudo-Licht: Terrain-Normale × feste Sonnenrichtung (Hillshade) — +## Sonnenabgewandte Hänge werden dunkler, zugewandte heller. Das ist der +## günstigste echte Relief-Eindruck für die mobile Vertex-Farb-Pipeline +## und identisch zum Match-Client (svmap_terrain.gd, gleiche Konstanten). +## +## Editor-only Extra (gleiche Karte): Höhenlinien alle 1 m + 5-m-Indexlinie +## per Custom-Shader (terrain_contour.gdshader, UV2.x = Höhe). Quest-Vertrag +## „kein Custom-Shader" gilt fürs Spiel — der EDITOR darf teurer sein +## (DECISIONS.md-Konvention). Der Match-Client bleibt StandardMaterial3D. +## Editor-Viewport material ist UNSHADED: das gebackene Shading ist exakt +## sichtbar (kein Doppel-Shading mit dem Viewport-Sonnenlicht, deterministisch +## für die Render-Probe). const MATERIAL_COLORS: Array[Color] = [ Color(0.30, 0.45, 0.22), # 0 = Gras Color(0.78, 0.70, 0.45), # 1 = Sand Color(0.45, 0.44, 0.42), # 2 = Fels ] -const SHADING_LOW := 0.72 # Tal-Faktor (Material-Farbe × Faktor) -const SHADING_HIGH := 1.15 # Hügel-Faktor +# Höhen-Faktor (Material-Farbe × Faktor) — KARTE t_3ebe4086: bewusst breiter +# und mit S-Kurve (t*t*(3-2t)) kalibriert als die alte Linear-Version +# (0.72 … 1.15): Senken deutlich dunkler, Hügel deutlich heller. +const SHADING_LOW := 0.58 # Tal-Faktor +const SHADING_HIGH := 1.28 # Hügel-Faktor +# Pseudo-Licht (Hillshade): feste Sonnenrichtung, editor-seitig so gewählt, +# dass beim Default-Blick (Kamera südlich der Map, Blick Richtung −Z) die +# Formen seitlich beleuchtet wirken (Licht von rechts-hinten +X/+Z). +const RELIEF_SUN := Vector3(0.45, 0.75, 0.5) +const LIGHT_LOW := 0.62 # Hang abgewandt von der Sonne +const LIGHT_HIGH := 1.22 # Hang zugewandt +const LIGHT_T0 := 0.15 # Smoothstep-Fenster der NDOT-Kurve +const LIGHT_T1 := 0.95 +const SHADE_MIN := 0.40 # Gesamtklemme (Höhen × Licht), Schutz vor Schwarz +const SHADE_MAX := 1.35 +# Höhenlinien (editor-only) nur ab 2 m Gesamtrelief — auf einer flachen Map +# läge sonst ALLES auf der 0-m-Linie und die Fläche würde dunkel übermalt. +const CONTOUR_MIN_SPAN := 2.0 + +const CONTOUR_SHADER := "res://src/viewport/terrain_contour.gdshader" var _mi: MeshInstance3D var _res := 0 @@ -31,6 +62,8 @@ var _mats_cache := PackedByteArray() # zuletzt gesehene Material-Schicht # (res²) — Live-Pfad-Fallback, Karte # t_55ed041a. var _triangles := 0 +var _contours_pref := true # UI-Toggle „Höhenlinien“ (Default AN) +var _shader: Shader func _ready() -> void: @@ -41,13 +74,40 @@ func _ensure_mesh_instance() -> void: if _mi != null and is_instance_valid(_mi): return _mi = MeshInstance3D.new() - var mat := StandardMaterial3D.new() - mat.vertex_color_use_as_albedo = true - mat.roughness = 1.0 - _mi.material_override = mat + _mi.material_override = _make_material() add_child(_mi) +## Material fürs Editor-Terrain: unshaded Kontur-Shader (Vertex-Farbe als +## Albedo + Höhenlinien). Fällt auf unshaded StandardMaterial3D zurück, wenn +## der Shader nicht ladbar ist (Robustheit — Terrain bleibt immer sichtbar). +func _make_material() -> Material: + _shader = load(CONTOUR_SHADER) + if _shader != null: + var mat := ShaderMaterial.new() + mat.shader = _shader + return mat + push_warning("TerrainMesh: Kontur-Shader fehlt — Fallback StandardMaterial3D (unshaded).") + var std := StandardMaterial3D.new() + std.vertex_color_use_as_albedo = true + std.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED + std.roughness = 1.0 + return std + + +## UI-Toggle „Höhenlinien“ (main.gd): ohne Rebuild an/ab. +func set_contours(on: bool) -> void: + _contours_pref = on + _apply_contour_state(_last_span >= CONTOUR_MIN_SPAN) + + +func contours_enabled() -> bool: + return _contours_pref + + +var _last_span := 0.0 + + ## Kompletter Neuaufbau aus dem svmap-terrain-Block (inkl. Material-Schicht). func rebuild_from_doc(data: Dictionary) -> void: var terrain: Dictionary = data.get("terrain", {}) @@ -95,10 +155,12 @@ func update_heights(heights: PackedFloat32Array, res: int = 0, size_x: float = 0 var positions := PackedVector3Array() var normals := PackedVector3Array() var colors := PackedColorArray() + var heights_uv := PackedVector2Array() var indices := PackedInt32Array() positions.resize(res * res) normals.resize(res * res) colors.resize(res * res) + heights_uv.resize(res * res) var hmin := INF var hmax := -INF @@ -108,8 +170,10 @@ func update_heights(heights: PackedFloat32Array, res: int = 0, size_x: float = 0 if v > hmax: hmax = v var span := maxf(hmax - hmin, 0.0001) + _last_span = span var cell_x := size_x / last var cell_z := size_z / last + _apply_contour_state(span >= CONTOUR_MIN_SPAN) var z := 0 while z < res: @@ -138,13 +202,12 @@ func update_heights(heights: PackedFloat32Array, res: int = 0, size_x: float = 0 (hx_l - hx_r) * cell_z, 2.0 * cell_x * cell_z, (hz_d - hz_u) * cell_x).normalized() - var t := (heights[idx] - hmin) / span - # Material-Farbe × Höhen-Faktor (Steigungen bleiben ablesbar). var mat_id := 0 if idx < mats.size(): mat_id = clampi(mats[idx], 0, MATERIAL_COLORS.size() - 1) - var shade := lerpf(SHADING_LOW, SHADING_HIGH, t) - colors[idx] = (MATERIAL_COLORS[mat_id] as Color) * shade + # Material-Farbe × Höhen-Kurve × Pseudo-Licht (Relief lesbar). + colors[idx] = relief_color(mat_id, heights[idx], hmin, hmax, normals[idx]) + heights_uv[idx] = Vector2(heights[idx], 0.0) x += 1 z += 1 @@ -166,6 +229,7 @@ func update_heights(heights: PackedFloat32Array, res: int = 0, size_x: float = 0 arrays[Mesh.ARRAY_VERTEX] = positions arrays[Mesh.ARRAY_NORMAL] = normals arrays[Mesh.ARRAY_COLOR] = colors + arrays[Mesh.ARRAY_TEX_UV2] = heights_uv # Höhe je Vertex → Kontur-Shader arrays[Mesh.ARRAY_INDEX] = indices var mesh := ArrayMesh.new() mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays) @@ -173,6 +237,39 @@ func update_heights(heights: PackedFloat32Array, res: int = 0, size_x: float = 0 _triangles = indices.size() / 3 +## Höhenlinien-Uniform an die aktuelle Situation anpassen (Pref ∧ Relief ≥ 2 m). +func _apply_contour_state(span_ok: bool) -> void: + if _mi == null or not is_instance_valid(_mi): + return + var mat := _mi.material_override + if mat is ShaderMaterial: + mat.set_shader_parameter("linien_an", 1.0 if (_contours_pref and span_ok) else 0.0) + + +## Vertex-Farbe EINER Zelle: Material × Höhen-Kurve × Pseudo-Licht. +## Static (statt inline), damit Tests die Mathematik direkt prüfen können und +## Editor/Client die IDENTISCHE Formel teilen (Konstanten spiegeln — gleiche +## Kommentare/Konstanten wie scripts/world/svmap_terrain.gd im Client-Repo). +## Alpha bewusst IMMER 1.0 (Color × float skaliert auch den Alpha-Kanal — +## der Shading-Faktor darf nur auf RGB wirken, Karte t_603ec1a8). +static func relief_color(mat_id: int, h: float, hmin: float, hmax: float, + normal: Vector3) -> Color: + var m := clampi(mat_id, 0, MATERIAL_COLORS.size() - 1) + var span := maxf(hmax - hmin, 0.0001) + var t := clampf((h - hmin) / span, 0.0, 1.0) + # Steilere Kurve statt linear: S-Kurve hebt die Enden, senkt die Mitte — + # Hügel/Senken trennen sich stärker als bei t direkt. + var t_s := t * t * (3.0 - 2.0 * t) + var hf := lerpf(SHADING_LOW, SHADING_HIGH, t_s) + # Pseudo-Licht: Normale × feste Sonnenrichtung → Hillshade. + var nd := clampf(normal.normalized().dot(RELIEF_SUN.normalized()), 0.0, 1.0) + var x := clampf((nd - LIGHT_T0) / (LIGHT_T1 - LIGHT_T0), 0.0, 1.0) + var lf := lerpf(LIGHT_LOW, LIGHT_HIGH, x * x * (3.0 - 2.0 * x)) + var f := clampf(hf * lf, SHADE_MIN, SHADE_MAX) + var base := MATERIAL_COLORS[m] as Color + return Color(base.r * f, base.g * f, base.b * f, 1.0) + + func triangle_count() -> int: return _triangles diff --git a/tests/probe_terrain_relief_render.gd b/tests/probe_terrain_relief_render.gd new file mode 100644 index 0000000..1f18c18 --- /dev/null +++ b/tests/probe_terrain_relief_render.gd @@ -0,0 +1,219 @@ +extends SceneTree +## Render-Probe (Karte t_3ebe4086): Relief-Lesbarkeit des Editor-Terrains. +## Rendert Stufe + Hügel/Senke top-down (unshaded Vertex-Farb-Pfad) und misst +## im Pixel-Bild: +## - Farb-Delta über der Stufen-Kante (LINKS vs. RECHTS) > Schwellwert +## - Höhenlinien sichtbar: AN vs. AUS unterscheidet sich im Bild +## PNGs landen im Scratch-Verzeichnis (Sly-Verifikation nach dem Deploy). +## Start (BRAUCHT Rendering — NICHT --headless): +## xvfb-run godot --path . --rendering-driver opengl3 -s res://tests/probe_terrain_relief_render.gd + +var failures := 0 +var checks := 0 +var out_dir := "/opt/data/profiles/godot-vr/cache/scratch" + + +func _initialize() -> void: + _run() + + +func _check(cond: bool, label: String) -> void: + checks += 1 + if cond: + print(" ok: %s" % label) + else: + failures += 1 + print(" FAIL: %s" % label) + + +func _run() -> void: + # WICHTIG: in -s-SceneTree-Scripts ist root beim _initialize NOCH NICHT im + # Baum (add_child würde outside-tree hängen, look_at wirft) — erst nach + # dem ersten Frame aufbauen. + await process_frame + await process_frame + # --- Stufe: h springt bei x=0 von 0 auf 10 m (Kante genau mittig) --- + var res := 65 + var size := 320.0 + var step := _step_field(res, size) + var img_step := await _render_topdown(step, res, size, true) + img_step.save_png(out_dir + "/terrain_relief_stufe.png") + print("PROBE_SAVED %s/terrain_relief_stufe.png" % out_dir) + + var d := _edge_delta(img_step) + print("PROBE_STUFE_DELTA %.4f (0..1-Skala, max-Kanal-Differenz links/rechts)" % d) + _check(d > 0.10, "Stufe: Farb-Delta über der Reliefkante messbar (Δ %.3f > 0.10)" % d) + + # Gleiche Szene mit AUSgeschalteten Höhenlinien: Bild muss sich ändern + # (Linien sind sichtbar) — Mean-Luminance-Unterschied. + var img_step_ohne := await _render_topdown(step, res, size, false) + img_step_ohne.save_png(out_dir + "/terrain_relief_stufe_ohne_linien.png") + print("PROBE_SAVED %s/terrain_relief_stufe_ohne_linien.png" % out_dir) + var lum_an := _mean_lum(img_step) + var lum_aus := _mean_lum(img_step_ohne) + var lum_diff := absf(lum_an - lum_aus) + print("PROBE_LUMINANZ an=%.3f aus=%.3f diff=%.4f" % [lum_an, lum_aus, lum_diff]) + # Per-pixel: auf der Rampe/den Hängen sind die Linien — DORT muss sich + # das Bild deutlich ändern (Mittelwerte wären zu stark verwässert). + var px_diff := _max_pixel_diff(img_step, img_step_ohne) + print("PROBE_LINIEN_PIXELDIFF %.4f" % px_diff) + _check(px_diff > 0.05, "Höhenlinien sichtbar: Pixel-Diff mit/ohne Linien (Δ %.4f)" % px_diff) + + # --- Hügel + Senke (Demo für die visuelle Sly-Verifikation) --- + var relief := _hill_pit_field(res, size) + var img_relief := await _render_topdown(relief, res, size, true) + img_relief.save_png(out_dir + "/terrain_relief_demo.png") + print("PROBE_SAVED %s/terrain_relief_demo.png" % out_dir) + # Senke (−X-Seite) muss dunkler sein als der Hügel (+X-Seite). + var d_huegel_senke := _side_delta(img_relief) + print("PROBE_HUEGEL_SENKE_DELTA %.4f" % d_huegel_senke) + _check(d_huegel_senke > 0.10, "Hügel/Senke: deutliches Farb-Delta (Δ %.3f)" % d_huegel_senke) + + if failures == 0: + print("ALL OK: %d checks" % checks) + quit(0) + else: + print("FAILED: %d/%d checks" % [failures, checks]) + quit(1) + + +## Stufen-Feld: linke Hälfte h=0, rechte Hälfte h=10 (Kante exakt mittig). +func _step_field(res: int, size: float) -> PackedFloat32Array: + var heights := PackedFloat32Array() + heights.resize(res * res) + for z in res: + for x in res: + heights[z * res + x] = 10.0 if x > res / 2 else 0.0 + return heights + + +## Demo-Relief: Hügel (+30 m bei +X/0) und Senke (−12 m bei −X/0), sanft. +func _hill_pit_field(res: int, size: float) -> PackedFloat32Array: + var heights := PackedFloat32Array() + heights.resize(res * res) + var half := size * 0.5 + var cell := size / float(res - 1) + for z in res: + for x in res: + var wx := -half + float(x) * cell + var wz := -half + float(z) * cell + var d_hill := Vector2(wx - 80.0, wz).length() + var d_pit := Vector2(wx + 80.0, wz).length() + heights[z * res + x] = 30.0 * maxf(0.0, 1.0 - d_hill / 90.0) \ + - 12.0 * maxf(0.0, 1.0 - d_pit / 90.0) + return heights + + +## Top-down ORTHOGONAL rendern (unshaded → Licht egal, deterministisch). +func _render_topdown(heights: PackedFloat32Array, res: int, size: float, + linien: bool) -> Image: + var view := Node3D.new() + view.set_script(load("res://src/viewport/terrain_mesh.gd")) + root.add_child(view) + view.call("update_heights", heights, res, size, size) + view.call("set_contours", linien) + var cam := Camera3D.new() + root.add_child(cam) + cam.projection = Camera3D.PROJECTION_ORTHOGONAL + # Ortho-Größe so wählen, dass die Map EXAKT die Bildbreite füllt + # (KEEP_HEIGHT: size = vertikale Ausdehnung; Breite = size × Seitenverhältnis + # → kein grauer Hintergrund-Rand in der Messregion). + 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() + cam.queue_free() + view.free() + await process_frame + return img + + +## Farb-Delta zwischen linker und rechter Bildhälfte (max-Kanal, Mittelwerte). +func _edge_delta(img: Image) -> float: + var w := img.get_width() + var h := img.get_height() + var links := Vector3(0.0, 0.0, 0.0) + var rechts := Vector3(0.0, 0.0, 0.0) + var n := 0 + var y0 := int(h * 0.35) + var y1 := int(h * 0.65) + for y in range(y0, y1): + for x in range(0, w): + var c := img.get_pixel(x, y) + if x < w / 2: + links += Vector3(c.r, c.g, c.b) + else: + rechts += Vector3(c.r, c.g, c.b) + n += 1 + links /= float(n) + rechts /= float(n) + return maxf(absf(links.x - rechts.x), + maxf(absf(links.y - rechts.y), absf(links.z - rechts.z))) + + +## Hügel (Apex-Fenster rechts) vs. Senke (Grund-Fenster links). +## Bewusst NUR die zentralen Fenster um Apex/Grund messen (10 %-Radius): eine +## ganze Bildhälfte mittelt auch den Sattel dazwischen, dessen Höhe fast +## neutral ist — das Signal würde künstlich verwässert. Die Lesbarkeit, die +## Sly sieht, wird am Apex/Grund (und den Hängen dazwischen) sichtbar. +func _side_delta(img: Image) -> float: + var w := img.get_width() + var h := img.get_height() + return _window_delta(img, + Vector2(w * 0.75, h * 0.5), # Hügel-Apex (Welt x=+80 von 320) + Vector2(w * 0.25, h * 0.5), # Senken-Grund (Welt x=−80) + w * 0.10) + + +## Mittel-Farb-Delta zweier quadratischer Bildfenster (max-Kanal). +func _window_delta(img: Image, a: Vector2, b: Vector2, radius_px: float) -> float: + var ma := _window_mean(img, a, radius_px) + var mb := _window_mean(img, b, radius_px) + return maxf(absf(ma.x - mb.x), maxf(absf(ma.y - mb.y), absf(ma.z - mb.z))) + + +func _window_mean(img: Image, center: Vector2, radius_px: float) -> Vector3: + var s := Vector3.ZERO + var n := 0 + var r := int(radius_px) + var cx := int(center.x) + var cy := int(center.y) + for y in range(maxi(0, cy - r), mini(img.get_height(), cy + r)): + for x in range(maxi(0, cx - r), mini(img.get_width(), cx + r)): + var c := img.get_pixel(x, y) + s += Vector3(c.r, c.g, c.b) + n += 1 + return s / float(maxi(n, 1)) + + +func _mean_lum(img: Image) -> float: + var w := img.get_width() + var h := img.get_height() + var s := 0.0 + var n := 0 + for y in range(0, h, 4): + for x in range(0, w, 4): + var c := img.get_pixel(x, y) + s += (c.r + c.g + c.b) / 3.0 + n += 1 + return s / float(n) + + +## Maximale pro-pixel Helligkeits-Differenz zweier Bilder (dichte Abtastung) — +## misst sichtbare Linien robust gegen Hintergrund-/Mittelwert-Verwässerung. +func _max_pixel_diff(a: Image, b: Image) -> float: + var w := mini(a.get_width(), b.get_width()) + var h := mini(a.get_height(), b.get_height()) + var mx := 0.0 + for y in range(0, h, 2): + for x in range(0, w, 2): + var ca := a.get_pixel(x, y) + var cb := b.get_pixel(x, y) + var d := absf(ca.r - cb.r) + absf(ca.g - cb.g) + absf(ca.b - cb.b) + mx = maxf(mx, d / 3.0) + return mx diff --git a/tests/probe_terrain_relief_render.gd.uid b/tests/probe_terrain_relief_render.gd.uid new file mode 100644 index 0000000..fa75305 --- /dev/null +++ b/tests/probe_terrain_relief_render.gd.uid @@ -0,0 +1 @@ +uid://clpfwkbqs1pfy diff --git a/tests/test_terrain.gd b/tests/test_terrain.gd index cef85a9..d2f200c 100644 --- a/tests/test_terrain.gd +++ b/tests/test_terrain.gd @@ -50,6 +50,7 @@ func _run_tests() -> void: _test_material_format() _test_mesh_winding_live_grid() _test_live_material_colors() + _test_relief_shading() _test_grid_toggle() # _test_ray_terrain_mapping läuft in _finish() — braucht Viewport im Baum. @@ -730,6 +731,143 @@ func _test_ray_terrain_mapping() -> void: vp.free() +## Karte t_3ebe4086: Relief-Lesbarkeit — Sly-Befund „Erhöhungen/Vertiefungen +## kaum erkennbar, alles knallgrün und hell“. Neue Vertex-Farb-Pipeline: +## Höhen-Kurve (S statt linear, breitere Spanne) × Pseudo-Licht (Normale × +## feste Sonnenrichtung). asserted: +## - Tal vs. Hügel: deutlicher Helligkeits-Unterschied (besser als die alte +## Linear-Kalibrierung 0.72…1.15), +## - Pseudo-Licht: zugewandter Hang heller als abgewandter (gleiche Höhe), +## - Farb-Delta über einer Reliefkante (Stufe) > Schwellwert UND > alter Pfad, +## - Material-Farben bleiben unterscheidbar (Gras/Sand/Fels), +## - Alpha bleibt 1.0 (Shading-Faktor darf nur RGB skalieren). +func _test_relief_shading() -> void: + print("Testgruppe: Relief-Shading (Höhen-Kurve + Pseudo-Licht)") + var hmin := 0.0 + var hmax := 20.0 + var up := Vector3.UP + + # Tal (h=1) vs. Hügel (h=19), beide flach: + var tal := _relief_color(0, 1.0, hmin, hmax, up) + var huegel := _relief_color(0, 19.0, hmin, hmax, up) + var delta_h := _col_maxdiff(tal, huegel) + var tal_alt := _old_cell_color(0, 1.0, hmin, hmax) + var huegel_alt := _old_cell_color(0, 19.0, hmin, hmax) + var delta_alt := _col_maxdiff(tal_alt, huegel_alt) + _check(delta_h > 0.10, + "Relief: Tal vs. Hügel deutlich unterschiedlich (Δ %.3f)" % delta_h) + _check(delta_h > delta_alt * 1.2, + "Relief: Kontrast deutlich besser als alte Kalibrierung (%.3f vs. %.3f)" % [ + delta_h, delta_alt]) + _check(tal.r < huegel.r and tal.g < huegel.g and tal.b < huegel.b, + "Relief: Hügel heller als Tal (monoton, nicht nur kanalweise gemischt)") + + # Pseudo-Licht: gleiche Höhe (h=10, Mitte), Hang zur Sonne vs. weg. + # Sonne steht editor-seitig bei RELIEF_SUN (≈ +X/+Z oben). Ein Hang zeigt + # zur Sonne, wenn seine Normale Richtung +X/+Z kippt. + var zur_sonne := Vector3(0.45, 0.75, 0.5).normalized() # Normale ∥ Sonnenrichtung + var von_sonne := Vector3(-0.45, 0.75, -0.5).normalized() # gegenüberliegender Hang + var hell := _relief_color(0, 10.0, hmin, hmax, zur_sonne) + var dunkel := _relief_color(0, 10.0, hmin, hmax, von_sonne) + var delta_l := _col_maxdiff(hell, dunkel) + _check(delta_l > 0.08, + "Pseudo-Licht: zugewandter Hang heller als abgewandter (Δ %.3f)" % delta_l) + _check(hell.r > dunkel.r and hell.g > dunkel.g and hell.b > dunkel.b, + "Pseudo-Licht: Richtung stimmt (Sonne +X/+Z → +X/+Z-Hang heller)") + + # Stufe: Feld mit harter Kante (h springt 0 → 10 m) — Farb-Delta über die + # Kante muss klar messbar sein (das ist die eigentliche Sly-Messgröße). + var res := 33 + var heights := PackedFloat32Array() + heights.resize(res * res) + for i in heights.size(): + heights[i] = 10.0 if (i % res) >= res / 2 else 0.0 + var view := Node3D.new() + view.set_script(load("res://src/viewport/terrain_mesh.gd")) + view.call("update_heights", heights, res, 320.0, 320.0) + var mi = view.get("_mi") + var col: PackedColorArray = (mi.mesh.surface_get_arrays(0))[Mesh.ARRAY_COLOR] + var c_links: Color = col[16 * res + 14] + var c_rechts: Color = col[16 * res + 18] + var stufe_delta := _col_maxdiff(c_links, c_rechts) + _check(stufe_delta > 0.08, + "Relief: Farb-Delta über der Stufen-Kante messbar (Δ %.3f)" % stufe_delta) + _check(stufe_delta > _old_step_delta() * 1.2, + "Relief: Stufen-Kontrast deutlich besser als alte Kalibrierung (%.3f vs. %.3f)" % [ + stufe_delta, _old_step_delta()]) + view.free() + + # Material-Farben bleiben unterscheidbar NACH dem Shading (gleiche + # Höhe/Neigung → nur die Palette trennt): + var g := _relief_color(0, 5.0, 0.0, 10.0, up) + var s := _relief_color(1, 5.0, 0.0, 10.0, up) + var f := _relief_color(2, 5.0, 0.0, 10.0, up) + _check(_col_maxdiff(g, s) > 0.06 and _col_maxdiff(g, f) > 0.06 + and _col_maxdiff(s, f) > 0.06, + "Material: Gras/Sand/Fels nach Shading weiterhin unterscheidbar") + + # Alpha-Kanal: Shading darf Alpha NICHT skalieren (t_603ec1a8-Falle). + var alpha_ok := tal.a == 1.0 and huegel.a == 1.0 and hell.a == 1.0 and dunkel.a == 1.0 + _check(alpha_ok, "Alpha: bleibt 1.0 (Shading nur auf RGB)") + + # Höhenlinien-Toggle + Flach-Map-Schutz (Uniform am echten Material). + var flat := PackedFloat32Array() + flat.resize(res * res) + var view2 := Node3D.new() + view2.set_script(load("res://src/viewport/terrain_mesh.gd")) + view2.call("update_heights", flat, res, 320.0, 320.0) + var mat2 = (view2.get("_mi") as MeshInstance3D).material_override + _check(mat2 is ShaderMaterial, "Höhenlinien: Kontur-Shader aktiv (Editor-only)") + _check(float(mat2.get_shader_parameter("linien_an")) == 0.0, + "Höhenlinien: flache Map (< 2 m Relief) → automatisch AUS") + view2.call("set_contours", false) + view2.free() + var view3 := Node3D.new() + view3.set_script(load("res://src/viewport/terrain_mesh.gd")) + view3.call("update_heights", heights, res, 320.0, 320.0) # Stufe = 10 m Relief + var mat3 = (view3.get("_mi") as MeshInstance3D).material_override + _check(float(mat3.get_shader_parameter("linien_an")) == 1.0, + "Höhenlinien: Relief ≥ 2 m → AN (Default)") + view3.call("set_contours", false) + _check(float(mat3.get_shader_parameter("linien_an")) == 0.0, + "Höhenlinien: Toggle AUS wirkt ohne Rebuild") + view3.free() + + +## Statische relief_color()-Mathematik aus terrain_mesh.gd über ein +## Script-Exemplar rufen (statische Funktionen sind über die Instanz +## aufrufbar; das Exemplar braucht NICHT im Baum zu liegen). +var _relief_helper: Node3D + + +func _relief_color(mat_id: int, h: float, hmin: float, hmax: float, + normal: Vector3) -> Color: + if _relief_helper == null or not is_instance_valid(_relief_helper): + _relief_helper = Node3D.new() + _relief_helper.set_script(load("res://src/viewport/terrain_mesh.gd")) + return _relief_helper.call("relief_color", mat_id, h, hmin, hmax, normal) + + +## Die ALTE Kalibrierung (t_603ec1a8): linear 0.72…1.15 — Referenz für die +## Kontrast-Regression („besser als heute“). +func _old_cell_color(mat_id: int, h: float, hmin: float, hmax: float) -> Color: + var pal := [Color(0.30, 0.45, 0.22), Color(0.78, 0.70, 0.45), Color(0.45, 0.44, 0.42)] + var span := maxf(hmax - hmin, 0.0001) + var t := clampf((h - hmin) / span, 0.0, 1.0) + var f := lerpf(0.72, 1.15, t) + var base: Color = pal[clampi(mat_id, 0, 2)] + return Color(base.r * f, base.g * f, base.b * f, 1.0) + + +## Alter Stufen-Kontrast (gleiche Stufe wie oben, alter Farb-Pfad). +func _old_step_delta() -> float: + return _col_maxdiff(_old_cell_color(0, 0.0, 0.0, 10.0), _old_cell_color(0, 10.0, 0.0, 10.0)) + + +func _col_maxdiff(a: Color, b: Color) -> float: + return maxf(absf(a.r - b.r), maxf(absf(a.g - b.g), absf(a.b - b.b))) + + func _variance_block(a: PackedFloat32Array, res: int, x0: int, x1: int, z0: int, z1: int) -> float: var s := 0.0 var count := 0