Zwischenstand gesichert bei den voxel poxoxeln

This commit is contained in:
2026-08-09 22:02:00 +02:00
parent 00a7ccbe47
commit 46be9ffe57
6 changed files with 65 additions and 57 deletions

View File

@@ -2133,6 +2133,11 @@ public class VoxelEditorState extends BaseAppState {
Map<Long, Float> terrainCache2b = new HashMap<>();
// ── Loop 1: Pass A Basisterrain (LOWER + RAISE) ───────────────────
// Muss als eigenständiger Loop VOR Pass B laufen: Pass B liest smoothMap
// der Nachbarn. Wären beide Passes im selben Loop, sähe Pass B manchmal
// noch den vergrabenen h0raw einer Nachbarzelle, die von PassA-RAISE noch
// nicht angehoben wurde (HashMap-Reihenfolge unbestimmt) → Graben.
for (Map.Entry<Long, Float> entry : hfMap.entrySet()) {
long hk0 = entry.getKey();
float h0raw = entry.getValue();
@@ -2140,16 +2145,10 @@ public class VoxelEditorState extends BaseAppState {
int colX2b = (int)(hk0 / 60001L) - 30000;
int colZ2b = (int)(hk0 % 60001L) - 30000;
float minRampH = h0sm;
// Terrain ÜBER dem Voxel: Voxel nach oben auf Terrain-Niveau heben.
// Kein +0.1f-Offset an Ring-1 → Ring-1-Oberfläche liegt exakt auf Terrain
// (nahtloser Übergang). Innere Ringe steigen per 4:1-Rampe weiter an.
float minRaiseH = Float.POSITIVE_INFINITY;
float minRampH = h0sm;
float minRaiseH = Float.POSITIVE_INFINITY;
float passA_maxThAbove = Float.NEGATIVE_INFINITY;
// ── Pass A: Basisterrain ──────────────────────────────────────────
// Pro Ring: höchstes qualifizierendes th (maxThAtR) → Abwärts-Kandidat.
// Gegenfall: Terrain ÜBER h0sm (maxThAboveAtR) → Aufwärts-Kandidat.
float passA_maxThAbove = Float.NEGATIVE_INFINITY; // Terrain ÜBER h0sm (r=1, Diagnose)
for (int r = 1; r <= MAX_RAMP_R_TERRAIN; r++) {
float maxThAtR = Float.NEGATIVE_INFINITY;
float maxThAboveAtR = Float.NEGATIVE_INFINITY;
@@ -2188,7 +2187,6 @@ public class VoxelEditorState extends BaseAppState {
minRampH = candidate;
}
}
// Aufwärts-Kandidat: gleiche Formel wie Abwärts (symmetrisch)
if (maxThAboveAtR > Float.NEGATIVE_INFINITY) {
float raiseCandidate = maxThAboveAtR + (r - 1) / RAMP_RATIO;
if (raiseCandidate < minRaiseH) {
@@ -2196,7 +2194,6 @@ public class VoxelEditorState extends BaseAppState {
}
}
}
// Diagnose
if (log.isTraceEnabled() && passA_maxThAbove > Float.NEGATIVE_INFINITY
&& minRampH >= h0sm) {
if (minRaiseH < Float.POSITIVE_INFINITY) {
@@ -2205,61 +2202,17 @@ public class VoxelEditorState extends BaseAppState {
colX2b, colZ2b, h0sm, minRaiseH, passA_maxThAbove));
} else {
log.trace(String.format(
"PassA-UNTEN (%d,%d) h0sm=%.3f h0raw=%.3f terrainR1=%.3f Terrain zu weit oben (>MAX_TERRAIN_DIFF), kein Kandidat",
"PassA-UNTEN (%d,%d) h0sm=%.3f h0raw=%.3f terrainR1=%.3f",
colX2b, colZ2b, h0sm, h0raw, passA_maxThAbove));
}
}
// ── Pass B: Voxel-zu-Voxel ────────────────────────────────────────
// Nur Terrassen-Zellen (h0raw ≤ 4.25m) Bergterrain wird nicht angefasst.
// Pass B als if-Block (kein continue): smoothMap.put() am Ende bleibt für
// Pass-A-Ergebnisse aller Zellen erreichbar.
// Cascade-Schutz: alle Vergleiche ausschließlich mit RAW-Höhen (hN, h0raw),
// nie mit smoothMap gerampte Kliffränder behalten raw ihre Originalhöhe
// und werden nicht fälschlicherweise als niedrigere Nachbarn erkannt.
if (h0raw <= 4.25f) {
boolean hadAnyLower = false;
boolean hadNonVoxelR1R3 = false;
voxelPassB:
for (int r = 1; r <= MAX_RAMP_R; r++) {
for (int dz2b = -r; dz2b <= r; dz2b++) {
for (int dx2b = -r; dx2b <= r; dx2b++) {
if (Math.max(Math.abs(dx2b), Math.abs(dz2b)) != r) continue;
long hkN = (long)(colX2b + dx2b + 30000) * 60001L
+ (colZ2b + dz2b + 30000);
Float hN = hfMap.get(hkN);
if (hN == null) {
if (r <= 3) { hadNonVoxelR1R3 = true; }
continue;
}
if (hN >= h0raw) continue;
if ((h0raw - hN) >= MAX_VOXEL_DIFF) continue;
hadAnyLower = true;
if ((h0raw - hN) > (float) r) continue;
float candidate = hN + r / RAMP_RATIO;
if (candidate < minRampH) {
minRampH = candidate;
}
}
}
// Nach Ring-3: kein gültiger niedrigerer Voxel + kein Terrain → Plateau-Innenzelle
if (r == 3 && !hadAnyLower && !hadNonVoxelR1R3) {
break voxelPassB;
}
if (hadAnyLower) {
break voxelPassB;
}
}
}
if (minRampH < h0sm) {
if (log.isTraceEnabled()) {
log.trace(String.format("Schritt2b (%d,%d) h=%.2f → %.2f",
log.trace(String.format("Schritt2b-A (%d,%d) h=%.2f → %.2f",
colX2b, colZ2b, h0sm, minRampH));
}
smoothMap.put(hk0, minRampH);
} else if (minRaiseH > h0sm && minRaiseH < Float.POSITIVE_INFINITY) {
// Voxel lag unter dem Terrain → auf Terrain-Niveau anheben
if (log.isTraceEnabled()) {
log.trace(String.format("Schritt2b-RAISE (%d,%d) h=%.2f → %.2f",
colX2b, colZ2b, h0sm, minRaiseH));
@@ -2267,6 +2220,61 @@ public class VoxelEditorState extends BaseAppState {
smoothMap.put(hk0, minRaiseH);
}
}
// ── Loop 2: Pass B Voxel-zu-Voxel ─────────────────────────────────
// Liest smoothMap aus Loop 1 → RAISE-Zellen haben ihre angehobene Höhe.
// Effektive Nachbarhöhe: max(raw, smooth).
// RAISE-Nachbar (smooth > raw): smooth gewinnt → kein Kandidat unter Terrain.
// LOWER-Nachbar (smooth < raw): raw gewinnt → Kaskaden-Schutz bleibt.
for (Map.Entry<Long, Float> entry : hfMap.entrySet()) {
long hk0 = entry.getKey();
float h0raw = entry.getValue();
if (h0raw > 4.25f) continue;
float h0sm = smoothMap.getOrDefault(hk0, h0raw);
int colX2b = (int)(hk0 / 60001L) - 30000;
int colZ2b = (int)(hk0 % 60001L) - 30000;
float minRampH = h0sm;
boolean hadAnyLower = false;
boolean hadNonVoxelR1R3 = false;
voxelPassB:
for (int r = 1; r <= MAX_RAMP_R; r++) {
for (int dz2b = -r; dz2b <= r; dz2b++) {
for (int dx2b = -r; dx2b <= r; dx2b++) {
if (Math.max(Math.abs(dx2b), Math.abs(dz2b)) != r) continue;
long hkN = (long)(colX2b + dx2b + 30000) * 60001L
+ (colZ2b + dz2b + 30000);
Float hNraw = hfMap.get(hkN);
if (hNraw == null) {
if (r <= 3) { hadNonVoxelR1R3 = true; }
continue;
}
float hN = Math.max(hNraw, smoothMap.getOrDefault(hkN, hNraw));
if (hN >= h0raw) continue;
if ((h0raw - hN) >= MAX_VOXEL_DIFF) continue;
hadAnyLower = true;
if ((h0raw - hN) > (float) r) continue;
float candidate = hN + r / RAMP_RATIO;
if (candidate < minRampH) {
minRampH = candidate;
}
}
}
if (r == 3 && !hadAnyLower && !hadNonVoxelR1R3) {
break voxelPassB;
}
if (hadAnyLower) {
break voxelPassB;
}
}
if (minRampH < h0sm) {
if (log.isTraceEnabled()) {
log.trace(String.format("Schritt2b-B (%d,%d) h=%.2f → %.2f",
colX2b, colZ2b, h0sm, minRampH));
}
smoothMap.put(hk0, minRampH);
}
}
}
// ── Schritt 3: Dichte anpassen ────────────────────────────────────