Voxel Umstellung vollendet
This commit is contained in:
@@ -399,7 +399,7 @@ public class VoxelEditorState extends BaseAppState {
|
||||
// Verwaiste gebackene .j3o ohne zugehörige .blvc löschen
|
||||
for (int[] cxyz : SculptedMeshIO.findAllBakedChunks()) {
|
||||
int cx = cxyz[0], cy = cxyz[1], cz = cxyz[2];
|
||||
if (!VoxelChunkIO.exists(cx, cy, cz) && !VoxelChunkIO.preBakeExists(cx, cy, cz)) {
|
||||
if (!VoxelChunkIO.exists(cx, cy, cz)) {
|
||||
for (int lod = 0; lod <= 3; lod++) {
|
||||
try { Files.deleteIfExists(VoxelChunkIO.getBakedPath(cx, cy, cz, lod)); } catch (Exception ignored) {}
|
||||
}
|
||||
@@ -422,7 +422,9 @@ public class VoxelEditorState extends BaseAppState {
|
||||
if (input.markAllChunksDirtyRequested) {
|
||||
input.markAllChunksDirtyRequested = false;
|
||||
for (long key : chunks.keySet()) sessionDirtyChunks.add(key);
|
||||
// Chunks die noch nicht geladen sind von Disk nachladen, damit der Bake sie kennt
|
||||
// Sofort aktualisieren damit der Bake-on-Exit-Guard den neuen Wert sieht
|
||||
input.sessionDirtyVoxelCount = sessionDirtyChunks.size();
|
||||
// Chunks die noch nicht geladen sind von Disk nachladen, damit der Bake-on-Exit sie kennt
|
||||
executor.submit(() -> {
|
||||
List<VoxelChunk> allDisk = VoxelChunkIO.loadAll();
|
||||
app.enqueue(() -> {
|
||||
@@ -431,7 +433,22 @@ public class VoxelEditorState extends BaseAppState {
|
||||
if (!chunks.containsKey(key)) chunks.put(key, c);
|
||||
sessionDirtyChunks.add(key);
|
||||
}
|
||||
// Leere Chunks: .blvc löschen (kein Mesh zu backen, kein Speicherbedarf)
|
||||
List<Long> emptyKeys = new ArrayList<>();
|
||||
for (long key : sessionDirtyChunks) {
|
||||
VoxelChunk c = chunks.get(key);
|
||||
if (c != null && c.isEmpty()) emptyKeys.add(key);
|
||||
}
|
||||
for (long key : emptyKeys) {
|
||||
VoxelChunk c = chunks.remove(key);
|
||||
sessionDirtyChunks.remove(key);
|
||||
VoxelChunkNode node = nodes.remove(key);
|
||||
if (node != null) node.removeFromParent();
|
||||
try { VoxelChunkIO.delete(c.cx, c.cy, c.cz); } catch (Exception ignored) {}
|
||||
}
|
||||
input.sessionDirtyVoxelCount = sessionDirtyChunks.size();
|
||||
log.info("Alle-dirty: {} Chunks geladen, {} leer gelöscht, {} dirty für Bake-on-Exit",
|
||||
allDisk.size(), emptyKeys.size(), sessionDirtyChunks.size());
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1970,20 +1987,27 @@ public class VoxelEditorState extends BaseAppState {
|
||||
}
|
||||
}
|
||||
|
||||
private void bakeAll(List<VoxelChunk> toProcess) {
|
||||
/** Gibt die Anzahl erfolgreich gebackener Chunks zurück (0 wenn nichts zu tun). */
|
||||
private int bakeAll(List<VoxelChunk> toProcess) {
|
||||
saveAll();
|
||||
log.info("bakeAll: {} Chunks übergeben", toProcess.size());
|
||||
List<VoxelChunk> nonEmpty = new java.util.ArrayList<>();
|
||||
for (VoxelChunk c : toProcess) { if (!c.isEmpty()) nonEmpty.add(c); }
|
||||
log.info("bakeAll: nach isEmpty-Filter: {} verbleiben", nonEmpty.size());
|
||||
|
||||
// Chunks ohne .blvc-Datei überspringen (noch nie gespeichert).
|
||||
nonEmpty.removeIf(c -> !VoxelChunkIO.exists(c.cx, c.cy, c.cz));
|
||||
log.info("bakeAll: nach exists-Filter: {} verbleiben", nonEmpty.size());
|
||||
// Chunks ohne nennenswerte Geometrie überspringen (nur Brush-Randberührungen,
|
||||
// solidYSpan < 2 → gleiche Schwelle wie der Game-Loader).
|
||||
nonEmpty.removeIf(c -> c.solidYSpan() < 2);
|
||||
log.info("bakeAll: nach solidYSpan-Filter: {} verbleiben (→ diese werden gebacken)", nonEmpty.size());
|
||||
|
||||
if (nonEmpty.isEmpty()) {
|
||||
input.bakeStatusMsg = "Nichts zu backen (keine nicht-leeren Chunks).";
|
||||
return;
|
||||
log.warn("bakeAll: kein Chunk überlebt alle Filter – übergebene Chunks: {}",
|
||||
toProcess.stream().map(c -> c.cx+","+c.cy+","+c.cz).toList());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// bakeTotal sofort setzen, damit die Fortschrittsanzeige schon während des Blurs läuft
|
||||
@@ -2236,8 +2260,12 @@ public class VoxelEditorState extends BaseAppState {
|
||||
// gebackene Terrain systematisch zu hoch machen.
|
||||
smoothH = h0;
|
||||
} else {
|
||||
float dDown = dDownRaw - 1;
|
||||
smoothH = (dDown <= 0f) ? h0 : (h0 * dUp + hUp * dDown) / (dUp + dDown);
|
||||
// dDown = dDownRaw (kein -1): auch unmittelbar angrenzende Stufen
|
||||
// (dDownRaw=1) bekommen eine interpolierte Höhe statt h0 zurück.
|
||||
// Die frühere "Trailing-Edge-Korrektur" (dDownRaw-1) ließ genau die
|
||||
// Kanten-Spalten unberührt, die am meisten abfallen müssten.
|
||||
float dDown = (float) dDownRaw;
|
||||
smoothH = (h0 * dUp + hUp * dDown) / (dUp + dDown);
|
||||
}
|
||||
smoothMap.put(hk0, smoothH);
|
||||
}
|
||||
@@ -2291,7 +2319,9 @@ public class VoxelEditorState extends BaseAppState {
|
||||
final float RAMP_RATIO = 4.0f;
|
||||
final int MAX_RAMP_R = 20;
|
||||
final int MAX_RAMP_R_TERRAIN = 4;
|
||||
final float MAX_TERRAIN_DIFF = 1.0f;
|
||||
// Erhöht von 1.0 auf 3.0: unterste Voxel-Schicht liegt oft 2–3 m über dem
|
||||
// Basisterrain – mit 1.0 wurde sie komplett übersprungen → senkrechte Klippe.
|
||||
final float MAX_TERRAIN_DIFF = 3.0f;
|
||||
final float MAX_VOXEL_DIFF = 1.05f;
|
||||
|
||||
Map<Long, Float> terrainCache2b = new HashMap<>();
|
||||
@@ -2308,6 +2338,12 @@ public class VoxelEditorState extends BaseAppState {
|
||||
int colX2b = (int)(hk0 / 60001L) - 30000;
|
||||
int colZ2b = (int)(hk0 % 60001L) - 30000;
|
||||
|
||||
// Schritt 2 hat diese Spalte bereits auf eine Rampe gesetzt → Pass A
|
||||
// nicht überschreiben, sonst entsteht ein Konflikt (Schritt-2-Steigung
|
||||
// vs. terrain-basierter Rampenwert). Pass A nur für Spalten die Schritt 2
|
||||
// unberührt gelassen hat (smoothH ≈ h0raw = Plateau oder unterste Schicht).
|
||||
if (Math.abs(h0sm - h0raw) > 0.1f) continue;
|
||||
|
||||
float minRampH = h0sm;
|
||||
float minRaiseH = Float.POSITIVE_INFINITY;
|
||||
float passA_maxThAbove = Float.NEGATIVE_INFINITY;
|
||||
@@ -2392,7 +2428,9 @@ public class VoxelEditorState extends BaseAppState {
|
||||
for (Map.Entry<Long, Float> entry : hfMap.entrySet()) {
|
||||
long hk0 = entry.getKey();
|
||||
float h0raw = entry.getValue();
|
||||
if (h0raw > 4.25f) continue;
|
||||
// Kein Höhen-Cutoff hier – Pass B muss für alle Voxel-Säulen laufen.
|
||||
// Der frühere "h0raw > 4.25f"-Guard hat Pass B für praktisch alle
|
||||
// Terrain-Voxel (Welt-Y >> 4) komplett deaktiviert.
|
||||
float h0sm = smoothMap.getOrDefault(hk0, h0raw);
|
||||
int colX2b = (int)(hk0 / 60001L) - 30000;
|
||||
int colZ2b = (int)(hk0 % 60001L) - 30000;
|
||||
@@ -2717,14 +2755,11 @@ public class VoxelEditorState extends BaseAppState {
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-Bake-Backup anlegen (ermöglicht Revert nach dem Bake).
|
||||
// Wichtig: zuerst .blvc auf Disk speichern, falls Auto-Save noch nicht gelaufen ist,
|
||||
// damit savePreBake() etwas zum Kopieren hat.
|
||||
// .blvc sicherstellen bevor der Bake läuft (falls Auto-Save noch nicht lief)
|
||||
for (VoxelChunk chunk : nonEmpty) {
|
||||
try {
|
||||
VoxelChunkIO.save(chunk);
|
||||
VoxelChunkIO.savePreBake(chunk.cx, chunk.cy, chunk.cz);
|
||||
} catch (Exception e) { log.warn("Pre-Bake-Backup fehlgeschlagen ({},{},{}): {}",
|
||||
} catch (Exception e) { log.warn("Speichern vor Bake fehlgeschlagen ({},{},{}): {}",
|
||||
chunk.cx, chunk.cy, chunk.cz, e.getMessage()); }
|
||||
}
|
||||
|
||||
@@ -2760,6 +2795,7 @@ public class VoxelEditorState extends BaseAppState {
|
||||
input.bakeStatusMsg = msg;
|
||||
input.sculptRescanNeeded = true;
|
||||
log.info("Voxel-Bake abgeschlossen – {}.", msg);
|
||||
return successfullyBaked.size();
|
||||
}
|
||||
|
||||
/** Bäckt einen einzelnen Chunk. Gibt true zurück wenn erfolgreich, false bei Fehler. */
|
||||
|
||||
@@ -1266,8 +1266,9 @@ public class TerrainPanelController {
|
||||
"wird alles neu gebacken – kann je nach Groesse der Karte sehr lange dauern.");
|
||||
rebakeWarning.setStyle("-fx-text-fill: #b05000; -fx-font-size: 10;");
|
||||
rebakeWarning.setWrapText(true);
|
||||
// visibleProperty binden – aber NICHT managedProperty: der Label belegt immer
|
||||
// seinen Platz, damit sich das Layout nicht verschiebt und der OK-Button sichtbar bleibt.
|
||||
rebakeWarning.visibleProperty().bind(cbRebakeAll.selectedProperty());
|
||||
rebakeWarning.managedProperty().bind(cbRebakeAll.selectedProperty());
|
||||
|
||||
javafx.scene.layout.VBox content = new javafx.scene.layout.VBox(8,
|
||||
cbUnderground, cbOrphaned,
|
||||
@@ -1298,7 +1299,7 @@ public class TerrainPanelController {
|
||||
|
||||
if (doRebakeAll) {
|
||||
input.markAllChunksDirtyRequested = true;
|
||||
statusLbl.setText("Alle Chunks als dirty markiert.");
|
||||
statusLbl.setText("Alle Chunks als dirty markiert – Bake beim Tab-Verlassen.");
|
||||
}
|
||||
if (doUnderground || doOrphaned) {
|
||||
input.cleanupUndergroundVoxels = doUnderground;
|
||||
|
||||
Reference in New Issue
Block a user