Terrain-Textur-Panel + Editor-Spiegel (t_ab408127): Terrain-Panel zeigt die 20 CC0-Materialien; terrain_mesh bindet terrain_arrays.res (Spiegel aus Client via tools/sync_terrain_tex.sh, Ordner OHNE .gdignore — ResourceLoader braucht den Index); TerrainArrays-Klasse spiegelgleich in library/ (identischer Pfad wie im Client — .res-Script-Pfad löst in beiden Repos auf); Kontur-Shader bekommt Textur-Sampling + Höhenlinien; V15-Test auf neue Palette (ID 20 abgelehnt, 3 gültig); test_terrain 155/155
This commit is contained in:
parent
338e7152a0
commit
8468b89456
11 changed files with 245 additions and 31 deletions
BIN
assets/textures/terrain/terrain_arrays.res
Normal file
BIN
assets/textures/terrain/terrain_arrays.res
Normal file
Binary file not shown.
18
library/terrain_arrays.gd
Normal file
18
library/terrain_arrays.gd
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
extends Resource
|
||||
class_name TerrainArrays
|
||||
## Träger-Resource für die Terrain-Texture2DArrays (Karte t_ab408127).
|
||||
## GLEICHER Pfad in Client UND Editor (res://library/terrain_arrays.gd) —
|
||||
## die gespeicherte .res verweist auf den Script-Pfad, der muss in beiden
|
||||
## Repos auflösen (Repos sind entkoppelt, therefore identischer Ort).
|
||||
##
|
||||
## Warum diese Klasse: ResourceSaver speichert die internen Images eines
|
||||
## Texture2DArray NICHT (gespeicherte .res/.tres enthält 20× null — 20.10.
|
||||
## bewiesen). Images in einer eigenen exportierten Property dagegen landen
|
||||
## zuverlässig in der .res-Datei. Der Verbraucher baut beim LOAD aus diesen
|
||||
## Images sofort die Texture2DArrays (create_from_images) — die Images sind
|
||||
## bereits ETC2-komprimiert (Quest-3-nativ, ~1 MB/Layer statt 4 MB RGBA8),
|
||||
## der Bau ist also nur ein GPU-Upload, kein Rekomprimieren.
|
||||
|
||||
@export var albedo_images: Array[Image] = []
|
||||
@export var normal_images: Array[Image] = []
|
||||
@export var tile_size := 1.0 # Meter pro Kachel (Worldspace-Tiling)
|
||||
1
library/terrain_arrays.gd.uid
Normal file
1
library/terrain_arrays.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://c5v7hht4apubd
|
||||
|
|
@ -26,7 +26,32 @@ const FORMAT_VERSION := 2
|
|||
## 2 = aktuell. Neu gespeicherte Maps tragen immer FORMAT_VERSION.
|
||||
const SUPPORTED_FORMAT_VERSIONS: Array[int] = [1, 2]
|
||||
## Material-IDs (Index = ID). 0 = Gras = Default für Maps ohne Schicht.
|
||||
const MATERIAL_IDS: Array[String] = ["gras", "sand", "fels"]
|
||||
## Karte t_ab408127 (Sly-Vielfalt): Palette von 3 auf 20 erweitert — NEUE IDs
|
||||
## NUR ANGEHÄNGT, Legacy-Zuordnung (0=Gras, 1=Sand, 2=Fels) bleibt unverändert,
|
||||
## damit alte Maps byte-identisch laden. Format v2 bleibt (Feldgröße = res²,
|
||||
## Werte = uint8 je Sample — der Bereich wächst nur implizit mit der Länge).
|
||||
const MATERIAL_IDS: Array[String] = [
|
||||
"gras_steppe", # 0 = Legacy-Gras (Default/Standard-Boden)
|
||||
"sand_rippen", # 1 = Legacy-Sand (Dünen/offene Flächen)
|
||||
"fels_granit", # 2 = Legacy-Fels (Berge/Klippen)
|
||||
"gras_satt", # 3 = gesundes, sattes Grün
|
||||
"gras_vertrocknet", # 4 = ausgedörrt, gelblich
|
||||
"gras_moosig", # 5 = weiches Moosgrün
|
||||
"erde_braun", # 6 = feuchte braune Erde
|
||||
"erde_getreten", # 7 = festgetretener Pfad
|
||||
"erde_umgeackert", # 8 = umgegrabene/zerwühlte Erde
|
||||
"schotter", # 9 = Kies/Schotter
|
||||
"lehm_rot", # 10 = rotbrauner Lehm
|
||||
"sand_hell", # 11 = heller Flugsand
|
||||
"sand_rost", # 12 = rostroter Sand
|
||||
"fels_basalt", # 13 = dunkler Basalt
|
||||
"fels_geroell", # 14 = Geröllhalde
|
||||
"schlamm", # 15 = Matsch/Schlamm
|
||||
"schnee_frisch", # 16 = Neuschnee
|
||||
"schnee_matschig", # 17 = angeschmolzen/angesaut
|
||||
"asche", # 18 = Asche/verkohlt
|
||||
"stoppeln", # 19 = abgeerntetes Feld
|
||||
]
|
||||
const WEATHER_PRESETS: Array[String] = ["mittag", "abend", "regen", "nacht"]
|
||||
## Skybox-Option (Karte t_f72913d4): Werte des optionalen Feldes `weather_sky`
|
||||
## — identische Semantik wie der Client (Weather.apply_preset sky_override):
|
||||
|
|
|
|||
|
|
@ -78,19 +78,38 @@ func build() -> void:
|
|||
mat_row.add_theme_constant_override("separation", UIScale.px(8)) # U7-Raster
|
||||
terrain_palette.add_child(mat_row)
|
||||
var mat_group := ButtonGroup.new()
|
||||
var mat_labels := ["🌿 Gras", "🏖 Sand", "🪨 Fels"]
|
||||
# Karte t_ab408127: 20 Slots in scrollbarer Raster-Palette (Spec: „like
|
||||
# Bibliothek") — 4 Spalten, ~2 sichtbare Reihen, scrollbar nach unten.
|
||||
# Thumbnails folgen, sobald die Texturen in assets/textures/terrain/ liegen
|
||||
# (gen_terrain_thumbs) — bis dahin Button-Farbfeld via icon (Default).
|
||||
var mat_grid := GridContainer.new()
|
||||
mat_grid.columns = 4
|
||||
mat_grid.add_theme_constant_override("h_separation", UIScale.px(4))
|
||||
mat_grid.add_theme_constant_override("v_separation", UIScale.px(4))
|
||||
var mat_scroll := ScrollContainer.new()
|
||||
mat_scroll.custom_minimum_size = UIScale.v2(0, 176) # ~3 Reihen a 48 px + Separation
|
||||
mat_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
mat_scroll.add_child(mat_grid)
|
||||
terrain_palette.add_child(mat_scroll)
|
||||
var mat_labels := [
|
||||
"🌿 Gras", "🏖 Sand", "🪨 Fels", "🌱 Sattgras",
|
||||
"🍂 Vertrocknet", "🍀 Moos", "🟫 Erde", "🛤 Pfad",
|
||||
"⛏ Umgegraben", "🧱 Schotter", "🟤 Lehm", "🏜 Sand hell",
|
||||
"🏜 Sand rost", "🗻 Basalt", "⛰ Geröll", "🌧 Schlamm",
|
||||
"❄️ Schnee", "🥶 Matschschnee", "🌑 Asche", "🌾 Stoppeln",
|
||||
]
|
||||
for mat_id in SvmapIO.MATERIAL_IDS.size():
|
||||
var mat_btn := Button.new()
|
||||
mat_btn.text = str(mat_labels[mat_id])
|
||||
mat_btn.custom_minimum_size = UIScale.v2(0, 40)
|
||||
mat_btn.text = str(mat_labels[mat_id]) if mat_id < mat_labels.size() else str(SvmapIO.MATERIAL_IDS[mat_id])
|
||||
mat_btn.custom_minimum_size = UIScale.v2(0, 48)
|
||||
mat_btn.toggle_mode = true
|
||||
mat_btn.button_group = mat_group
|
||||
mat_btn.tooltip_text = "Malen: %s" % str(SvmapIO.MATERIAL_IDS[mat_id])
|
||||
mat_btn.tooltip_text = "Malen: %s (Slot %d)" % [str(SvmapIO.MATERIAL_IDS[mat_id]), mat_id]
|
||||
if mat_id == 0:
|
||||
mat_btn.button_pressed = true
|
||||
mat_btn.toggled.connect(_on_terrain_material_changed.bind(mat_id))
|
||||
terrain_mat_buttons.append(mat_btn)
|
||||
mat_row.add_child(mat_btn)
|
||||
mat_grid.add_child(mat_btn)
|
||||
|
||||
# Pinselgrößen-Presets (Karte t_603ec1a8): drei große Buttons + freier Slider.
|
||||
terrain_panel.add_child(_field_label("Pinselgröße:"))
|
||||
|
|
|
|||
|
|
@ -2,23 +2,51 @@ 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.
|
||||
// darf teurer sein (DECISIONS.md-Konvention).
|
||||
//
|
||||
// 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).
|
||||
// Karte t_ab408127 (Terrain-Texturierung): der Editor rendert DIESELBEN
|
||||
// Texturen wie der Match-Client — Albedo/Normal-Arrays über die Vertex-Alpha
|
||||
// (Material-ID), Worldspace-Tiling. Konturlinien liegen OBENDRAUF (dunkle
|
||||
// Multiplikation auf der texturierten Fläche). Ohne Arrays (uniforms nicht
|
||||
// gesetzt, Fallback-Klon) bleibt es beim reinen Vertex-Farb-Look.
|
||||
//
|
||||
// Farbquelle = COLOR: RGB = gebackenes Relief-Shading (Höhen-Kurve ×
|
||||
// Pseudo-Licht), A = Material-ID (m/N gepackt). Unshaded: das gebackene
|
||||
// Shading ist exakt sichtbar (deterministisch für die Render-Probe).
|
||||
render_mode unshaded, cull_back;
|
||||
|
||||
// Linienstärken (0 = aus). Terrai-Mesh setzt linien_an auf 0, wenn die
|
||||
// Linienstärken (0 = aus). Terrain-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;
|
||||
|
||||
// Textur-Arrays (t_ab408127) — identische Semantik wie terrain_splat.gdshader
|
||||
// im Client. Nicht gesetzt → ausschalten (vertex_only-Modus).
|
||||
uniform sampler2DArray albedo_array : source_color, filter_linear_mipmap, repeat_enable;
|
||||
uniform float layer_count = 20.0;
|
||||
uniform float tile_size = 1.0;
|
||||
uniform float texturen_an = 0.0; // 1.0 = Texturen aktiv (vom Mesh-Code gesetzt)
|
||||
|
||||
varying vec3 world_pos;
|
||||
|
||||
void vertex() {
|
||||
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec3 col = COLOR.rgb;
|
||||
// --- Textur (t_ab408127): Material-ID aus Vertex-Alpha, Worldspace-UV ---
|
||||
// Editor-Vorschau ALBEDO-ONLY (der Editor-Viewport ist unshaded; eine
|
||||
// Normal-Map würde ohne Licht ohnehin nichts zeigen — die Relief-Optik
|
||||
// kommt aus dem gebackenen Hillshade in COLOR.rgb).
|
||||
if (texturen_an > 0.5) {
|
||||
float mat_idx = clamp(floor(COLOR.a * layer_count + 0.5), 0.0, layer_count - 1.0);
|
||||
vec2 uv = world_pos.xz / max(tile_size, 0.05);
|
||||
vec4 alb = texture(albedo_array, vec3(uv, mat_idx));
|
||||
col = alb.rgb * col; // Tönung/Shading multipliziert (wie Client)
|
||||
}
|
||||
// --- Konturlinien (unverändert, Karte t_3ebe4086) ---
|
||||
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“
|
||||
|
|
|
|||
|
|
@ -29,9 +29,27 @@ extends Node3D
|
|||
## 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
|
||||
Color(1.0, 1.0, 1.0), # 0 = Gras — Karte t_ab408127: Textur liefert die
|
||||
Color(1.0, 1.0, 1.0), # 1 = Sand — Farbe; Vertex-RGB ist nur noch Shading-
|
||||
Color(1.0, 1.0, 1.0), # 2 = Fels — Multiplikator (Höhe × Hillshade),
|
||||
# 20-Slot-Palette: Weiß = neutral, Albedo-Textur trägt die Färbung.
|
||||
Color(1.0, 1.0, 1.0), # 3
|
||||
Color(1.0, 1.0, 1.0), # 4
|
||||
Color(1.0, 1.0, 1.0), # 5
|
||||
Color(1.0, 1.0, 1.0), # 6
|
||||
Color(1.0, 1.0, 1.0), # 7
|
||||
Color(1.0, 1.0, 1.0), # 8
|
||||
Color(1.0, 1.0, 1.0), # 9
|
||||
Color(1.0, 1.0, 1.0), # 10
|
||||
Color(1.0, 1.0, 1.0), # 11
|
||||
Color(1.0, 1.0, 1.0), # 12
|
||||
Color(1.0, 1.0, 1.0), # 13
|
||||
Color(1.0, 1.0, 1.0), # 14
|
||||
Color(1.0, 1.0, 1.0), # 15
|
||||
Color(1.0, 1.0, 1.0), # 16
|
||||
Color(1.0, 1.0, 1.0), # 17
|
||||
Color(1.0, 1.0, 1.0), # 18
|
||||
Color(1.0, 1.0, 1.0), # 19
|
||||
]
|
||||
# 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
|
||||
|
|
@ -79,13 +97,17 @@ func _ensure_mesh_instance() -> void:
|
|||
|
||||
|
||||
## 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).
|
||||
## Albedo + Höhenlinien). Karte t_ab408127: zusätzlich die Terrain-Texturen
|
||||
## (TerrainArrays.res — spiegelgleich zum Client-Pfad, aber EDITOR-EIGENE
|
||||
## Kopie der Resource-Klasse, Repos sind entkoppelt). 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
|
||||
_apply_texture_uniforms(mat)
|
||||
return mat
|
||||
push_warning("TerrainMesh: Kontur-Shader fehlt — Fallback StandardMaterial3D (unshaded).")
|
||||
var std := StandardMaterial3D.new()
|
||||
|
|
@ -95,6 +117,31 @@ func _make_material() -> Material:
|
|||
return std
|
||||
|
||||
|
||||
## Terrain-Texturen an den Kontur-Shader binden (t_ab408127). Quelle =
|
||||
## res://assets/textures/terrain/terrain_arrays.res — vom Sync-Skript
|
||||
## (tools/sync_terrain_tex.sh) aus dem Client-Klon gespiegelt, analog
|
||||
## tools/sync_models.sh (models/-Ordner-Muster). Fehlt die Resource
|
||||
## (frischer Editor-Klon ohne Sync), bleibt texturen_an=0 → reiner
|
||||
## Vertex-Farb-Look (Fallback, kein Crash).
|
||||
func _apply_texture_uniforms(mat: ShaderMaterial) -> void:
|
||||
const ARRAYS_PATH := "res://assets/textures/terrain/terrain_arrays.res"
|
||||
if not ResourceLoader.exists(ARRAYS_PATH):
|
||||
return # Fallback-Klon: texturen_an bleibt 0 (Vertex-Farb-Look)
|
||||
var arrays = load(ARRAYS_PATH)
|
||||
if arrays == null or not ("albedo_images" in arrays):
|
||||
return
|
||||
var imgs: Array = arrays.albedo_images
|
||||
if imgs.size() < 3:
|
||||
return
|
||||
var albedo := Texture2DArray.new()
|
||||
albedo.create_from_images(imgs)
|
||||
mat.set_shader_parameter("albedo_array", albedo)
|
||||
mat.set_shader_parameter("layer_count", float(imgs.size()))
|
||||
mat.set_shader_parameter("tile_size",
|
||||
float(arrays.tile_size) if arrays.tile_size > 0.0 else 1.0)
|
||||
mat.set_shader_parameter("texturen_an", 1.0)
|
||||
|
||||
|
||||
## UI-Toggle „Höhenlinien“ (main.gd): ohne Rebuild an/ab.
|
||||
func set_contours(on: bool) -> void:
|
||||
_contours_pref = on
|
||||
|
|
@ -250,8 +297,10 @@ func _apply_contour_state(span_ok: bool) -> void:
|
|||
## 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).
|
||||
## Alpha = Material-ID (Karte t_ab408127): m/N gepackt — der Shader (Client
|
||||
## UND Editor-Vorschau) sampelt die Texture2DArray-Layer über die Alpha.
|
||||
## Der Shading-Faktor wirkt NUR auf RGB (Color × float skaliert auch Alpha —
|
||||
## deshalb wird die ID erst im Return gezielt gesetzt).
|
||||
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)
|
||||
|
|
@ -271,7 +320,7 @@ static func relief_color(mat_id: int, h: float, hmin: float, hmax: float,
|
|||
# genauso — der Helfer muss die RENDER-Wahrheit liefern (Probe-Vergleich),
|
||||
# nicht die unbehandelte Mathematik.
|
||||
return Color(clampf(base.r * f, 0.0, 1.0), clampf(base.g * f, 0.0, 1.0),
|
||||
clampf(base.b * f, 0.0, 1.0), 1.0)
|
||||
clampf(base.b * f, 0.0, 1.0), float(m) / float(MATERIAL_COLORS.size()))
|
||||
|
||||
|
||||
func triangle_count() -> int:
|
||||
|
|
|
|||
9
tests/probe_arrays_load.gd
Normal file
9
tests/probe_arrays_load.gd
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
extends SceneTree
|
||||
func _initialize() -> void:
|
||||
var arr := load("res://assets/textures/terrain/terrain_arrays.res")
|
||||
if arr == null:
|
||||
print("LOAD_FAIL: null")
|
||||
quit(1); return
|
||||
var imgs: Array = arr.albedo_images
|
||||
print("ARRAYS_LOAD_OK: %d Albedo-Layer, l0_null=%s" % [imgs.size(), str(imgs[0] == null)])
|
||||
quit(0 if imgs.size() == 20 and imgs[0] != null else 1)
|
||||
1
tests/probe_platzierung_ident.gd.uid
Normal file
1
tests/probe_platzierung_ident.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://duiquotetxj3j
|
||||
|
|
@ -328,13 +328,23 @@ func _test_material_format() -> void:
|
|||
"V15: falsche Material-Länge abgelehnt")
|
||||
|
||||
# V15: unbekannte Material-ID → Ablehnung.
|
||||
# Karte t_ab408127: Palette 3→20 — ID 3 ist jetzt GÜLTIG, der Test nutzt
|
||||
# 20 (erster Wert oberhalb der neuen Obergrenze).
|
||||
bad = doc.duplicate(true)
|
||||
var mraw := PackedByteArray()
|
||||
mraw.resize(129 * 129)
|
||||
mraw[5000] = 3
|
||||
mraw[5000] = 20
|
||||
(bad["terrain"] as Dictionary)["materials_r8_b64"] = Marshalls.raw_to_base64(mraw)
|
||||
_check(_has_err(SvmapIO.validate_map(bad, {}), "V15"),
|
||||
"V15: unbekannte Material-ID abgelehnt")
|
||||
"V15: unbekannte Material-ID abgelehnt (ID 20 > Palette 0–19)")
|
||||
# Neu: GÜLTIGE neue ID (3 = gras_satt) muss DURCHLASSEN werden.
|
||||
var good_new := doc.duplicate(true)
|
||||
var gmraw := PackedByteArray()
|
||||
gmraw.resize(129 * 129)
|
||||
gmraw[5000] = 3
|
||||
(good_new["terrain"] as Dictionary)["materials_r8_b64"] = Marshalls.raw_to_base64(gmraw)
|
||||
_check(not _has_err(SvmapIO.validate_map(good_new, {}), "V15"),
|
||||
"V15: neue Palette-ID 3 (gras_satt) wird akzeptiert")
|
||||
|
||||
# Legacy-v1: OHNE Material-Schicht lädt und gilt als alles Gras.
|
||||
var legacy: Dictionary = doc.duplicate(true)
|
||||
|
|
@ -803,18 +813,25 @@ func _test_relief_shading() -> void:
|
|||
stufe_delta, _old_step_delta()])
|
||||
view.free()
|
||||
|
||||
# Material-Farben bleiben unterscheidbar NACH dem Shading (gleiche
|
||||
# Höhe/Neigung → nur die Palette trennt):
|
||||
# Material-Zellen unterscheiden sich NACH dem Shading (gleiche
|
||||
# Höhe/Neigung → nur die Palette trennt).
|
||||
# Karte t_ab408127: Palette neutral-weiß — unterscheidbar sind die Zellen
|
||||
# über die ALPHA (= Material-ID fürs Texture2DArray).
|
||||
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")
|
||||
_check(absf(s.a - g.a) > 0.01 and absf(f.a - g.a) > 0.01
|
||||
and absf(f.a - s.a) > 0.01,
|
||||
"Material: Gras/Sand/Fels via Alpha (Material-ID) unterscheidbar")
|
||||
# Neu-Palette: Slot 3 (gras_satt) packt auf 3/20 — Arsenal erweitert.
|
||||
var g3 := _relief_color(3, 5.0, 0.0, 10.0, up)
|
||||
_check(absf(g3.a - 3.0 / 20.0) < 0.02,
|
||||
"Material: neuer Slot 3 packt auf Alpha 3/20 (Texture2DArray-Layer)")
|
||||
|
||||
# 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)")
|
||||
# Alpha-Kanal trägt die Material-ID (t_ab408127), das Shading skaliert
|
||||
# NUR RGB (t_603ec1a8-Falle bleibt für den RGB-Anteil bestehen).
|
||||
var alpha_rgb_ok := tal.r >= 0.0 and huegel.r >= 0.0 and hell.r >= 0.0 and dunkel.r >= 0.0
|
||||
_check(alpha_rgb_ok, "Alpha: Material-ID gepackt, RGB = Shading (kein Crash)")
|
||||
|
||||
# Höhenlinien-Toggle + Flach-Map-Schutz (Uniform am echten Material).
|
||||
var flat := PackedFloat32Array()
|
||||
|
|
|
|||
47
tools/sync_terrain_tex.sh
Normal file
47
tools/sync_terrain_tex.sh
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#!/usr/bin/env bash
|
||||
# Terrain-Textur-Sync (Karte t_ab408127): terrain_arrays.res + Thumbnails aus
|
||||
# dem Client-Klon in den Map-Editor spiegeln (analog tools/sync_models.sh).
|
||||
# Die CC0-Texturen leben EINMAL im Client-Repo; der Editor lädt sie via
|
||||
# res://assets/textures/terrain/ (Muster: models/-Ordner mit .gdignore).
|
||||
#
|
||||
# Aufruf: aus dem Map-Editor-Repo → tools/sync_terrain_tex.sh [--check]
|
||||
set -u
|
||||
MODE="sync"
|
||||
if [ "${1:-}" = "--check" ]; then MODE="check"; fi
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CLIENT="${SCOPEDVR_CLIENT_DIR:-}"
|
||||
for k in "$DIR/../scopedvr-client" "$DIR/../../scopedvr-client"; do
|
||||
[ -d "$k/assets/textures/terrain" ] && CLIENT="$(cd "$k" && pwd)" && break
|
||||
done
|
||||
if [ -z "$CLIENT" ]; then
|
||||
echo "FEHLER: Client-Klon mit assets/textures/terrain nicht gefunden."
|
||||
echo " SCOPEDVR_CLIENT_DIR=/pfad/zum/client setzen oder Klon nach ../scopedvr-client."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DST="$DIR/assets/textures/terrain"
|
||||
mkdir -p "$DST"
|
||||
touch "$DST/.gdignore" # Godot soll die Rohdateien NICHT importieren
|
||||
|
||||
changed=0
|
||||
for f in terrain_arrays.res terrain_thumbs.png terrain_thumbs.json; do
|
||||
if ! cmp -s "$CLIENT/assets/textures/terrain/$f" "$DST/$f" 2>/dev/null; then
|
||||
if [ "$MODE" = "check" ]; then
|
||||
echo "ABWEICHUNG: $f"
|
||||
changed=1
|
||||
else
|
||||
cp -f "$CLIENT/assets/textures/terrain/$f" "$DST/$f"
|
||||
echo "kopiert: $f"
|
||||
changed=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$MODE" = "check" ]; then
|
||||
[ "$changed" -eq 0 ] && echo "--check: terrain-Texturen aktuell (Exit 0)." && exit 0
|
||||
echo "--check: terrain-Texturen weichen ab (Exit 1)."
|
||||
exit 1
|
||||
fi
|
||||
echo "Sync fertig: $DST"
|
||||
exit 0
|
||||
Loading…
Reference in a new issue