Die bisherige Y-Check-Methode versagte bei Tunneln aus Sculpted Mesh,
weil bakeVoxelSurface nur VoxelChunk-Daten liest und NaN liefert.
- loadCaveMask: buildCaveFootprintMask bei 2048px statt buildCaveTunnelMask
bei 1024px (kein MIN_FOOTPRINT-Filter der schmale Tunnel eliminiert)
- isCavePoint: erkennt Maskengröße automatisch (1024 oder 2048px)
- buildTunnelContourPaths: Größe dynamisch aus Maskenlänge
- isNodeUnderground: Cave-Maske als primäre Prüfung, Y-Check als Fallback
- Segment-Abtastung: 7 Punkte statt Mittelpunkt (erkennt Tunnel an jeder
Position im Segment, nicht nur bei 50%)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Weltkarte: Cave-Overlay (brown fill + Kontur) vollständig entfernt,
war nie gewünscht; drawTunnelOverlay tut nun nichts
- Pfad-Detection: Cave-Maske als Filter entfernt; Grund: MIN_FOOTPRINT_
Filter in buildCaveMasks (≥40 Pixel bei 1024px) schmeißt schmale
Tunnel raus und würde Tunnel-Segmente blocken. Stattdessen reiner
Y-Check: voxelSurface > pathY + 3m (Tunneldecke direkt über dem Weg)
- Höhlenkarte: Pixel-Bild ersetzt durch Vektor-Füllung (gc.fill auf
tunnelContourPaths) → bleibt bei jedem Zoom-Level scharf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Pfad-Split: Sandwich-Maske (per-Spalte, fragil) ersetzt durch
Chunk-Level-Cave-Maske + Y-Prüfung; erkennt schmalere Tunnel
zuverlässig und vermeidet extra Disk-Scan
- Weltkarte: Tunneleingang-Halbkreise ersetzt durch gefüllte
Fläche (semi-transparent braun) + glatte Chaikin-Kontur
(ähnlich Wasser-Rendering)
- Höhlenkarte: smooth Kontur-Overlay (warmweiss) um die
Tunnelfläche ergänzt, zusätzlich zu den D-Eingängen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Zoom-Bug behoben: CaveMapView auto-lud mit Canvas 0×0 (scale=1.0 ungültig)
→ Tab-Sync jetzt nur wenn isViewInitialized() (zoomLevels != null)
- Layout-Fix: CaveMapView-Statusleiste unter dem Canvas (wie WorldMapView),
nicht mehr darüber → Canvas-Höhe jetzt identisch → Position-Sync 1:1 korrekt
- Auto-Load aus CaveMapView.redraw() entfernt; deferred fitToView() stattdessen
- Pfad-Split: Sandwich-Maske (solid→void≥3→solid per Spalte) verhindert
False-Positives an Klippen-Füßen; Wege werden nur als unterirdisch erkannt
wenn in ihrer X,Z-Spalte ein echter Hohlraum (Tunnel) vorliegt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pfad-Trennung (Oberfläche vs. Untergrund):
- loadPathChains() speichert jetzt Y-Koordinate an Index 2
- splitPathChains() prüft voxelSurface(x,z) > node_y + 3m statt
2D-Cave-Maske; erkennt damit alle Tunnel-Typen korrekt
- Bisektion interpoliert Y für präzisen Grenzpunkt
Höhlenkarte Features:
- Label-Toggle (Bearbeiten-Modus für Area-Labels) + Klick-Handler
- PNG-Export (⇩) rendert 4096×4096 via buildCaveMap()
- mapData wird gecacht für späteren Export ohne Reload
Zoom/Position-Sync:
- Tab-Listener liest Werte vor dem Wechsel, übergibt sie per
Platform.runLater() NACH JavaFX-Layout → kein Sprung mehr
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pfad-Ketten werden exakt an Tunnel-Grenzen per Bisektion aufgesplittet
(12 Iterationen ≈ 0.05m Genauigkeit). Gebäude und Areas werden anhand
des Footprint-/Schwerpunkt-Punkts der Cave-Maske zugeordnet.
- WorldMapView: zeigt nur Oberflächen-Segmente (splitChains[0])
- CaveMapView: zeigt nur Tunnel-Segmente (splitChains[1])
- Cave-Maske: buildCaveTunnelMask bei 1024px, einmal pro Lade-Zyklus
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CaveMapView: Layer-MenuButton (Eingänge, Gebäude, Wege, Areas), Refresh-Button, Vollbild-Modus analog WorldMapView
- WorldMapView + CaveMapView: getScale/getPanX/getPanY + setView() für Tab-Wechsel-Sync
- EditorApp: caveMapView als Feld; beim Tab-Wechsel werden Zoom und Position übernommen; Vollbild-Callbacks für Höhlenkarte
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nur Chunks mit echtem Sandwich-Tunnel (solid → void ≥3m → solid) werden
für die voxelSurface-Berechnung herangezogen. Isolierte Voxel-Strukturen
(cx=16/17) werden so automatisch ausgeschlossen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: Fill nutzt fsfz = (WH-wz)/WS * FD (Skala: /FD=512),
MS-Grid nutzt msW → / (fdSize-1)=511. Ergibt ~2m Versatz in Kartenmitte.
Fix: h-Feld bei SEA_MASK_SIZE mit exakt der Fill-Formel aufbauen
(* FD statt * (FD-1)), dann MS mit thr=0 auf diesem h-Feld.
Die Isokontur liegt damit pixelgenau auf der Fill-Grenze.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Vorher: Density → binäre Maske (-1/+1) → MS bei 0.5 → Boundary 75% vom
Zellrand entfernt (falscher Offset, unabhängig von echten Density-Werten).
Jetzt: h = threshold - density → MS direkt auf Density-Werten bei thr=0
→ msLerp interpoliert genau an der echten threshold-Isokontur, identisch
zur Fill-Grenze in computeForestPixels.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Chaikin corner-cutting bewegt Punkte nach innen → Outline lag kleiner als
die Fill-Fläche. Die Wald-Density-Boundary ist bereits glatt (Summe
kreisförmiger Kernels), braucht kein Smoothing.
- assembleClosedPaths(segs, chaikinIter) extrahiert aus buildSeaCoastPaths:
chaikinIter=0 → Pfade ohne Glättung, liegen exakt auf der Fill-Grenze
- buildForestOutlinePaths nutzt assembleClosedPaths(..., 0)
- buildSeaCoastPaths delegiert weiterhin mit chaikinIter=3 (Küste braucht
Glättung wegen der stepped Terrain-Height-Boundary)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- buildForestOutlinePaths: bilineare Interpolation statt NN → Outline liegt
exakt auf der gleichen Schwelle wie die Füllung in computeForestPixels
- Perimeter-Filter: Pfade mit Umfang < 60m² werden verworfen (Artefakte
aus 1-2 isolierten Bäumen)
- Threshold 2.0 → 3.0: 2 Bäume ergeben max. Density=2, liegen damit unter
der Schwelle → kein Wald-Ring mehr bei Kleingruppen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- buildForestOutlinePaths: Density-Map hat Z invertiert (Nord=mz=0) gegenüber
Marching-Squares-Koordinaten (Süd=mz=0) → gespiegelte Konturen landeten
off-screen, kein sichtbarer Wald-Rand. Fix: dz = fdSize-1-mz beim Samplen.
- Render-Reihenfolge korrigiert: Klippen → Wald → Wasser → Areas
(Wald war über Wasser, sollte darunter liegen)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fixWindingForClip(): äußere Pfade CCW, innere Pfade (Löcher/Berg-Inneres) CW
→ gc.clip() füllt mit Non-Zero-Rule nur das echte Kliff-Band, nicht den
gesamten eingeschlossenen Bereich inkl. Berggipfel
- Schraffierungs-Liniendicke = overlayLineWidth() (identisch zu Konturlinie)
- Schraffierungs-Abstand = 4× Liniendicke → konstante ~25% Flächendeckung
über alle Zoom-Stufen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Kliff-Bereiche: diagonale Schraffierung (45°, ~5.5px Abstand) im gleichen
Grauton via gc.clip() auf die Marching-Squares-Pfade
- Klippen- und Wald-Konturlinien: 1.2px bei Zoom 0-2, 2.5px bei Zoom 3,
3.5px bei Zoom 4 (deutlich dicker zum Reinzoomen)
- Wald-Umrandung: selber Stil und Dicke wie Klippen, aber dunkelgrün (28/78/12)
- Render-Reihenfolge bleibt: Klippen → Wasser → Wald
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Cliff- und Wald-Randlinien via Marching Squares + Chaikin als Canvas-Vektoren
statt Pixel-Kanten im Raster → immer scharf wie Küstenlinie und Areas
- COARSE von 512px (4m/px) auf 256px (8m/px) erhöht + Bilinear-Interpolation
der Flachfarben → dünne Texturen (<8m) verschwinden, keine Block-Artefakte
bei Hires-Zoom
- Render-Reihenfolge: Klippen → Wasser → Wald (laut User-Anforderung)
- buildCliffOverlay: nur noch weiche Grau-Füllung, keine pixel-basierten Kanten
- computeForestPixels: nur noch Füllung, Randlinie kommt als Vektor-Overlay
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Terrain-Textur-Overlay (Splatmap-Blend, Klippen, Wald) als Dirty-Flag-Cache
- Wald wird nach Wasser gerendert, 50% Deckkraft, Schwelle 3+ Bäume
- Speichern vor Weltkarten-Refresh (save-then-load via pendingAfterSave)
- ModelCategory-System: Baum/Palme/Busch/Gebäude; Ordner-Kategorie im UI versteckt
- Selektion nur mit Auswahl-Werkzeug (kein versehentliches Selektieren im Platzier-Modus)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- WorldMapRenderModel: neues Record als Zwischenschicht zwischen I/O und Rendering
- WorldMapRenderer: render() akzeptiert Modell direkt; rückwärtskompatible Wrapper-Signatur für MinimapState
- WorldMapView: Layer-Auswahl als MenuButton mit Checkboxen; Vollbild-Modus (centerStack-Swap); Kamera-Indikator mit FOV-Kegel (120ms-Timer, unabhängig vom Karten-Rendering); Linksklick teleportiert JME3-Kamera unter Beibehaltung der Terrain-Höhendifferenz
- EditorApp: CameraInfoSupplier, TeleportCallback und FullscreenCallbacks verdrahtet
- MinimapState, Logback: Debug-Logging für Modell-Build und Render-Zyklen
- Map-Daten: Zwischenstand Weltdaten
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tunnel-Spalten werden im Gradientenschreiben jetzt vollständig geschützt:
Pre-Scan erkennt echte Tunnel (void ≥ Terrain-Höhe mit festem Voxel darüber)
und überspringt dann sämtliche negativen Zellen in dieser Spalte – auch
unter-terrain-liegende. Damit erzeugt MC keinen Plateau-Boden mehr am
Tunnel-Eingang.
Terrain-Übergang-Extrapolation bekommt zusätzlich einen hasVoidBelow-Check:
existieren leere Zellen unterhalb von yBot im geblurrten Chunk, wird die
Extrapolation übersprungen, statt den Hohlraum zuzuschütten.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>