Fehler bei den Höhlen behoben

This commit is contained in:
2026-08-16 22:54:22 +02:00
parent 13a1c45ca6
commit c9ab15bda5
23 changed files with 43 additions and 4 deletions

View File

@@ -1891,12 +1891,15 @@ public class TerrainEditorState extends BaseAppState {
}
camPos.y = FastMath.clamp(camPos.y, -200f, MAX_CAM_Y);
// Kamera nicht unter das Terrain oder gebackenes Voxel-Terrain fallen lassen
// Kamera nicht unter Basis-Terrain oder gebackenes Voxel-Terrain fallen lassen.
// Raycast von Kameraposition nach unten: findet Bergoberfläche, Tunnelboden oder nichts.
float baseFloor = getTerrainHeightFast(camPos.x, camPos.z);
VoxelEditorState ves = getStateManager().getState(VoxelEditorState.class);
float floor = (ves != null)
? Math.max(baseFloor, ves.columnTopWorldY(camPos.x, camPos.z))
: baseFloor;
float floor = baseFloor;
if (ves != null) {
float vf = ves.voxelFloorBelow(camPos.x, camPos.y, camPos.z, 4f);
if (!Float.isNaN(vf)) floor = Math.max(baseFloor, vf);
}
if (camPos.y < floor + 2f) camPos.y = floor + 2f;
cam.setLocation(camPos);

View File

@@ -1381,6 +1381,30 @@ public class VoxelEditorState extends BaseAppState {
return terrainH(worldX, worldZ); // Kein Voxel → Terrain-Oberfläche als Basis
}
/** Voxel-Dichte am Weltpunkt; &gt;0 = solid, ≤0 = Luft/leer. */
public float densityAt(float worldX, float worldY, float worldZ) {
int cx = VoxelChunk.worldXToCx(worldX);
int cy = VoxelChunk.worldYToCy(worldY);
int cz = VoxelChunk.worldZToCz(worldZ);
VoxelChunk chunk = chunks.get(chunkKey(cx, cy, cz));
if (chunk == null || chunk.isEmpty()) return -1f;
int lx = Math.max(0, Math.min(VoxelChunk.SIZE - 1, Math.round(VoxelChunk.worldXToLocal(worldX, cx))));
int ly = Math.max(0, Math.min(VoxelChunk.SIZE - 1, Math.round(VoxelChunk.worldYToLocal(worldY, cy))));
int lz = Math.max(0, Math.min(VoxelChunk.SIZE - 1, Math.round(VoxelChunk.worldZToLocal(worldZ, cz))));
return chunk.getDensity(lx, ly, lz);
}
/** Erste gebackene Voxel-Fläche unterhalb (wx,wy,wz) innerhalb maxDist Metern.
* Gibt Float.NaN zurück wenn keine Fläche in Reichweite. */
public float voxelFloorBelow(float wx, float wy, float wz, float maxDist) {
if (voxelRoot == null) return Float.NaN;
CollisionResults results = new CollisionResults();
voxelRoot.collideWith(new Ray(new Vector3f(wx, wy, wz), new Vector3f(0f, -1f, 0f)), results);
if (results.size() == 0) return Float.NaN;
CollisionResult closest = results.getClosestCollision();
return (closest.getDistance() <= maxDist) ? closest.getContactPoint().y : Float.NaN;
}
/**
* Gemeinsamer Kern: iteriert über alle Spalten im Radius und bewegt sie
* schrittweise auf die per Lambda berechnete Ziel-Welt-Y zu.
@@ -2385,6 +2409,7 @@ public class VoxelEditorState extends BaseAppState {
float[] next = Arrays.copyOf(cur, cur.length);
int wiyBase = c.cy * C;
VoxelChunk origChunk = allOriginal.get(k);
for (int bz = 0; bz < C; bz++) {
int colZ = c.cz * C + bz;
for (int bx = 0; bx < C; bx++) {
@@ -2396,8 +2421,19 @@ public class VoxelEditorState extends BaseAppState {
// Sub-voxel-genauen Dichte-Gradienten schreiben, damit MC die
// Isofläche auf die exakte smoothH-Höhe interpoliert.
// Ausnahme: manuell ausgegrabene Voxel (Tunnel/Höhlen) behalten
// ihren MIN_VALUE erkennbar daran dass Gradient solid will (d>0)
// aber die Original-Dichte bereits Luft ist (MIN_VALUE).
for (int by = 0; by < blurN; by++) {
float d = (smoothH - (wiyBase + by)) * 127f;
// Tunnel/Höhlen-Voxel schützen: Gradient will Solid (d>0),
// aber der Voxel wurde ausgegraben (Dichte negativ) →
// nicht mit Solid-Gradient überschreiben.
// Gilt sowohl für Byte.MIN_VALUE (setDensity) als auch für
// partiell-reduzierte Werte (reduceDensity im Cave-Modus).
if (d > 0f && origChunk.getDensity(bx, by, bz) < 0) {
continue;
}
next[c.idx(bx, by, bz)] = Math.max(-128f, Math.min(127f, d));
}
}