Stein Tool ein wenig überarbeitet
This commit is contained in:
@@ -323,7 +323,7 @@ public class GrassState extends BaseAppState
|
||||
int vi = 0;
|
||||
for (float[] blade : blades) {
|
||||
float x = blade[0], y = blade[1], z = blade[2], h = blade[3];
|
||||
float w = Math.max(0.05f, h * ar * 0.5f);
|
||||
float w = Math.max(0.001f, h * ar * 0.5f);
|
||||
|
||||
pos.put(x-w).put(y ).put(z); uv.put(0).put(0); nrm.put(0).put(1).put(0);
|
||||
pos.put(x+w).put(y ).put(z); uv.put(1).put(0); nrm.put(0).put(1).put(0);
|
||||
|
||||
@@ -561,6 +561,18 @@ public final class MarchingCubes {
|
||||
}
|
||||
}
|
||||
|
||||
// Originale build()-Normalen der Border-Gruppen retten; build() hat sie
|
||||
// via getDensityWithNeighbors() chunk-übergreifend korrekt berechnet.
|
||||
// Nach smooth() würden sie durch rein chunk-interne Mittelung überschrieben
|
||||
// → sichtbare Kante. Deshalb nach der Normal-Neuberechnung wiederherstellen.
|
||||
FloatBuffer normFOrig = mesh.getFloatBuffer(VertexBuffer.Type.Normal);
|
||||
float[] origNorms = null;
|
||||
if (normFOrig != null) {
|
||||
origNorms = new float[vertCount * 3];
|
||||
normFOrig.rewind();
|
||||
normFOrig.get(origNorms);
|
||||
}
|
||||
|
||||
float[] ax = new float[groupCount];
|
||||
float[] ay = new float[groupCount];
|
||||
float[] az = new float[groupCount];
|
||||
@@ -623,6 +635,23 @@ public final class MarchingCubes {
|
||||
if (len>1e-6f) { gnx[g]/=len; gny[g]/=len; gnz[g]/=len; }
|
||||
else { gny[g]=1f; }
|
||||
}
|
||||
// Pinned Border-Gruppen: originale (chunk-übergreifend berechnete) Normalen zurückschreiben.
|
||||
if (origNorms != null) {
|
||||
float[] pgx = new float[groupCount], pgy = new float[groupCount], pgz = new float[groupCount];
|
||||
int[] pgc = new int[groupCount];
|
||||
for (int v = 0; v < vertCount; v++) {
|
||||
int g = vertGroup[v];
|
||||
if (pinned[g]) {
|
||||
pgx[g] += origNorms[v*3]; pgy[g] += origNorms[v*3+1]; pgz[g] += origNorms[v*3+2];
|
||||
pgc[g]++;
|
||||
}
|
||||
}
|
||||
for (int g = 0; g < groupCount; g++) {
|
||||
if (!pinned[g] || pgc[g] == 0) continue;
|
||||
float len = (float)Math.sqrt(pgx[g]*pgx[g]+pgy[g]*pgy[g]+pgz[g]*pgz[g]);
|
||||
if (len > 1e-6f) { gnx[g]=pgx[g]/len; gny[g]=pgy[g]/len; gnz[g]=pgz[g]/len; }
|
||||
}
|
||||
}
|
||||
// 3. Vertex-Normalen schreiben
|
||||
FloatBuffer normF = mesh.getFloatBuffer(VertexBuffer.Type.Normal);
|
||||
normF.rewind();
|
||||
|
||||
@@ -18,7 +18,6 @@ import de.blight.common.PlacedStoneIO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.*;
|
||||
|
||||
public class StoneWorldState extends BaseAppState {
|
||||
@@ -68,13 +67,13 @@ public class StoneWorldState extends BaseAppState {
|
||||
: 0f;
|
||||
float yCenter = worldY + s.radius() * (1f - 2f * s.sinkFraction());
|
||||
|
||||
Mesh mesh = buildStoneMesh(s.radius(), s.noiseSeed(), 2);
|
||||
Mesh mesh = buildStoneMesh(s.radius(), s.noiseSeed(), subdivForRadius(s.radius()), data.roughness());
|
||||
Geometry geo = new Geometry("stone", mesh);
|
||||
Material mat = (s.textureSlot() >= 0 && s.textureSlot() < slotMat.length && slotMat[s.textureSlot()] != null)
|
||||
? slotMat[s.textureSlot()] : defMat;
|
||||
geo.setMaterial(mat);
|
||||
geo.setLocalTranslation(s.x(), yCenter, s.z());
|
||||
geo.rotate(0f, s.rotY() * FastMath.DEG_TO_RAD, 0f);
|
||||
geo.setLocalRotation(new com.jme3.math.Quaternion().fromAngles(s.rotX(), s.rotY() * FastMath.DEG_TO_RAD, s.rotZ()));
|
||||
geo.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
|
||||
|
||||
if (bulletAppState != null) {
|
||||
@@ -173,7 +172,7 @@ public class StoneWorldState extends BaseAppState {
|
||||
return new float[]{v[0]/len, v[1]/len, v[2]/len};
|
||||
}
|
||||
|
||||
private static Mesh buildStoneMesh(float radius, int noiseSeed, int subdivisions) {
|
||||
private static Mesh buildStoneMesh(float radius, int noiseSeed, int subdivisions, float roughness) {
|
||||
List<float[]> verts = new ArrayList<>(Arrays.asList(ICO_V));
|
||||
List<int[]> faces = new ArrayList<>(Arrays.asList(ICO_F));
|
||||
|
||||
@@ -194,53 +193,111 @@ public class StoneWorldState extends BaseAppState {
|
||||
verts = nv; faces = nf;
|
||||
}
|
||||
|
||||
Random rng = new Random(noiseSeed);
|
||||
float ox = rng.nextFloat() * 50f, oy = rng.nextFloat() * 50f, oz = rng.nextFloat() * 50f;
|
||||
float freq = 2.5f + (Math.abs(noiseSeed) % 3);
|
||||
float amp = 0.22f;
|
||||
// Variantenreiche Verformung: individuelle Achsenskalierung + 3 Oktaven
|
||||
Random rng = new Random(noiseSeed);
|
||||
float ox = rng.nextFloat() * 50f;
|
||||
float oy = rng.nextFloat() * 50f;
|
||||
float oz = rng.nextFloat() * 50f;
|
||||
float flattenY = 0.50f + rng.nextFloat() * 0.45f;
|
||||
float scaleX = 0.80f + rng.nextFloat() * 0.70f;
|
||||
float scaleZ = 0.80f + rng.nextFloat() * 0.70f;
|
||||
float freq = 1.5f + (Math.abs(noiseSeed) % 4);
|
||||
float amp = roughness * 0.60f;
|
||||
|
||||
int nv2 = verts.size();
|
||||
float[] pos = new float[nv2 * 3];
|
||||
float[] nor = new float[nv2 * 3];
|
||||
float[] uv = new float[nv2 * 2];
|
||||
|
||||
for (int i = 0; i < nv2; i++) {
|
||||
float[] unit = verts.get(i);
|
||||
float n = smoothNoise3D(unit[0] * freq + ox, unit[1] * freq + oy, unit[2] * freq + oz);
|
||||
float r = radius * (1f + n * amp);
|
||||
pos[i*3] = unit[0] * r;
|
||||
pos[i*3+1] = unit[1] * r;
|
||||
pos[i*3+2] = unit[2] * r;
|
||||
uv[i*2] = (float)(Math.atan2(unit[2], unit[0]) / (2 * Math.PI) + 0.5);
|
||||
uv[i*2+1] = (float)(Math.asin(Math.max(-1, Math.min(1, unit[1]))) / Math.PI + 0.5);
|
||||
int numVerts = verts.size();
|
||||
float[] def = new float[numVerts * 3];
|
||||
for (int i = 0; i < numVerts; i++) {
|
||||
float[] u = verts.get(i);
|
||||
float n1 = smoothNoise3D(u[0]*freq+ox, u[1]*freq+oy, u[2]*freq+oz);
|
||||
float n2 = smoothNoise3D(u[0]*freq*2.5f+ox+11f, u[1]*freq*2.5f+oy+11f, u[2]*freq*2.5f+oz+11f);
|
||||
float n3 = smoothNoise3D(u[0]*freq*5.5f+ox+23f, u[1]*freq*5.5f+oy+23f, u[2]*freq*5.5f+oz+23f);
|
||||
float r = radius * (1f + (n1 * 0.55f + n2 * 0.30f + n3 * 0.15f) * amp);
|
||||
def[i*3] = u[0] * r * scaleX;
|
||||
def[i*3+1] = u[1] * r * flattenY;
|
||||
def[i*3+2] = u[2] * r * scaleZ;
|
||||
}
|
||||
|
||||
for (int[] f : faces) {
|
||||
int ai = f[0]*3, bi = f[1]*3, ci = f[2]*3;
|
||||
float ax = pos[ai], ay = pos[ai+1], az = pos[ai+2];
|
||||
float bx = pos[bi], by = pos[bi+1], bz = pos[bi+2];
|
||||
float cx = pos[ci], cy = pos[ci+1], cz = pos[ci+2];
|
||||
float nx = (by-ay)*(cz-az) - (bz-az)*(cy-ay);
|
||||
float ny = (bz-az)*(cx-ax) - (bx-ax)*(cz-az);
|
||||
float nz = (bx-ax)*(cy-ay) - (by-ay)*(cx-ax);
|
||||
for (int vi : f) { nor[vi*3] += nx; nor[vi*3+1] += ny; nor[vi*3+2] += nz; }
|
||||
}
|
||||
for (int i = 0; i < nv2; i++) {
|
||||
float len = (float) Math.sqrt(nor[i*3]*nor[i*3] + nor[i*3+1]*nor[i*3+1] + nor[i*3+2]*nor[i*3+2]);
|
||||
if (len > 0) { nor[i*3] /= len; nor[i*3+1] /= len; nor[i*3+2] /= len; }
|
||||
// Taubin-Glättung (lambda-mu, kein Mesh-Schrumpfen)
|
||||
{
|
||||
List<List<Integer>> adj = new ArrayList<>(numVerts);
|
||||
for (int i = 0; i < numVerts; i++) adj.add(new ArrayList<>(6));
|
||||
for (int[] f : faces) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int a = f[i], b = f[(i + 1) % 3];
|
||||
List<Integer> la = adj.get(a), lb = adj.get(b);
|
||||
if (!la.contains(b)) la.add(b);
|
||||
if (!lb.contains(a)) lb.add(a);
|
||||
}
|
||||
}
|
||||
float lambda = 0.5f, mu = -0.53f;
|
||||
for (int iter = 0; iter < 6; iter++) {
|
||||
float step = (iter % 2 == 0) ? lambda : mu;
|
||||
float[] next = new float[numVerts * 3];
|
||||
for (int i = 0; i < numVerts; i++) {
|
||||
List<Integer> nb = adj.get(i);
|
||||
float sx = 0, sy = 0, sz = 0;
|
||||
for (int n : nb) { sx += def[n*3]; sy += def[n*3+1]; sz += def[n*3+2]; }
|
||||
float inv = nb.isEmpty() ? 1f : 1f / nb.size();
|
||||
next[i*3] = def[i*3] + step * (sx*inv - def[i*3]);
|
||||
next[i*3+1] = def[i*3+1] + step * (sy*inv - def[i*3+1]);
|
||||
next[i*3+2] = def[i*3+2] + step * (sz*inv - def[i*3+2]);
|
||||
}
|
||||
def = next;
|
||||
}
|
||||
}
|
||||
|
||||
FloatBuffer pb = BufferUtils.createFloatBuffer(pos);
|
||||
FloatBuffer nb = BufferUtils.createFloatBuffer(nor);
|
||||
FloatBuffer ub = BufferUtils.createFloatBuffer(uv);
|
||||
java.nio.IntBuffer ib = BufferUtils.createIntBuffer(faces.size() * 3);
|
||||
for (int[] f : faces) ib.put(f[0]).put(f[1]).put(f[2]);
|
||||
// Flat-Shading: jedes Dreieck bekommt eigene Vertices mit Flächennormale + Box-Map UV
|
||||
int fc = faces.size();
|
||||
float[] pos = new float[fc * 9];
|
||||
float[] nor = new float[fc * 9];
|
||||
float[] uv = new float[fc * 6];
|
||||
java.nio.IntBuffer ib = BufferUtils.createIntBuffer(fc * 3);
|
||||
for (int i = 0; i < fc * 3; i++) ib.put(i);
|
||||
ib.flip();
|
||||
|
||||
float uvScale = 0.5f / radius;
|
||||
for (int fi = 0; fi < fc; fi++) {
|
||||
int[] f = faces.get(fi);
|
||||
float ax = def[f[0]*3], ay = def[f[0]*3+1], az = def[f[0]*3+2];
|
||||
float bx = def[f[1]*3], by = def[f[1]*3+1], bz = def[f[1]*3+2];
|
||||
float cx = def[f[2]*3], cy = def[f[2]*3+1], cz = def[f[2]*3+2];
|
||||
float ex = bx-ax, ey = by-ay, ez = bz-az;
|
||||
float gx = cx-ax, gy = cy-ay, gz = cz-az;
|
||||
float nx = ey*gz-ez*gy, ny = ez*gx-ex*gz, nz = ex*gy-ey*gx;
|
||||
float nlen = (float) Math.sqrt(nx*nx+ny*ny+nz*nz);
|
||||
if (nlen > 0) { nx/=nlen; ny/=nlen; nz/=nlen; }
|
||||
|
||||
float anx = Math.abs(nx), any = Math.abs(ny), anz = Math.abs(nz);
|
||||
float uA, vA, uB, vB, uC, vC;
|
||||
if (anx >= any && anx >= anz) {
|
||||
uA=az*uvScale+0.5f; vA=ay*uvScale+0.5f;
|
||||
uB=bz*uvScale+0.5f; vB=by*uvScale+0.5f;
|
||||
uC=cz*uvScale+0.5f; vC=cy*uvScale+0.5f;
|
||||
} else if (any >= anx && any >= anz) {
|
||||
uA=ax*uvScale+0.5f; vA=az*uvScale+0.5f;
|
||||
uB=bx*uvScale+0.5f; vB=bz*uvScale+0.5f;
|
||||
uC=cx*uvScale+0.5f; vC=cz*uvScale+0.5f;
|
||||
} else {
|
||||
uA=ax*uvScale+0.5f; vA=ay*uvScale+0.5f;
|
||||
uB=bx*uvScale+0.5f; vB=by*uvScale+0.5f;
|
||||
uC=cx*uvScale+0.5f; vC=cy*uvScale+0.5f;
|
||||
}
|
||||
int p3 = fi*9, u2 = fi*6;
|
||||
pos[p3]=ax; pos[p3+1]=ay; pos[p3+2]=az;
|
||||
pos[p3+3]=bx; pos[p3+4]=by; pos[p3+5]=bz;
|
||||
pos[p3+6]=cx; pos[p3+7]=cy; pos[p3+8]=cz;
|
||||
nor[p3]=nx; nor[p3+1]=ny; nor[p3+2]=nz;
|
||||
nor[p3+3]=nx; nor[p3+4]=ny; nor[p3+5]=nz;
|
||||
nor[p3+6]=nx; nor[p3+7]=ny; nor[p3+8]=nz;
|
||||
uv[u2]=uA; uv[u2+1]=vA;
|
||||
uv[u2+2]=uB; uv[u2+3]=vB;
|
||||
uv[u2+4]=uC; uv[u2+5]=vC;
|
||||
}
|
||||
|
||||
Mesh mesh = new Mesh();
|
||||
mesh.setBuffer(Type.Position, 3, pb);
|
||||
mesh.setBuffer(Type.Normal, 3, nb);
|
||||
mesh.setBuffer(Type.TexCoord, 2, ub);
|
||||
mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(pos));
|
||||
mesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(nor));
|
||||
mesh.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(uv));
|
||||
mesh.setBuffer(Type.Index, 3, ib);
|
||||
mesh.updateBound();
|
||||
return mesh;
|
||||
@@ -275,4 +332,9 @@ public class StoneWorldState extends BaseAppState {
|
||||
}
|
||||
|
||||
private static float lerp(float a, float b, float t) { return a + (b-a)*t; }
|
||||
|
||||
private static int subdivForRadius(float radius) {
|
||||
double logR = Math.log(Math.max(0.01, radius) / 0.1) / Math.log(2.0);
|
||||
return Math.max(1, Math.min(5, (int) Math.round(logR) + 2));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user