- BauKatalog: 12 Teile (Fundament, Boden, Wände mit Tür/Fenster/Scharte, Brüstung, Pfeiler, Giebel, Dächer, Treppe) auf Basis der CC0-Geometrie des Modular Builder 0.2.0, Wandhöhe 3 m
- BuildSnap: Wände auf Zellkanten, Böden in Zellmitte, Höhe aus Ebenen statt Klickhöhe, Ersetzen (Tür in Wand)
- Gebäude kopieren/auswählen/löschen im Bauen-Tab, Kopien bekommen eigene Gruppe
- Kollision serverkonsistent über Trimesh-Dateien (Türen/Fenster echte Löcher), tools/gen_bauteile.gd erzeugt GLB, Kollision, Registry-Fragment
- Alte gebaeude-Sektion wird beim Laden migriert, altes Bauen-Werkzeug entfernt, Resize behält Gruppennamen
- Tests: test_bauen (258 Checks), Playtest läuft Treppe/Tür
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sly 25.10.: Texturen wiederholen sich zu krass, „Gras“ sieht nicht nach Gras aus.
- Kachel 3 m statt 1 m; zweite Abtastung gedreht + 2,3× größer, großflächiges
Rauschen mischt beide, zweites Rauschen variiert die Helligkeit (Spiegel Client).
- Material 0 (gras_steppe) heißt im Editor „Steppe“ (Format unverändert);
Hilfetexte nachgezogen.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sly 25.10.: nah gezoomt gerade, rechteckige harte Kanten in den Verläufen.
Ursache: sin() großer Argumente ist auf der GPU ungenau, die Warp-Noise
bekam rasterförmige Sprünge. Mit malentest.svmap reproduziert (ohne Warp
weg, mit Ganzzahl-Hash weg, Warp bleibt organisch).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sly 25.10.: „weich“ soll ein Textur-Verlauf am Pinselrand sein, nicht fleckig;
gerade Strichkanten wirkten weiter hart (1-m-Übergang).
- Format (optional, alte Karten gültig): materials_b_r8_b64 (zweites Material
je Punkt) + materials_w_r8_b64 (Anteil 0–255); ohne Mischung fallen die
Felder weg. V15 prüft Länge/IDs/Paarigkeit.
- TerrainField.apply_material_brush_mix: innen voll, außen Smoothstep-Anteil;
nur verstärken, nie abschwächen; Gras drübermalen mischt zurück.
- Shader: Top-2 aus 4 Nachbarpunkten × (a, b) mit Anteilen → nur noch 2
Albedo-Fetches; Warp bleibt.
- Pinsel/Live-Vorschau, Spiegeln, Symmetrie-Analyse/-Anwenden, Resize,
Anheben, Undo (terrain-Snapshot) tragen die Misch-Schicht mit.
- Streu-Rand (vorheriger Commit) entfernt.
- Tests: test_mal_fein 30/30 (Verlauf fällt weich, nie abschwächen,
zurückmischen, Kodierung), Editor-Suite unverändert grün bis auf die
bekannten Umgebungs-Tests; Kette Editor → Server → Client per
run_map_download_e2e 15/15 mit gemischter Karte.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sly 25.10.: „immer noch harte Kanten beim Malen“. Ursache: die Splat-Textur
hatte beim feinen 1-m-Raster genau einen Texel je Materialpunkt und wird
nearest gesampelt → jede Zelle ein harter Block.
- Shader liest die Material-ID-Karte (R8, ein Texel je Punkt, texelFetch)
und mischt PRO PIXEL bilinear über die 4 Nachbarpunkte.
- Leichter Domain-Warp (Value-Noise in Rasterzellen, eine Oktave) verwackelt
die Abtaststelle → keine Rastertreppen, natürliche Kante.
- Malen aktualisiert nur noch die Bytes der Karte (kein Splat-Neuberechnen).
- TerrainArrays.material_image (Spiegel-Datei, identisch im Client).
- Render-Probe tests/probe_mal_weich_render.gd (vorher/nachher geprüft).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sly: „feiner malen“. Die Material-Schicht bekommt ein eigenes Raster
(terrain.material_resolution, optional; Default = resolution → alte Karten
unverändert gültig): ~1 m pro Punkt, auf dem Höhenraster ausgerichtet,
max. 1025 (1024 m → 1025, 256 m → 257, 2048 m → 1025 = 2 m).
- SvmapIO: material_resolution / material_resolution_for / resample /
upgrade_material_layer / material_at_height_point; V15 prüft Raster + Länge.
- Neue Karten starten fein; beim Öffnen werden alte Karten optisch identisch
angehoben (Datei ändert sich erst beim Speichern); Resize rechnet die
Schicht auf ihrem eigenen Raster an der Weltposition um.
- Pinsel, Spiegeln (Symmetrie live + „Symmetrie anwenden“) und Live-Splat
arbeiten auf dem Material-Raster; Vertex-Farben lesen den passenden Punkt.
- Malen-Hinweis zeigt die echte feinste Einheit.
- Tests: test_mal_fein 22/22 (Raster, Anheben, 2-m-Pinsel ≈ 13 Punkte,
V15, Roundtrip, Resize); test_terrain/test_terrain_mirror/
test_symmetry_onclick auf das neue Raster nachgezogen; Identitäts-Runner OK.
Kette Editor → Server (Prüfsumme) → Client per run_map_download_e2e 15/15.
Format-Spiegel: Match-Server svmap_loader (V15) + Client svmap_terrain.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Gleiche Ursache wie im Client (BEWEISE.md in work/terrain_blending_v2_t_5c94d098_bilder/):
splat_side_for(129) = 512 px = 2 m/Texel auf der 1024-m-Standardmap (8 m/Zelle)
-> Gewichtsrampe in 2-m-Stufen, sichtbar als Baender/Stufen an der Materialgrenze.
Aenderung Editor:
- library/terrain_arrays.gd (Spiegel-Datei, byte-identisch zum Client):
splat_side_for(res, map_size_m = 0.0) = MAX(4 Texel/Zelle, 1 m/Texel aus der
Karten-Kantenlaenge), Zweierpotenz, geklemmt [256, 1024]; build_splat_bytes
ohne Allokation je Texel + Schnellpfad (gleiche ID in allen vier Nachbarzellen)
-> unveraenderte Bytes, deutlich schneller.
- src/viewport/terrain_mesh.gd: _build_splat bekommt size_x/size_z und gibt sie an
splat_side_for; der Live-Sub-Rect-Pfad greift nur noch, wenn auch die
Kantengroesse stimmt (Size-Aenderung ohne res-Aenderung baut die Splat neu).
- tests/test_terrain_splat.gd (Spiegel, identisch im Client): +6 Kartenmass-Checks
(31/31).
Suiten (frischer Forgejo-Klon): test_terrain_splat 31/31, test_terrain 155/155,
test_terrain_mirror 106/106.
Neu shaders/selection_outline.gdshader: Inverted-Hull-Außenhülle (unshaded,
cull_front, Grow entlang der Normalen; Streifen-Uniform für den Zwilling).
Markierung jetzt über Outline-Kind am Mesh (Auswahl/Hover/Partner); Farben +
Breiten aus theme/editor_theme.tres (selection_outline*, *_width_mm) — eine
Quelle. material_override wird von der Markierung nicht mehr angefasst:
GLB-Texturen bleiben im Auswahl-Zustand voll sichtbar, der Tint bleibt beim
Markieren sichtbar (kein "Orange gewinnt" mehr).
Spawn-Zonen: konstante Disc-Deckkraft + dezenter Kontur-Ring (Torus) statt
Aufhellung/Emission; Label-Farbe aus dem Theme statt hartkodiertem Gelb.
Bau-Teile: Kontur um das Bauteil-Mesh statt halbtransparenter Vollbox.
Neu: set_hover()-API (schwächste Stufe) — Anschluss an die Mausbewegung
liegt in interaction.gd (Grenze dieser Karte) und folgt als eigene Karte.
Suite tests/test_selection_look.gd (41 checks: Theme-Quelle, Grundmaterial
unangetastet, Outline nur bei Markierung, restlos weg bei Abwahl, Zwilling
dünner+gestrichelt, Spawn-Ring, Bau-Teil-Kontur). Altsuiten an die neue
Semantik angepasst (test_glb_view: GLB-Auswahl ohne Override; test_map_tint
T3: Kontur statt Orange-gewinnt; test_object_mirror: Partner per Kontur).
Beweise: 4 Renderproben (fass, echtes GLB — vorher Vollorange / nachher
Kontur, je auf Sand und Fels) in /opt/data/scopedvr/work/u6_auswahl_beweise/.
Voll-Batterie 32/32 grün (EXIT 0) inkl. der drei angepassten Suiten.
Operator-Befund aus dem Eigenstaendigkeits-Beweis auf dem PC (20.10.):
Die App baute den Registry-Pfad nur als <quelle>/<rel-pfad-mit-Praefix
'assets/objekte/'>. Der mitgelieferte models/-Ordner NEBEN der App enthaelt die
Pack-Struktur aber DIREKT (models/military_desert/props/box_010.glb). Ergebnis:
291 von 334 Modellen (alle verschachtelten Pack-Objekte — genau die, mit denen
die Karten gebaut werden) wurden in der eigenstaendigen App nicht gefunden.
Fix: zusaetzlicher Kandidat <quelle>/<rel-pfad OHNE 'assets/objekte/'> — der
flache Alt-Pfad bleibt unveraendert.
Belegt (exportierte App, nacktes Umfeld, frisches HOME):
box_010 -> models/military_desert/props/box_010.glb (vorher: leer)
baum_kiefer_1 -> models/baum_kiefer_1.glb
fehlende_glbs=0 PROBE ERGEBNIS: OK
Suiten: gui_smoke 92 OK, Import 0 Fehler.
fit_to_document setzt die Ansicht jetzt auf START_DISTANCE_M = 80 m über der
Kartenmitte (XZ-Mitte der Map-Bounds, Höhe = Gelände dort), yaw 45°, pitch 35°.
Die Regel ist unabhängig von Kartengröße (256/1024/4096) und Inhaltslage;
leeres Dokument ebenfalls 80 m. Statuszeile: „Ansicht: Kartenmitte, Abstand
80 m (Pos1 = ganze Karte).“ Pos1/Home bleibt fit_to_bounds = ganze Karte
(MAP-Bounds werden weiterhin gemerkt, nicht die Content-Bounds).
Ziel-Y bewusst auf der Geländehöhe: die y-Mitte der Bounds liegt bei neuen
Maps 35 m über dem Boden — damit hätte die Ansicht über den Boden gezielt und
die Objekte stünden am unteren Bildrand.
Suite test_orbit_camera auf die neue Regel umgestellt (50 Checks):
Startabstand/Richtung, 80-m-Regel über Größen und Inhaltslagen, leeres
Dokument, „Pos1 ≠ Startansicht“ (80 m → 1215,6 m, alle 8 Map-Ecken).
Dazu Renderprobe tests/probe_k1d_render.gd (2 PNGs).
aa94bde hat im Live-Pfad die alten Camo-Dateien (desert_multicam_*) verwendet und
zusaetzlich per Inhaltsvergleich auch KORREKTE Texturen wieder durch Camo ersetzt
- das haette die Palette-Arbeit aus t_279df760 im Editor rueckgaengig gemacht.
Jetzt: Zuordnung Materialname -> Pack-Textur (military_base -> Demonstration_Textures1.png,
picture_1/2/3 -> Demonstration_picture_N.png), Pfad ../../../textures/military_desert/
(Client-Repo, dort liegen die Dateien seit 88fded1). Fuer den Inhalt gibt es EINE Quelle:
tools/PACK_TEXTURE_MAPPING.md. Fuer den Inhalt gibt es EINE Quelle: dieselbe Tabelle.
Flaechen mit bereits aufgeloester Textur bleiben unangetastet.
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.
Sly-Befund: Beim Heben/Senken über gemalten Sand/Fels verschwanden die
Material-Farben live (alles Gras), erst am Stroke-Ende kehrten sie mit
dem Vollrebuild zurück.
Root-Cause: terrain_tool rief im Live-Pfad (Heben/Senken/Glätten)
update_terrain(_heights) OHNE Material-Schicht auf — _mats wurde nur im
Malen-Modus dekodiert — und terrain_mesh fällt bei leerer Schicht auf
alles-Gras zurück (Legacy-Fallback).
Fix (zwei Ebenen):
- terrain_tool._refresh_from_doc dekodiert die Material-Schicht immer,
der Live-Stroke übergibt sie mit (Heben/Senken/Glätten wie Malen).
- terrain_mesh cached die zuletzt gesehene Schicht: Live-Updates ohne
mats-Param nutzen denselben Farb-Pfad wie der Idle-Rebuild; Legacy-v1
(nie gesehene Schicht) bleibt alles Gras — kein Phantom-Cache.
Test: tests/test_terrain.gd neue Gruppe „Live-Material-Farben" —
per-Frame-Check (5 Frames Heben über Sand-Zone, nicht nur Endzustand),
terrain_tool-Integration (Heben-Stroke übergibt Sand-Schicht, Stroke-Ende
= EIN Commit), Legacy-Gras ohne Phantom-Cache. Suite: 99 checks ALL OK;
alle weiteren Headless-Suiten grün (test_glb_view 12/22 FAIL ist
vorbestehend auf origin/main — GLB-Quellen fehlen im Workspace).
- Terrain-Pinsel: Rechtsklick startet keinen Strich mehr; Strg+LMB invertiert
(Heben↔Senken, Blender/Photoshop-Konvention; Malen: Strg+Klick = Gras zurück).
- Platzieren: RMB bricht nicht mehr ab — Objekt bleibt in der Hand, Abwurf nur
über Esc. build_tool/spawn_tool: RMB-Deselect/Werkzeugwechsel entfernt
(Auswahl-Deselect = Esc / LMB auf Leere).
- orbit_camera: rmb_filter entfernt — Flug startet in JEDEM Werkzeugzustand.
- docs/tasten.md + H-Overlay + Status-/Paneltexte aktualisiert.
- Neuer Test tests/test_rmb_vorrang.gd (32 Checks, echte Main-UI headless);
in tools/run_tests.sh aufgenommen. Suite grün (test_glb_view: 12/22 FAIL auch
auf origin/main — fehlende GLB-Quellen im Scratch-Workspace, umgebungsbedingt).
Root-Cause 1 (Spiegelung): terrain_mesh.gd positionierte die Mesh-Vertices
gespiegelt (+0.5 − index·cell in X UND Z), während ALLE anderen Verbraucher
der Heightmap der Dokument-Konvention folgen (Index 0 = −X/−Z-Ecke):
SvmapDocument.terrain_height_at, TerrainField.height_at/apply_brush und der
Match-Client (svmap_terrain.gd). Der Renderer war der einzige Ausreißer —
der Pinsel-Strahl traf das gerenderte Mesh punktgespiegelt um die Map-Mitte
(Sly-Befund: Erhöhung erschien an ganz anderer Stelle). Fix: Vertices auf
−size*0.5 + index*cell (identisch zum Client), Normalen-Vorzeichen angepasst
(n ∝ (−∂h/∂x, 1, −∂h/∂z)); Winding bleibt gültig (180°-Rotation erhält die
Orientierung, Kreuzprodukt-Normale weiter nach unten = Frontseite).
Root-Cause 2 (Tangenten-Graze): Ein Strahl, der exakt auf eine Hügelkuppe
zielt, grast die gewölbte Oberfläche — f(t) bleibt minimal knapp über 0 und
steigt danach wieder, obwohl weiter hinten (VERDECKT hinter dem Hügel) noch
ein Vorzeichenwechsel an der Basis-Ebene lag. Der March lief durch und der
Pinsel sprang auf den verdeckten Basis-Punkt meterweit weg. Fix in
TerrainField.raycast: Regel 'erster sichtbarer Oberflächen-Kontakt gewinnt' —
kommt f innerhalb des Bandes (½ March-Schritt) an die Oberfläche und entfernt
sich der Strahl wieder, wird das Graze-Minimum lokal per Ternärsuche
verfeinert und als Treffer zurückgegeben. Echter Treffer (f ≤ 0) gewinnt
weiterhin immer; betrifft auch Bauen-Werkzeug und Spawn-Zonen (gleiche
Raycast-Routine).
Tests (test_terrain.gd, 64 → 86 Checks): Neue Gruppe 'Ray→Terrain-Mapping'
mit simulierter Kamera (SubViewport, schief + steil): exakter Screen-Punkt
(unproject) → project_ray → raycast → asserted Treffer-Zelle == Cursor-Zelle
(< 1 Zelle) auf Mitte/Innenraum/Corner/Kanten; Brush-Stroke am Treffer mit
Höhen-Änderungs-Zentrum == erwarteter Zelle; erhabenes Terrain (Strahl trifft
NEUE Oberfläche, y = Hügelhöhe); Mesh-Vertices komplett gegen die
Dokument-Konvention asserted (Root-Cause-Regression). Hügel-Normalen-Checks
auf die korrigierte Konvention umgestellt (lehnt jetzt −X/−Z).
Suite: 7 Editor-Suiten 463 Checks grün + Export 21/0 + GUI-Smoke 20 grün.
Root-Cause für Slys Gerätesymptom (dunkle Platte + Gitter, Terrain „wacht erst nach langem Drücken auf“): terrain_mesh.gd baute alle Dreiecke von oben CCW = Backface → komplette Deckfläche wurde von oben ge-cullt; sichtbar war nur der dunkle Sky-Boden (0.1/0.1/0.12) plus Gitter-Overlay. Gehobene Flanken waren nur sichtbar, wenn sie über die unsichtbare Deckfläche ragten (Rückenansicht) — daher „wirkt der Pinsel erst nach langem Drücken“.
- terrain_mesh.gd: Dreiecks-Indices gespiegelt [i, i+1, i+res, i+1, i+res+1, i+res] (CW von oben = Frontseite, Godot-Konvention); Normalen-Gradient korrigiert (x-Index läuft gegen Welt-X: n ∝ ((hx_r-hx_l)·cell_z, 2·cell_x·cell_z, (hz_u-hz_d)·cell_x)) — Hänge waren vorher falsch beleuchtet. MeshInstance lazily erzeugt (update_heights sicher vor _ready).
- map_view_builder.gd: Gitter default AUS über sichtbarem Terrain (grid_node), ohne Terrain-Block als Boden-Referenz sichtbar; set_grid_visible()/grid_is_visible() ohne Rebuild.
- main.gd: Toggle-Knopf „⊞ Gitter“ in der Top-Bar; Nutzer-Wahl überlebt jeden Viewport-Rebuild.
- Erster Pinselkontakt: mit sichtbarem Terrain wirkt der Live-Pfad ab Frame 1 (code-seitig korrekt, war nur unsichtbar).
Tests: test_terrain 64 Checks (NEU: Winding aller Dreiecke = Frontseite von oben, Normale h=0 = (0,1,0), X/Z-Hügel-Normale nicht invertiert, erster RAISE-Druck verschiebt Vertex messbar im Mesh, Gitter-Default/Toggle/Boden-Referenz). Suite 441 Checks + Export-Suite 21 grün; --import ohne Parse-Error.