Weiter am Voxel kram gebastelt
This commit is contained in:
@@ -58,8 +58,11 @@ void main() {
|
||||
vec2 uvY = vWorldPos.xz / m_TexScale;
|
||||
vec2 uvZ = vWorldPos.xy / m_TexScale;
|
||||
|
||||
// Flach ab ~11° Gefälle (20% grade, normal.y≈0.98); alles andere Fels.
|
||||
float flatBlend = smoothstep(0.94, 0.99, vNormal.y);
|
||||
// Flach bis ~22,5° Gefälle (deckt sich mit der Bake-Schwelle in VoxelEditorState,
|
||||
// die Rampen bis 22,5° glättet – vorher lag diese Grenze bei ~15° und sorgte dafür,
|
||||
// dass geometrisch glatt gebackene, aber 15-22,5° steile Rampen trotzdem komplett
|
||||
// mit der Klippen-Textur eingefärbt wurden statt mit der Flach-Textur.
|
||||
float flatBlend = smoothstep(0.90, 0.95, vNormal.y);
|
||||
float steepBlend = 1.0 - flatBlend;
|
||||
|
||||
// Flat: reines XZ-UV wie das Terrain (uvY = worldPos.xz / texScale), kein Triplanar.
|
||||
|
||||
@@ -10,8 +10,8 @@ import java.util.List;
|
||||
*/
|
||||
public class DayTime {
|
||||
|
||||
/** Standard-Tagesdauer in Echtzeit-Sekunden (15 Minuten). */
|
||||
public static final float DEFAULT_DAY_DURATION = 900f;
|
||||
/** Standard-Tagesdauer in Echtzeit-Sekunden (96 Minuten). */
|
||||
public static final float DEFAULT_DAY_DURATION = 5760f;
|
||||
|
||||
private float timeOfDay;
|
||||
private float timeScale;
|
||||
@@ -25,7 +25,7 @@ public class DayTime {
|
||||
/** @param dayDuration Echtzeit-Sekunden für einen kompletten Spieltag */
|
||||
public DayTime(float dayDuration) {
|
||||
this.timeScale = 1f / dayDuration;
|
||||
this.timeOfDay = 0.25f; // Start: Sonnenaufgang
|
||||
this.timeOfDay = 0.5f; // Start: Mittag (bessere Sicht zum Testen als Sonnenaufgang)
|
||||
}
|
||||
|
||||
public void update(float tpf) {
|
||||
|
||||
@@ -782,8 +782,6 @@ public class SharedInput {
|
||||
public volatile int bakeTotal = 0;
|
||||
/** JME → JFX: Status-Meldung nach Abschluss des Backens oder Löschens. */
|
||||
public volatile String bakeStatusMsg = null;
|
||||
/** JME → JFX: Aktuell abgeschlossene Blur-Iteration (0-7). */
|
||||
public volatile int blurIterDone = 0;
|
||||
|
||||
/**
|
||||
* Per BFS selektierte gebackene Chunks (chunkKey-kodiert).
|
||||
|
||||
@@ -547,6 +547,11 @@ public class TerrainEditorState extends BaseAppState {
|
||||
thirdSplatTex.setWrap(Texture.WrapMode.EdgeClamp);
|
||||
thirdSplatTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
|
||||
thirdSplatTex.setMagFilter(Texture.MagFilter.Bilinear);
|
||||
|
||||
// loadedMapData wird ab hier nicht mehr gebraucht (letzter Verbraucher war
|
||||
// dieser Merge). Freigeben, weil terrainHeight allein ~1 GB (16385² floats)
|
||||
// belegt und sonst die ganze Session über unnötig im Heap hängt.
|
||||
loadedMapData = null;
|
||||
}
|
||||
|
||||
// Standard-Texturpfade (Fallback wenn kein benutzerdefinierter Pfad gesetzt)
|
||||
|
||||
@@ -1558,47 +1558,10 @@ public class VoxelEditorState extends BaseAppState {
|
||||
cBuf[c.idx(bx, VoxelChunk.CELLS, bz)] = tBuf[c.idx(bx, 0, bz)];
|
||||
}
|
||||
|
||||
input.blurIterDone = 0;
|
||||
// Bilateraler Filter: Nachbarn mit großem Dichtesprung (Klippen, Felskanten)
|
||||
// bekommen fast Null-Gewicht → scharfe Übergänge bleiben erhalten.
|
||||
// Flache Bereiche (geringe Differenz) werden voll geglättet wie bisher.
|
||||
// sigma_r = 30 → diff=20: w≈0.80 (flach, volles Blur);
|
||||
// diff=80: w≈0.04 (Klippe, kaum Blur).
|
||||
final float BILATERAL_INV_2S2 = 1f / (2f * 30f * 30f);
|
||||
for (int iter = 0; iter < 7; iter++) {
|
||||
Map<Long, float[]> nextBufs = new HashMap<>();
|
||||
for (VoxelChunk c : nonEmpty) {
|
||||
long k = chunkKey(c.cx, c.cy, c.cz);
|
||||
float[] cur = curBufs.get(k);
|
||||
float[] next = new float[blurN * blurN * blurN];
|
||||
for (int by = 0; by < blurN; by++) {
|
||||
for (int bz = 0; bz < blurN; bz++) {
|
||||
for (int bx = 0; bx < blurN; bx++) {
|
||||
float center = cur[c.idx(bx, by, bz)];
|
||||
float wSum = 0f, vSum = 0f;
|
||||
for (int dy = -1; dy <= 1; dy++)
|
||||
for (int dz = -1; dz <= 1; dz++)
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
int sx = bx+dx, sy = by+dy, sz = bz+dz;
|
||||
float nb;
|
||||
if (sx >= 0 && sx < blurN && sy >= 0 && sy < blurN && sz >= 0 && sz < blurN)
|
||||
nb = cur[c.idx(sx, sy, sz)];
|
||||
else
|
||||
nb = getBlurBuf(curBufs, allOriginal, c, sx, sy, sz);
|
||||
float diff = nb - center;
|
||||
float w = (float) Math.exp(-diff * diff * BILATERAL_INV_2S2);
|
||||
vSum += w * nb;
|
||||
wSum += w;
|
||||
}
|
||||
next[c.idx(bx, by, bz)] = vSum / wSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
nextBufs.put(k, next);
|
||||
}
|
||||
curBufs = nextBufs;
|
||||
input.blurIterDone = iter + 1;
|
||||
}
|
||||
// KEIN Blur mehr vor der Rampen-Berechnung (entfernt 2026-08-06): war ein Rest
|
||||
// der alten Pipeline, den ich beim "fang bei 0 an"-Rewrite versehentlich stehen
|
||||
// gelassen hatte. hfMap wird jetzt direkt aus curBufs (= unveränderte Rohdichte,
|
||||
// nur Rand-synchronisiert) gebaut, nicht aus geblurrten Daten.
|
||||
|
||||
// ── Neigungsadaptive Oberflächenverarbeitung (Regressions-Ansatz) ────────
|
||||
//
|
||||
@@ -1620,12 +1583,14 @@ public class VoxelEditorState extends BaseAppState {
|
||||
// (verhindert, dass lokale Extrema durch weit entfernte Werte abgezogen
|
||||
// werden; Regressions-Abweichung an Stufenfronten liegt bei ≤ 0,46).
|
||||
{
|
||||
final float TAN_22_5 = 0.414f; // tan(22,5°)
|
||||
final float TAN_45 = 1.000f; // tan(45°)
|
||||
final float MIN_SLOPE = 0.05f; // < ~3° → komplett flach, nicht anfassen
|
||||
java.util.Random rng = new java.util.Random(42);
|
||||
final int C = VoxelChunk.CELLS;
|
||||
final int W = 5; // Regressions-Halbfenster (±5 Spalten)
|
||||
// RADIUS in Voxel-Zellen. 1 Zelle = 1 Weltmeter (siehe VoxelChunk.toWorldX:
|
||||
// Koeffizient 1, kein Skalierungsfaktor). 10 m gewählt, weil ein kleinerer
|
||||
// Deckel bei konstanter Steigung (langer, flacher Hang) wieder künstliche
|
||||
// Stufen einführen würde, sobald die Suche vor dem echten Kontur-Nachbarn
|
||||
// abbricht (vom Nutzer bestätigt: "nicht so niedrig, dass wir bei einer
|
||||
// konstanten Steigung wieder Stufen reinbekommen").
|
||||
final int RADIUS = 10;
|
||||
|
||||
// ── Schritt 1: Höhenfeld ──────────────────────────────────────────
|
||||
// Key-Schema: (cx*C + bx + 30000) * 60001 + (cz*C + bz + 30000)
|
||||
@@ -1649,54 +1614,268 @@ public class VoxelEditorState extends BaseAppState {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Schritt 2: Lineare 2D-Regression je Spalte ───────────────────
|
||||
// Fit: H(dx,dz) = a + b·dx + c·dz (zentriert auf Spaltenmitte).
|
||||
// a = meanH → sub-voxel-genaue Ziel-Höhe (smoothH)
|
||||
// b = slopeX, c = slopeZ
|
||||
// totalSlope = sqrt(b²+c²) → Gesamt-Neigung des Ebenen-Fits
|
||||
// Nachbar-Chunks (nur XZ, gleiches cy) rein lesend nachladen, damit die
|
||||
// Ringsuche in Schritt 2 nicht an der Batch-Grenze abreißt (RADIUS=10 kann
|
||||
// problemlos in einen nicht mitgebackenen Nachbar-Chunk hineinreichen).
|
||||
// Diese Chunks werden NICHT gebacken/verändert, nur ihr Höhenfeld gelesen.
|
||||
for (VoxelChunk c : nonEmpty) {
|
||||
for (int dcz = -1; dcz <= 1; dcz++) {
|
||||
for (int dcx = -1; dcx <= 1; dcx++) {
|
||||
if (dcx == 0 && dcz == 0) continue;
|
||||
int ncx = c.cx + dcx, ncz = c.cz + dcz, ncy = c.cy;
|
||||
long nk = chunkKey(ncx, ncy, ncz);
|
||||
if (allOriginal.containsKey(nk)) continue; // schon im Batch enthalten
|
||||
try {
|
||||
if (!VoxelChunkIO.exists(ncx, ncy, ncz)) continue;
|
||||
VoxelChunk nc = VoxelChunkIO.load(ncx, ncy, ncz);
|
||||
for (int bz = 0; bz < C; bz++) {
|
||||
for (int bx = 0; bx < C; bx++) {
|
||||
for (int by = VoxelChunk.SIZE - 1; by >= 0; by--) {
|
||||
if (nc.getDensity(bx, by, bz) > 0) {
|
||||
int wiy = ncy * C + by;
|
||||
long hk = (long)(ncx * C + bx + 30000) * 60001L
|
||||
+ (ncz * C + bz + 30000);
|
||||
hfMap.merge(hk, wiy, Math::max);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Nachbar-Chunk ({},{},{}) konnte für Rampen-Suche nicht geladen werden", ncx, ncy, ncz, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Schritt 2: Waypoint-Distanz-Interpolation ────────────────────
|
||||
// 1:1-Umsetzung der Nutzer-Vorgabe vom 2026-08-05/06:
|
||||
// Für jede Spalte (Rohhöhe h0) isotrope Ringsuche (Chebyshev-Distanz –
|
||||
// vermeidet Zickzack-Artefakte einer Achsen-Suche) nach dem nächsten
|
||||
// Nachbarn mit Höhe > h0 (d_up, h_up = TATSÄCHLICHE gefundene Höhe,
|
||||
// kein Cap auf h0+1 – mehrstufige Sprünge werden laut Nutzer als EINE
|
||||
// durchgehende Fläche überbrückt statt Stufe für Stufe) und dem
|
||||
// nächsten Nachbarn mit Höhe < h0 (d_downRaw).
|
||||
// smoothH = (h0·d_up + h_up·d_down) / (d_up + d_down)
|
||||
// d_down = d_downRaw − 1 (Trailing-Edge-Korrektur: die eigene
|
||||
// Kante liegt einen Schritt vor der ersten tatsächlich
|
||||
// niedrigeren Zelle – siehe diag_waypoint*.jsh)
|
||||
// Kein höherer Nachbar gefunden → Plateau → smoothH = h0 (flach).
|
||||
// Kein niedrigerer Nachbar in der Voxel-Struktur gefunden → Basisterrain
|
||||
// wird als impliziter unterer Nachbar mitgesucht (grobe Version, per
|
||||
// Nutzer-Feedback erstmal ohne <1m-Schwelle – "passt so, sonst gehen
|
||||
// wir das später an"). Nur falls selbst das nicht greift (echtes
|
||||
// Innen-Plateau weit von jeder Kante) bleibt smoothH = h0.
|
||||
//
|
||||
// Warum Regression besser als Max-Slope:
|
||||
// Max-Slope wird von den UNMITTELBAREN Nachbarn dominiert (die bei
|
||||
// einer Stufe ±1 Voxel haben → slope = 1,0 > TAN_22,5°).
|
||||
// Die Regression sieht das gesamte ±5-Fenster und klassifiziert
|
||||
// die Stufe korrekt als sanfte Steigung (slope ≈ 0,14 für 3er-Stufen).
|
||||
// Validiert in jshell VOR dem Einbau (siehe scratchpad diag_waypoint2.jsh,
|
||||
// diag_waypoint2d_v2.jsh, diag_waypoint2d_v3.jsh):
|
||||
// - 1D-Vorgabe 3,2,2,2,2,1,1,1,0 → exakt erwartete Werte inkl. Basisterrain-Tail
|
||||
// - 2D-Rampen-Extrusion → identisch zum 1D-Fall
|
||||
// - Kuppel/Krümmungstest (der ursprüngliche Graben-Auslöser dieser
|
||||
// ganzen Debug-Session) → 0 spurious dips, weil die Formel auf reiner
|
||||
// Nachbar-Distanz basiert statt auf einer Fenster-Mittelung (Jensen's-
|
||||
// Inequality-Problem der alten Regressions-/Blur-Ansätze entfällt).
|
||||
// - Mehrstufen-Sprung 3,3,3,1,1,0 → 3,3,3,2,1,0 (Ebene 2 übersprungen,
|
||||
// eine durchgehende Fläche statt Stufen).
|
||||
// - Isolierter Spike auf großem Plateau → bleibt isolierte Säule
|
||||
// (kein niedrigerer Nachbar in der Umgebung → korrekt kein Ramping).
|
||||
//
|
||||
// regMap[0] = smoothH, regMap[1] = totalSlope
|
||||
Map<Long, float[]> regMap = new HashMap<>(hfMap.size());
|
||||
// NACHTRAG 2026-08-06 (echte Chunks 14/15_0_06/07): erster Bake zeigte kleine
|
||||
// aufgeschüttete Wälle an alten Voxel-Kanten. Ursache in echten (nicht rein
|
||||
// synthetischen) Daten gefunden: bei gleicher Ring-Distanz r existieren oft
|
||||
// MEHRERE Nachbarn mit unterschiedlicher Höhe (z.B. 8 im Westen, 9 im Norden,
|
||||
// beide r=1). Die ursprüngliche Version nahm den ERSTEN Treffer der dz/dx-
|
||||
// Schleife statt des nächstgelegenen tatsächlichen Stufen-Nachbarn → zufällige
|
||||
// Sprünge zu einer zu weit entfernten Höhe. Fix: pro Ring den Nachbarn mit der
|
||||
// kleinsten Höhe > h0 bzw. größten Höhe < h0 wählen. Betraf 17 von 10002 Spalten
|
||||
// in den Test-Chunks, max. Abweichung 1,0 (diag_realwall*.jsh im scratchpad).
|
||||
Map<Long, Float> smoothMap = new HashMap<>(hfMap.size());
|
||||
for (Map.Entry<Long, Integer> entry : hfMap.entrySet()) {
|
||||
long hk0 = entry.getKey();
|
||||
int h0 = entry.getValue();
|
||||
int colZ = (int)(hk0 % 60001L) - 30000;
|
||||
int colX = (int)(hk0 / 60001L) - 30000;
|
||||
|
||||
float sumH = 0f, sumDxH = 0f, sumDzH = 0f;
|
||||
float sumDx2 = 0f, sumDz2 = 0f;
|
||||
int cnt = 0;
|
||||
for (int dz = -W; dz <= W; dz++) {
|
||||
for (int dx = -W; dx <= W; dx++) {
|
||||
long hkN = (long)(colX + dx + 30000) * 60001L + (colZ + dz + 30000);
|
||||
Integer hN = hfMap.get(hkN);
|
||||
if (hN == null) continue;
|
||||
sumH += hN;
|
||||
sumDxH += (float)dx * hN;
|
||||
sumDzH += (float)dz * hN;
|
||||
sumDx2 += dx * dx;
|
||||
sumDz2 += dz * dz;
|
||||
cnt++;
|
||||
// WICHTIG: Bei gleicher Ring-Distanz r können mehrere Nachbarn mit
|
||||
// unterschiedlicher Höhe existieren (z.B. Höhe 8 im Westen UND Höhe 9
|
||||
// im Norden, beide bei r=1). Naiv den ERSTEN Treffer der Schleifen-
|
||||
// Reihenfolge zu nehmen wählt praktisch zufällig zwischen ihnen (abhängig
|
||||
// von der dz/dx-Iterationsreihenfolge) und erzeugte an echten Kanten
|
||||
// willkürliche Sprünge zum "falschen" (zu weit entfernten) Nachbarn –
|
||||
// sichtbar als kleine aufgeschüttete Wälle. Fix: pro Ring den Nachbarn
|
||||
// mit der KLEINSTEN Höhe > h0 (bzw. GRÖSSTEN Höhe < h0) wählen, also den
|
||||
// nächstgelegenen tatsächlichen Stufen-Nachbarn, nicht irgendeinen weiter
|
||||
// entfernten. Validiert gegen reale Chunk-Daten (diag_realwall*.jsh).
|
||||
Integer hUp = null; int dUp = -1;
|
||||
Integer hDown = null; int dDownRaw = -1;
|
||||
for (int r = 1; r <= RADIUS && (hUp == null || hDown == null); r++) {
|
||||
Integer bestUpThisRing = null, bestDownThisRing = null;
|
||||
for (int dz = -r; dz <= r; dz++) {
|
||||
for (int dx = -r; dx <= r; dx++) {
|
||||
if (Math.max(Math.abs(dx), Math.abs(dz)) != r) continue;
|
||||
long hkN = (long)(colX + dx + 30000) * 60001L + (colZ + dz + 30000);
|
||||
Integer hN = hfMap.get(hkN);
|
||||
if (hN == null) continue;
|
||||
if (hN > h0 && (bestUpThisRing == null || hN < bestUpThisRing)) bestUpThisRing = hN;
|
||||
if (hN < h0 && (bestDownThisRing == null || hN > bestDownThisRing)) bestDownThisRing = hN;
|
||||
}
|
||||
}
|
||||
if (hUp == null && bestUpThisRing != null) { hUp = bestUpThisRing; dUp = r; }
|
||||
if (hDown == null && bestDownThisRing != null) { hDown = bestDownThisRing; dDownRaw = r; }
|
||||
}
|
||||
|
||||
// KORREKTUR 2026-08-06 (echter Bake-Test, Screenshot mit 1m-Stufe vor langer
|
||||
// flacher Fläche): wenn kein niedrigerer Voxel-Nachbar gefunden wird, hatte
|
||||
// ich hier fälschlich "smoothH=h0" (flach, keine Rampe) verwendet. Das
|
||||
// widerspricht der Original-Vorgabe: "wenn wir keinen niedrigeren Partner
|
||||
// finden, dann haben wir die Höhe des Basisterrains erreicht und es soll die
|
||||
// OBERE GERADE WEITERGEZOGEN werden" – bereits im 1D-Fall exakt so validiert
|
||||
// (Tail-Extrapolation, siehe diag_waypoint2.jsh). Die 2D-Generalisierung hatte
|
||||
// das fälschlich auf "flach" vereinfacht, wodurch Spalten mit einem gültigen
|
||||
// hUp aber ohne erreichbaren hDown (z.B. eine 1m-Stufe vor einer sehr weiten
|
||||
// Ebene) GAR NICHT geramped wurden – sichtbar als scharfe Stufe.
|
||||
// Fix: fehlendes hDown wird als "gerade noch nicht gefunden, aber bei RADIUS
|
||||
// angenommen" behandelt statt als "keine Rampe". Das erzeugt eine sanft zum
|
||||
// Rand hin auslaufende Rampe über die vollen RADIUS Meter, statt eines
|
||||
// abrupten Sprungs zwischen "geramped" und "unverändert flach".
|
||||
float smoothH;
|
||||
if (hUp == null) {
|
||||
smoothH = h0; // Plateau: kein höherer Nachbar -> bewusst flach (Originalregel)
|
||||
} else {
|
||||
float dDown = (hDown != null) ? (dDownRaw - 1) : (RADIUS - 1);
|
||||
smoothH = (dDown <= 0f) ? h0 : (h0 * dUp + hUp * dDown) / (dUp + dDown);
|
||||
}
|
||||
smoothMap.put(hk0, smoothH);
|
||||
}
|
||||
|
||||
// ── Schritt 2b: Perimeter-Übergang zum Basisterrain ──────────────
|
||||
// Umsetzung v3 (2026-08-06) der Nutzer-Vorgabe: "berechne auch die Schnittpunkte
|
||||
// mit dem Basisterrain einmal außen herum... baue hier die Rampe weiter anstelle
|
||||
// einer Klippe", 10m Reichweite, Fallback-Szenario (nur wenn Schritt 2 die Struktur
|
||||
// nicht schon selbst bis auf Basisterrain-Niveau geführt hat).
|
||||
//
|
||||
// v1 (Höhen-Schwellwert pro Spalte) erzeugte an der Schwelle selbst neue Sprünge.
|
||||
// v2 (unabhängige Strahlen pro Rand-Spalte/Richtung) überschrieben sich gegenseitig
|
||||
// unkoordiniert an Überlappungen → Flickenteppich aus Dreiecken (Screenshot 21-56-58).
|
||||
// v3 (reine Nachbar-Mittelwert-Ausbreitung ohne Steigung) verlor den Winkel des
|
||||
// Randes komplett → 10m flaches Plateau statt Rampe (Nutzer-Feedback).
|
||||
// v4 (Höhe+Steigung Zelle-für-Zelle weiterreichen/mitteln, BFS) divergierte: die
|
||||
// Rückkopplung über viele Zellen hinweg schaukelte sich zu chaotischen, spitzen
|
||||
// Glitches auf (Screenshot 22-23-06) – klassisches Instabilitätsproblem iterativer
|
||||
// Fortpflanzung ohne Dämpfung.
|
||||
// Fix v5: KEINE Kettenfortpflanzung mehr. Jede leere Zelle im 10m-Streifen wird
|
||||
// unabhängig berechnet – exakt wie Schritt 2 selbst (dort funktioniert das robuste
|
||||
// "jede Spalte für sich per Ringsuche" bereits nachweislich gut): isotrope Ringsuche
|
||||
// zur nächsten Struktur-Zelle, deren lokale Steigung einmalig (nicht iterativ)
|
||||
// fortgesetzt, mit wachsender Distanz zunehmend zur echten Basisterrain-Höhe hin
|
||||
// eingeblendet. Keine Zelle hängt von einer anderen NEU BERECHNETEN Zelle ab →
|
||||
// keine Rückkopplung, keine Divergenz möglich. War in der Tat stabil, ABER: edgeH
|
||||
// selbst schwankte zwischen benachbarten Zielzellen stark (3,9 bis 5,33 nur 4
|
||||
// Zellen auseinander), weil jede Zelle nur die EINE zuerst gefundene Struktur-
|
||||
// Zelle nahm. Fix v5b: über ALLE Struktur-Zellen im nächstgelegenen Ring mitteln.
|
||||
// Trotzdem blieb ein welliges/oszillierendes Band über eigentlich flachen Flächen
|
||||
// sichtbar (Screenshot 22-43-57, aus Seitenansicht bestätigt: keine Höhle/Überhang,
|
||||
// echte Welligkeit) – die Steigungs-Fortsetzung (auch gedämpft, siehe rawH) war
|
||||
// schlicht zu rauschempfindlich.
|
||||
// Fix v6 (aktuell): Steigungs-Fortsetzung komplett gestrichen. Reiner Distanz-Blend
|
||||
// von der (ring-gemittelten) Kanten-Höhe zur tatsächlichen Basisterrain-Höhe. Folgt
|
||||
// nicht mehr exakt dem Rampen-Winkel, ist aber garantiert monoton/glatt.
|
||||
int[][] DIRS8 = {{1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
|
||||
|
||||
// Kandidaten sammeln: alle leeren Zellen innerhalb RADIUS einer Rand-Spalte.
|
||||
java.util.Set<Long> candidates = new java.util.HashSet<>();
|
||||
for (long hk0 : hfMap.keySet()) {
|
||||
int colZ = (int)(hk0 % 60001L) - 30000;
|
||||
int colX = (int)(hk0 / 60001L) - 30000;
|
||||
for (int dz = -RADIUS; dz <= RADIUS; dz++) {
|
||||
for (int dx = -RADIUS; dx <= RADIUS; dx++) {
|
||||
if (Math.max(Math.abs(dx), Math.abs(dz)) > RADIUS) continue;
|
||||
long hkC = (long)(colX + dx + 30000) * 60001L + (colZ + dz + 30000);
|
||||
if (!hfMap.containsKey(hkC)) candidates.add(hkC);
|
||||
}
|
||||
}
|
||||
if (cnt < 4) {
|
||||
regMap.put(hk0, new float[]{ h0, 99f });
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<Long, Float> extensionMap = new HashMap<>();
|
||||
int extCount = 0, extLogged = 0;
|
||||
for (long hkC : candidates) {
|
||||
int colZ = (int)(hkC % 60001L) - 30000;
|
||||
int colX = (int)(hkC / 60001L) - 30000;
|
||||
|
||||
// Nächsten Ring mit Struktur-Zellen isotrop suchen (gleiches Vorgehen wie
|
||||
// Schritt 2), dann ALLE Struktur-Zellen in diesem Ring mitteln – nicht nur die
|
||||
// erste gefundene. Grund (Log-Befund): benachbarte Zielzellen fanden oft
|
||||
// unterschiedliche einzelne "nächste" Zellen mit stark abweichender Höhe
|
||||
// (edgeH schwankte 3,9 bis 5,33 nur 4 Zellen auseinander) → wildes Springen.
|
||||
// Mittelung über den ganzen Ring glättet das.
|
||||
int nearestDist = -1;
|
||||
java.util.List<long[]> ringHits = new java.util.ArrayList<>(); // {colX,colZ}
|
||||
outer:
|
||||
for (int r = 1; r <= RADIUS; r++) {
|
||||
for (int dz = -r; dz <= r; dz++) {
|
||||
for (int dx = -r; dx <= r; dx++) {
|
||||
if (Math.max(Math.abs(dx), Math.abs(dz)) != r) continue;
|
||||
long hkN = (long)(colX + dx + 30000) * 60001L + (colZ + dz + 30000);
|
||||
if (hfMap.containsKey(hkN)) ringHits.add(new long[]{colX + dx, colZ + dz});
|
||||
}
|
||||
}
|
||||
if (!ringHits.isEmpty()) { nearestDist = r; break outer; }
|
||||
}
|
||||
float meanH = sumH / cnt;
|
||||
float slopeX = (sumDx2 > 0f) ? sumDxH / sumDx2 : 0f;
|
||||
float slopeZ = (sumDz2 > 0f) ? sumDzH / sumDz2 : 0f;
|
||||
float totalSlope = (float) Math.sqrt(slopeX * slopeX + slopeZ * slopeZ);
|
||||
regMap.put(hk0, new float[]{ meanH, totalSlope });
|
||||
if (nearestDist < 0) continue; // außerhalb der Reichweite, bleibt Klippe
|
||||
|
||||
float sumEdgeH = 0f; int hitCnt = 0;
|
||||
for (long[] hit : ringHits) {
|
||||
int hColX = (int) hit[0], hColZ = (int) hit[1];
|
||||
long hKey = (long)(hColX + 30000) * 60001L + (hColZ + 30000);
|
||||
Float hH = smoothMap.get(hKey);
|
||||
if (hH == null) continue;
|
||||
sumEdgeH += hH; hitCnt++;
|
||||
}
|
||||
if (hitCnt == 0) continue;
|
||||
float edgeH = sumEdgeH / hitCnt;
|
||||
|
||||
// KEINE Steigungs-Extrapolation mehr (v4/v5 hatten das versucht – eine an nur
|
||||
// wenigen Pixeln gemessene Steigung ist zu verrauscht, verstärkt sich über die
|
||||
// Distanz und erzeugte ein welliges/oszillierendes Band über eigentlich flachen
|
||||
// Flächen, siehe Screenshot 22-43-57). Reiner, robuster Distanz-Blend von der
|
||||
// (gemittelten) Kanten-Höhe zur tatsächlichen Basisterrain-Höhe: nah am Rand
|
||||
// dominiert die Kante, weiter draußen die Terrainhöhe. Folgt nicht mehr exakt
|
||||
// dem Winkel der letzten Rampe, ist aber garantiert monoton und ohne Rauschen.
|
||||
float wx = colX - 2048f, wz = colZ - 2048f;
|
||||
float th = terrainH(wx, wz);
|
||||
float finalH = edgeH;
|
||||
if (Float.isFinite(th)) {
|
||||
float t = nearestDist / (float) RADIUS; // 0 am Rand, 1 bei RADIUS
|
||||
finalH = edgeH * (1f - t) + th * t;
|
||||
}
|
||||
extensionMap.put(hkC, finalH);
|
||||
extCount++;
|
||||
if (extLogged < 20) {
|
||||
log.info("Perimeter-Rampe: colX={} colZ={} dist={} edgeH={} th={} finalH={}",
|
||||
colX, colZ, nearestDist, edgeH, th, finalH);
|
||||
extLogged++;
|
||||
}
|
||||
}
|
||||
log.info("Perimeter-Übergang: {} Erweiterungs-Spalten jenseits des Strukturrands erzeugt.", extCount);
|
||||
// Erweiterungs-Spalten in hfMap/smoothMap einspeisen, damit Schritt 3 sie mitschreibt.
|
||||
for (Map.Entry<Long, Float> e : extensionMap.entrySet()) {
|
||||
long hk = e.getKey();
|
||||
if (hfMap.containsKey(hk)) continue;
|
||||
hfMap.put(hk, Math.round(e.getValue()));
|
||||
smoothMap.put(hk, e.getValue());
|
||||
}
|
||||
|
||||
// ── Schritt 3: Dichte anpassen ────────────────────────────────────
|
||||
// ALLE Spalten aus hfMap bekommen den Dichte-Gradienten geschrieben, auch
|
||||
// Plateau-Spalten (smoothH==h0), nicht nur echte Rampen. Grund (gefunden
|
||||
// 2026-08-06 per Debug-Dump-Vergleich gegen das echte Mesh): eine unveränderte
|
||||
// Spalte (volle Dichte +127 bei h0, leer bei h0+1) hat ihre MC-Isofläche NICHT
|
||||
// exakt bei h0, sondern bei ≈ h0 + 127/255 ≈ h0+0.498 (lineare Nullstellen-
|
||||
// Interpolation). Wurde nur bei Rampen-Spalten explizit auf die exakte smoothH
|
||||
// geschrieben, blieben Plateau-Spalten bei ihrer natürlichen +0,5-Position →
|
||||
// an JEDER Grenze zwischen Plateau und Rampe ein ~0,5 Einheiten hoher Sprung.
|
||||
// Das war die eigentliche Ursache der "aufgeschütteten Wälle" (betraf 60% der
|
||||
// Spalten in den Test-Chunks). Fix: einheitliche Konvention für alle Spalten.
|
||||
{
|
||||
Map<Long, float[]> nextBufs = new HashMap<>();
|
||||
for (VoxelChunk c : nonEmpty) {
|
||||
@@ -1710,32 +1889,15 @@ public class VoxelEditorState extends BaseAppState {
|
||||
for (int bx = 0; bx < C; bx++) {
|
||||
int colX = c.cx * C + bx;
|
||||
long hk0 = (long)(colX + 30000) * 60001L + (colZ + 30000);
|
||||
Integer h0 = hfMap.get(hk0);
|
||||
float[] reg = regMap.get(hk0);
|
||||
if (h0 == null || reg == null) continue;
|
||||
Integer h0 = hfMap.get(hk0);
|
||||
Float smoothH = smoothMap.get(hk0);
|
||||
if (h0 == null || smoothH == null) continue;
|
||||
|
||||
float smoothH = reg[0];
|
||||
float totalSlope = reg[1];
|
||||
float diff = smoothH - h0;
|
||||
|
||||
if (totalSlope >= MIN_SLOPE && totalSlope < TAN_22_5
|
||||
&& Math.abs(diff) <= 0.6f) {
|
||||
// Sanfte Steigung: sub-voxel-genauen Dichte-Gradienten schreiben.
|
||||
// MC interpoliert die Isofläche dann auf die genaue smoothH-Höhe.
|
||||
for (int by = 0; by < blurN; by++) {
|
||||
float d = (smoothH - (wiyBase + by)) * 127f;
|
||||
next[c.idx(bx, by, bz)] = Math.max(-128f, Math.min(127f, d));
|
||||
}
|
||||
} else if (totalSlope > TAN_45) {
|
||||
// Steile Klippe: Noise für organische Optik
|
||||
int surfBy = h0 - wiyBase;
|
||||
if (surfBy < 0 || surfBy >= blurN) continue;
|
||||
for (int by = Math.max(0, surfBy - 2);
|
||||
by <= Math.min(blurN - 1, surfBy + 2); by++) {
|
||||
float noise = (float)(rng.nextGaussian() * 20.0);
|
||||
float val = cur[c.idx(bx, by, bz)] + noise;
|
||||
next[c.idx(bx, by, bz)] = Math.max(-128f, Math.min(127f, val));
|
||||
}
|
||||
// Sub-voxel-genauen Dichte-Gradienten schreiben, damit MC die
|
||||
// Isofläche auf die exakte smoothH-Höhe interpoliert.
|
||||
for (int by = 0; by < blurN; by++) {
|
||||
float d = (smoothH - (wiyBase + by)) * 127f;
|
||||
next[c.idx(bx, by, bz)] = Math.max(-128f, Math.min(127f, d));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2095,32 +2257,7 @@ public class VoxelEditorState extends BaseAppState {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cross-boundary Lookup für den kooperativen Blur:
|
||||
* Liest aus dem aktuellen Blur-Buffer des Nachbar-Chunks, oder – falls der
|
||||
* Nachbar nicht in bufs ist – direkt aus dessen Original-Daten.
|
||||
* Verwendet {@code src.idx()} da SIZE für alle Chunks identisch ist.
|
||||
*/
|
||||
private float getBlurBuf(Map<Long, float[]> bufs, Map<Long, VoxelChunk> originals,
|
||||
VoxelChunk src, int x, int y, int z) {
|
||||
int n = VoxelChunk.SIZE;
|
||||
int cells = VoxelChunk.CELLS;
|
||||
int ncx = src.cx, ncy = src.cy, ncz = src.cz;
|
||||
int lx = x, ly = y, lz = z;
|
||||
if (x < 0) { ncx--; lx = cells + x; }
|
||||
else if (x >= n) { ncx++; lx = x - cells; }
|
||||
if (y < 0) { ncy--; ly = cells + y; }
|
||||
else if (y >= n) { ncy++; ly = y - cells; }
|
||||
if (z < 0) { ncz--; lz = cells + z; }
|
||||
else if (z >= n) { ncz++; lz = z - cells; }
|
||||
long nk = chunkKey(ncx, ncy, ncz);
|
||||
float[] buf = bufs.get(nk);
|
||||
if (buf != null) return buf[src.idx(lx, ly, lz)];
|
||||
VoxelChunk nb = originals.get(nk);
|
||||
return nb != null ? nb.getDensity(lx, ly, lz) : Byte.MIN_VALUE;
|
||||
}
|
||||
|
||||
/** Liest Dichte aus einem geblurrten Chunk, clamped an Chunk-Grenzen. Nur für Gradienten-Berechnung. */
|
||||
/** Liest Dichte aus einem Chunk, clamped an Chunk-Grenzen. Nur für Gradienten-Berechnung. */
|
||||
private static float sampleBlurred(VoxelChunk chunk, int x, int y, int z) {
|
||||
int n = VoxelChunk.SIZE;
|
||||
return chunk.getDensity(
|
||||
|
||||
@@ -51,6 +51,9 @@ tasks.register('run', JavaExec) {
|
||||
description = 'Startet das Spiel'
|
||||
mainClass = mainClassName
|
||||
classpath = sourceSets.main.runtimeClasspath
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(26)
|
||||
}
|
||||
jvmArgs = [
|
||||
'--add-opens', 'java.base/java.lang=ALL-UNNAMED',
|
||||
'--add-opens', 'java.desktop/sun.awt=ALL-UNNAMED',
|
||||
|
||||
@@ -62,6 +62,15 @@ public class BlightGame extends SimpleApplication {
|
||||
private long startTimeMs = 0;
|
||||
private long lastFullscreenFix = 0;
|
||||
private long pendingFullscreenFixAt = Long.MAX_VALUE;
|
||||
// Cinnamons Display-Daemon interpretiert den glfwSetWindowMonitor()-Toggle in
|
||||
// fixFullscreenMonitor() offenbar als Monitor-Neukonfiguration und setzt dabei
|
||||
// "scaling-factor" (org.cinnamon.desktop.interface) auf 0 (=Auto) zurück, auch wenn
|
||||
// der Nutzer explizit 100% (Wert 1) eingestellt hatte – das bleibt dann auch nach dem
|
||||
// Spielende so stehen. Wir sichern den Wert vor dem Fix und schreiben ihn zeitversetzt
|
||||
// zurück (Cinnamon braucht etwas, bis es reagiert – sofortiges Zurückschreiben würde
|
||||
// von Cinnamons eigenem Reset kurz danach wieder überschrieben).
|
||||
private String savedScalingFactor = null;
|
||||
private long pendingScalingRestoreAt = Long.MAX_VALUE;
|
||||
private boolean firstLaunch = true; // false nach dem ersten simpleInitApp()
|
||||
private boolean initialFixDone = false; // Initial-Fix bei 0.5s bereits gelaufen
|
||||
private boolean pendingMenuRebuild = false; // nur true nach explizitem app.restart()
|
||||
@@ -202,6 +211,7 @@ public class BlightGame extends SimpleApplication {
|
||||
audioSettings = AudioSettingsStore.load();
|
||||
de.blight.game.state.ModelLodControl.debugLod2 = graphicsSettings.debugLod2;
|
||||
|
||||
captureScalingFactor();
|
||||
startTimeMs = System.currentTimeMillis();
|
||||
initialFixDone = false;
|
||||
|
||||
@@ -226,6 +236,16 @@ public class BlightGame extends SimpleApplication {
|
||||
// Fullscreen-Zustand war (wie beim 5s-Watchdog). Aus dem Hidden-Zustand behandelt
|
||||
// Cinnamon den nächsten Fullscreen-Übergang als Neuerstplatzierung und ignoriert
|
||||
// den Ziel-Monitor zugunsten des aktiven Workspaces (Editor-Monitor).
|
||||
//
|
||||
// mainMenuState wird weiter unten in dieser Methode gebaut – mit der JETZT
|
||||
// aktuellen (noch nicht fullscreen-korrigierten) Kamera-Größe. Der Vollbild-Fix
|
||||
// läuft aber erst ~200ms später in simpleUpdate() und löst dann reshape() aus.
|
||||
// Ohne pendingMenuRebuild würde rebuildScreenDependentStates() dabei NUR HUD/
|
||||
// Hotbar/LoadingScreen neu aufbauen (siehe dortige Kommentare: das ist normalerweise
|
||||
// extra vor app.restart() gesetzt) – das Menü bliebe dauerhaft auf der falschen,
|
||||
// zu kleinen Ausgangsgröße stehen und würde nur einen Bildschirm-Ausschnitt
|
||||
// (unten links) füllen, obwohl das Fenster längst auf volle Auflösung steht.
|
||||
pendingMenuRebuild = true;
|
||||
}
|
||||
// Restart nach Einstellungsänderung: pendingFullscreenFixAt (scheduleFullscreenFix)
|
||||
// bleibt gesetzt und übernimmt den Fix nach 1 s.
|
||||
@@ -493,6 +513,11 @@ public class BlightGame extends SimpleApplication {
|
||||
fixFullscreenMonitor();
|
||||
}
|
||||
|
||||
if (now >= pendingScalingRestoreAt) {
|
||||
pendingScalingRestoreAt = Long.MAX_VALUE;
|
||||
restoreScalingFactor();
|
||||
}
|
||||
|
||||
// Watchdog: Vollbild-Verlust durch GNOME/WM (erkennbar an Auflösungs-Mismatch).
|
||||
if (graphicsSettings != null && graphicsSettings.fullscreen) {
|
||||
if (now - startTimeMs > 5_000
|
||||
@@ -798,8 +823,56 @@ public class BlightGame extends SimpleApplication {
|
||||
glfwSleep(200);
|
||||
GLFW.glfwFocusWindow(win);
|
||||
|
||||
// scaling-factor zeitversetzt zurückschreiben, siehe Feld-Kommentar oben bei
|
||||
// savedScalingFactor – Cinnamon reagiert erst mit etwas Verzögerung auf den
|
||||
// Monitor-Toggle, ein sofortiger Restore würde von Cinnamons eigenem Reset
|
||||
// wieder überschrieben.
|
||||
pendingScalingRestoreAt = System.currentTimeMillis() + 2_000;
|
||||
|
||||
} catch (Throwable e) {
|
||||
log.warn("[Grafik] Vollbild-Fix fehlgeschlagen: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Liest den aktuellen Cinnamon-Skalierungsfaktor, um ihn nach dem Vollbild-Fix
|
||||
* wiederherzustellen (siehe Feld-Kommentar bei savedScalingFactor). */
|
||||
private void captureScalingFactor() {
|
||||
try {
|
||||
Process p = new ProcessBuilder(
|
||||
"gsettings", "get", "org.cinnamon.desktop.interface", "scaling-factor")
|
||||
.redirectErrorStream(true).start();
|
||||
String out = new String(p.getInputStream().readAllBytes()).trim();
|
||||
p.waitFor();
|
||||
// Format ist z.B. "uint32 1" – letztes Token ist der Wert. Nicht per
|
||||
// replaceAll("[^0-9]","") Ziffern rausfiltern: "uint32" enthält selbst
|
||||
// die Ziffern 3 und 2, das hängt sie vor den echten Wert (1 -> "321",
|
||||
// 321 -> "32321", ...) und schaukelt sich exponentiell hoch.
|
||||
String[] parts = out.split("\\s+");
|
||||
String value = parts[parts.length - 1];
|
||||
savedScalingFactor = value.matches("[0-9]+") ? value : null;
|
||||
log.info("[Grafik] scaling-factor gesichert: {}", savedScalingFactor);
|
||||
} catch (Exception e) {
|
||||
log.debug("[Grafik] scaling-factor konnte nicht gelesen werden (evtl. kein Cinnamon): {}", e.getMessage());
|
||||
savedScalingFactor = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Schreibt den zuvor gesicherten Cinnamon-Skalierungsfaktor zurück. */
|
||||
private void restoreScalingFactor() {
|
||||
if (savedScalingFactor == null) return;
|
||||
try {
|
||||
new ProcessBuilder(
|
||||
"gsettings", "set", "org.cinnamon.desktop.interface", "scaling-factor", savedScalingFactor)
|
||||
.start().waitFor();
|
||||
log.info("[Grafik] scaling-factor wiederhergestellt: {}", savedScalingFactor);
|
||||
} catch (Exception e) {
|
||||
log.warn("[Grafik] scaling-factor konnte nicht wiederhergestellt werden: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
restoreScalingFactor();
|
||||
super.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +373,7 @@ public class WorldScene extends BaseAppState {
|
||||
dayNight.getDayTime().setTimeOfDay(0.5f);
|
||||
} else if (envCamFrames == 30) {
|
||||
log.info("[WorldScene] Starte LightProbe-Backen bei Mittag (Frame {})...", envCamFrames);
|
||||
final float restoreTime = 0.25f;
|
||||
final float restoreTime = 0.5f; // Spielstart-Uhrzeit, siehe DayTime-Default
|
||||
LightProbeFactory.updateProbe(probe, envCam, rootNode,
|
||||
new JobProgressAdapter<LightProbe>() {
|
||||
@Override public void done(LightProbe result) {
|
||||
|
||||
@@ -87,7 +87,7 @@ public class DayNightState extends BaseAppState implements TimeListener {
|
||||
|
||||
// ── Konstruktoren ─────────────────────────────────────────────────────────
|
||||
|
||||
/** Game-Modus: Schatten aktiv, 5-Minuten-Tag. */
|
||||
/** Game-Modus: Schatten aktiv, 96-Minuten-Tag (siehe DayTime.DEFAULT_DAY_DURATION). */
|
||||
public DayNightState() {
|
||||
this(new DayTime(), true);
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
blight-map/src/main/map/chunks/sculpt_14_0_06.blsm
Normal file
BIN
blight-map/src/main/map/chunks/sculpt_14_0_06.blsm
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/sculpt_14_0_07.blsm
Normal file
BIN
blight-map/src/main/map/chunks/sculpt_14_0_07.blsm
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/sculpt_15_0_06.blsm
Normal file
BIN
blight-map/src/main/map/chunks/sculpt_15_0_06.blsm
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/sculpt_15_0_07.blsm
Normal file
BIN
blight-map/src/main/map/chunks/sculpt_15_0_07.blsm
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_14_0_06.blvc.prebake
Normal file
BIN
blight-map/src/main/map/chunks/voxel_14_0_06.blvc.prebake
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_14_0_06_baked_lod0.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_14_0_06_baked_lod0.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_14_0_06_baked_lod1.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_14_0_06_baked_lod1.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_14_0_06_baked_lod2.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_14_0_06_baked_lod2.j3o
Normal file
Binary file not shown.
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_14_0_07_baked_lod0.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_14_0_07_baked_lod0.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_14_0_07_baked_lod1.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_14_0_07_baked_lod1.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_14_0_07_baked_lod2.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_14_0_07_baked_lod2.j3o
Normal file
Binary file not shown.
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_15_0_06.blvc.prebake
Normal file
BIN
blight-map/src/main/map/chunks/voxel_15_0_06.blvc.prebake
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_15_0_06_baked_lod0.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_15_0_06_baked_lod0.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_15_0_06_baked_lod1.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_15_0_06_baked_lod1.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_15_0_06_baked_lod2.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_15_0_06_baked_lod2.j3o
Normal file
Binary file not shown.
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_15_0_07_baked_lod0.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_15_0_07_baked_lod0.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_15_0_07_baked_lod1.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_15_0_07_baked_lod1.j3o
Normal file
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_15_0_07_baked_lod2.j3o
Normal file
BIN
blight-map/src/main/map/chunks/voxel_15_0_07_baked_lod2.j3o
Normal file
Binary file not shown.
Binary file not shown.
BIN
blight-map/src/main/map/chunks/voxel_16_0_07.blvc
Normal file
BIN
blight-map/src/main/map/chunks/voxel_16_0_07.blvc
Normal file
Binary file not shown.
Reference in New Issue
Block a user