Back Effekte hinzugefügt
This commit is contained in:
@@ -682,4 +682,172 @@ public final class MarchingCubes {
|
||||
return chunk.getDensity(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verschiebt Vertices auf steilen Flächen (normal.y < 0.707 ≈ 45°) horizontal (XZ)
|
||||
* per kohärentem Value-Noise. Macht Klippen natürlicher und felsiger.
|
||||
* Rand-Vertices (Chunk-Grenzen) bleiben fixiert, Nahtlosigkeit bleibt erhalten.
|
||||
*
|
||||
* @param strength Amplitude: 0 = kein Effekt, 1 = bis ~2 m maximale Verschiebung
|
||||
* @param worldX Weltkoordinate X des Chunk-Ursprungs (cx * CELLS)
|
||||
* @param worldZ Weltkoordinate Z des Chunk-Ursprungs (cz * CELLS)
|
||||
*/
|
||||
public static Mesh perturb(Mesh mesh, float strength, float worldX, float worldZ) {
|
||||
if (mesh == null || strength < 0.001f) return mesh;
|
||||
FloatBuffer posF = mesh.getFloatBuffer(VertexBuffer.Type.Position);
|
||||
if (posF == null) return mesh;
|
||||
int vertCount = posF.capacity() / 3;
|
||||
if (vertCount < 3) return mesh;
|
||||
int triCount = vertCount / 3;
|
||||
|
||||
float[] pos = new float[vertCount * 3];
|
||||
posF.rewind(); posF.get(pos);
|
||||
|
||||
// Vertex-Gruppen aufbauen (gleiche Position → gleiche Gruppe)
|
||||
HashMap<Long, Integer> keyToGroup = new HashMap<>(vertCount / 3 + 16);
|
||||
int[] vertGroup = new int[vertCount];
|
||||
int[] groupFirst = new int[vertCount];
|
||||
int groupCount = 0;
|
||||
for (int v = 0; v < vertCount; v++) {
|
||||
long key = posKey(pos, v);
|
||||
Integer g = keyToGroup.get(key);
|
||||
if (g == null) {
|
||||
keyToGroup.put(key, groupCount);
|
||||
groupFirst[groupCount] = v;
|
||||
vertGroup[v] = groupCount++;
|
||||
} else {
|
||||
vertGroup[v] = g;
|
||||
}
|
||||
}
|
||||
|
||||
float[] gx = new float[groupCount];
|
||||
float[] gy = new float[groupCount];
|
||||
float[] gz = new float[groupCount];
|
||||
for (int g = 0; g < groupCount; g++) {
|
||||
int v = groupFirst[g];
|
||||
gx[g] = pos[v*3]; gy[g] = pos[v*3+1]; gz[g] = pos[v*3+2];
|
||||
}
|
||||
|
||||
// Rand-Vertices einfrieren (Chunk-Nahtlosigkeit)
|
||||
float bound = VoxelChunk.CELLS;
|
||||
boolean[] pinned = new boolean[groupCount];
|
||||
for (int g = 0; g < groupCount; g++) {
|
||||
float x = gx[g], y = gy[g], z = gz[g];
|
||||
if (x < 0.01f || x > bound - 0.01f ||
|
||||
y < 0.01f || y > bound - 0.01f ||
|
||||
z < 0.01f || z > bound - 0.01f) {
|
||||
pinned[g] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Flächennormalen berechnen und pro Gruppe akkumulieren
|
||||
float[] gnx = new float[groupCount];
|
||||
float[] gny = new float[groupCount];
|
||||
float[] gnz = new float[groupCount];
|
||||
for (int t = 0; t < triCount; t++) {
|
||||
int g0=vertGroup[t*3], g1=vertGroup[t*3+1], g2=vertGroup[t*3+2];
|
||||
float p0x=pos[t*9], p0y=pos[t*9+1], p0z=pos[t*9+2];
|
||||
float p1x=pos[t*9+3], p1y=pos[t*9+4], p1z=pos[t*9+5];
|
||||
float p2x=pos[t*9+6], p2y=pos[t*9+7], p2z=pos[t*9+8];
|
||||
float ex=p1x-p0x, ey=p1y-p0y, ez=p1z-p0z;
|
||||
float fx=p2x-p0x, fy=p2y-p0y, fz=p2z-p0z;
|
||||
float nx=ey*fz-ez*fy, ny=ez*fx-ex*fz, nz=ex*fy-ey*fx;
|
||||
gnx[g0]+=nx; gny[g0]+=ny; gnz[g0]+=nz;
|
||||
gnx[g1]+=nx; gny[g1]+=ny; gnz[g1]+=nz;
|
||||
gnx[g2]+=nx; gny[g2]+=ny; gnz[g2]+=nz;
|
||||
}
|
||||
for (int g = 0; g < groupCount; g++) {
|
||||
float len=(float)Math.sqrt(gnx[g]*gnx[g]+gny[g]*gny[g]+gnz[g]*gnz[g]);
|
||||
if (len > 1e-6f) { gnx[g]/=len; gny[g]/=len; gnz[g]/=len; }
|
||||
else { gny[g] = 1f; }
|
||||
}
|
||||
|
||||
// Horizontale Verschiebung auf steile Gruppen anwenden.
|
||||
// fBm (Fractal Brownian Motion) mit 4 Oktaven: gleiche Frequenz auf allen
|
||||
// drei Achsen → jedes Höhenniveau bekommt eigene, unabhängige Verschiebung.
|
||||
// Zwei unkorrelierte fBm-Felder für X und Z (unterschiedliche Offsets).
|
||||
final float STEEP = 0.707f; // cos(45°)
|
||||
final float MAX_D = 4.0f; // m maximale Verschiebung bei strength=1, senkrechter Fläche
|
||||
|
||||
for (int g = 0; g < groupCount; g++) {
|
||||
if (pinned[g]) continue;
|
||||
float ny = gny[g];
|
||||
if (ny >= STEEP) continue;
|
||||
float steepness = 1f - ny / STEEP; // 0 bei 45°, 1 bei senkrecht
|
||||
float wx = worldX + gx[g];
|
||||
float wz = worldZ + gz[g];
|
||||
float wy = gy[g];
|
||||
float noiseX = perturbFbm(wx * 0.08f, wy * 0.08f, wz * 0.08f);
|
||||
float noiseZ = perturbFbm(wx * 0.08f + 31.7f, wy * 0.08f + 11.3f, wz * 0.08f + 67.1f);
|
||||
float disp = steepness * strength * MAX_D;
|
||||
gx[g] += noiseX * disp;
|
||||
gz[g] += noiseZ * disp;
|
||||
}
|
||||
|
||||
// Positionen in Buffer schreiben
|
||||
for (int v = 0; v < vertCount; v++) {
|
||||
int g = vertGroup[v];
|
||||
pos[v*3] = gx[g]; pos[v*3+2] = gz[g];
|
||||
}
|
||||
posF.rewind(); posF.put(pos); posF.rewind();
|
||||
|
||||
// Normalen aus geänderter Geometrie neu berechnen
|
||||
java.util.Arrays.fill(gnx, 0f); java.util.Arrays.fill(gny, 0f); java.util.Arrays.fill(gnz, 0f);
|
||||
for (int t = 0; t < triCount; t++) {
|
||||
int g0=vertGroup[t*3], g1=vertGroup[t*3+1], g2=vertGroup[t*3+2];
|
||||
float p0x=pos[t*9], p0y=pos[t*9+1], p0z=pos[t*9+2];
|
||||
float p1x=pos[t*9+3], p1y=pos[t*9+4], p1z=pos[t*9+5];
|
||||
float p2x=pos[t*9+6], p2y=pos[t*9+7], p2z=pos[t*9+8];
|
||||
float ex=p1x-p0x, ey=p1y-p0y, ez=p1z-p0z;
|
||||
float fx=p2x-p0x, fy=p2y-p0y, fz=p2z-p0z;
|
||||
float nx=ey*fz-ez*fy, ny=ez*fx-ex*fz, nz=ex*fy-ey*fx;
|
||||
gnx[g0]+=nx; gny[g0]+=ny; gnz[g0]+=nz;
|
||||
gnx[g1]+=nx; gny[g1]+=ny; gnz[g1]+=nz;
|
||||
gnx[g2]+=nx; gny[g2]+=ny; gnz[g2]+=nz;
|
||||
}
|
||||
FloatBuffer normF = mesh.getFloatBuffer(VertexBuffer.Type.Normal);
|
||||
if (normF != null) {
|
||||
normF.rewind();
|
||||
for (int v = 0; v < vertCount; v++) {
|
||||
int g = vertGroup[v];
|
||||
float len=(float)Math.sqrt(gnx[g]*gnx[g]+gny[g]*gny[g]+gnz[g]*gnz[g]);
|
||||
if (len > 1e-6f) normF.put(gnx[g]/len).put(gny[g]/len).put(gnz[g]/len);
|
||||
else normF.put(0f).put(1f).put(0f);
|
||||
}
|
||||
normF.rewind();
|
||||
}
|
||||
|
||||
mesh.updateBound();
|
||||
return mesh;
|
||||
}
|
||||
|
||||
/** fBm (Fractal Brownian Motion), 4 Oktaven, Ergebnis in [-1, 1]. */
|
||||
private static float perturbFbm(float x, float y, float z) {
|
||||
float v = 0f, amp = 1f, sumAmp = 0f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
v += perturbNoise(x, y, z) * amp;
|
||||
sumAmp += amp;
|
||||
x *= 2.1f; y *= 2.1f; z *= 2.1f;
|
||||
amp *= 0.5f;
|
||||
}
|
||||
return v / sumAmp;
|
||||
}
|
||||
|
||||
/** Trilinear interpoliertes Value-Noise in [-1, 1]. Kohärent, deterministisch. */
|
||||
private static float perturbNoise(float x, float y, float z) {
|
||||
int ix=(int)Math.floor(x), iy=(int)Math.floor(y), iz=(int)Math.floor(z);
|
||||
float fx=x-ix, fy=y-iy, fz=z-iz;
|
||||
fx=fx*fx*(3-2*fx); fy=fy*fy*(3-2*fy); fz=fz*fz*(3-2*fz);
|
||||
float v00 = perturbHash(ix,iy,iz) + fx*(perturbHash(ix+1,iy,iz) - perturbHash(ix,iy,iz));
|
||||
float v10 = perturbHash(ix,iy+1,iz) + fx*(perturbHash(ix+1,iy+1,iz) - perturbHash(ix,iy+1,iz));
|
||||
float v01 = perturbHash(ix,iy,iz+1) + fx*(perturbHash(ix+1,iy,iz+1) - perturbHash(ix,iy,iz+1));
|
||||
float v11 = perturbHash(ix,iy+1,iz+1)+fx*(perturbHash(ix+1,iy+1,iz+1)-perturbHash(ix,iy+1,iz+1));
|
||||
return (v00 + fy*(v10-v00)) + fz*((v01 + fy*(v11-v01)) - (v00 + fy*(v10-v00)));
|
||||
}
|
||||
|
||||
private static float perturbHash(int x, int y, int z) {
|
||||
int h = x * 374761393 + y * 1103515245 + z * -2012135261;
|
||||
h ^= (h >>> 15); h *= 0x9e3779b9; h ^= (h >>> 12);
|
||||
return ((h >>> 1) & 0xFFFF) * (2f / 65535f) - 1f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user