Terrain-Relief lesbar machen (t_3ebe4086) — Sly-Befund 17.10. "Erhoehungen/Vertiefungen kaum erkennbar, alles knallgruen und hell".

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.
This commit is contained in:
Hermes (godot-vr) 2026-10-17 18:05:01 +00:00
parent e1c737df61
commit ffb27645d4
46 changed files with 2106 additions and 15 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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;
}

View file

@ -0,0 +1 @@
uid://clp2k02n02ruk

View file

@ -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

View file

@ -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

View file

@ -0,0 +1 @@
uid://clpfwkbqs1pfy

View file

@ -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