diff --git a/cubyz-common/src/io/cubyz/world/Chunk.java b/cubyz-common/src/io/cubyz/world/Chunk.java index 7d868cf7..a47c6a33 100644 --- a/cubyz-common/src/io/cubyz/world/Chunk.java +++ b/cubyz-common/src/io/cubyz/world/Chunk.java @@ -81,6 +81,8 @@ public class Chunk { * @param z */ public void addBlock(Block b, int x, int y, int z) { + if(y >= World.WORLD_HEIGHT) + return; int rx = x - (ox << 4); if (rx < 0) { // Determines if the block is part of another chunk. @@ -154,6 +156,8 @@ public class Chunk { for (int py = 0; py < 16; py++) { float value = map[px][py]; int y = (int) (value * World.WORLD_HEIGHT); + if(y == World.WORLD_HEIGHT) + y--; for (int j = y > SEA_LEVEL ? y : SEA_LEVEL; j >= 0; j--) { BlockInstance bi = null; if(j > y) { diff --git a/cubyz-common/src/io/cubyz/world/LocalWorld.java b/cubyz-common/src/io/cubyz/world/LocalWorld.java index 99bd687f..5fe5ae87 100644 --- a/cubyz-common/src/io/cubyz/world/LocalWorld.java +++ b/cubyz-common/src/io/cubyz/world/LocalWorld.java @@ -168,9 +168,9 @@ public class LocalWorld extends World { public void synchronousGenerate(Chunk ch) { int x = ch.getX() * 16; int y = ch.getZ() * 16; - float[][] heightMap = Noise.generateMapFragment(x, y, 16, 16, 300, seed); - float[][] vegetationMap = Noise.generateMapFragment(x, y, 16, 16, 300, seed + 3 * (seed + 1 & Integer.MAX_VALUE)); - float[][] oreMap = Noise.generateMapFragment(x, y, 16, 16, 300, seed - 3 * (seed - 1 & Integer.MAX_VALUE)); + float[][] heightMap = Noise.generateMapFragment(x, y, 16, 16, 256, seed); + float[][] vegetationMap = Noise.generateMapFragment(x, y, 16, 16, 128, seed + 3 * (seed + 1 & Integer.MAX_VALUE)); + float[][] oreMap = Noise.generateMapFragment(x, y, 16, 16, 128, seed - 3 * (seed - 1 & Integer.MAX_VALUE)); ch.generateFrom(heightMap, vegetationMap, oreMap); } diff --git a/cubyz-common/src/io/cubyz/world/Noise.java b/cubyz-common/src/io/cubyz/world/Noise.java index 592e0a84..2698d984 100644 --- a/cubyz-common/src/io/cubyz/world/Noise.java +++ b/cubyz-common/src/io/cubyz/world/Noise.java @@ -1,10 +1,13 @@ package io.cubyz.world; +import java.util.Random; + /** * Perlin Noise generator for Worlds * @author zenith391 */ public class Noise { + private static Random r = new Random(); static float get2DPerlinNoiseValue(float x, float y, float res, int seed) { @@ -75,6 +78,83 @@ public class Noise { return 0.5f*(1+Li1 + Cy*(Li2-Li1)); } + + private static int seed; + private static int resolution; + private static int resolution2; + // Calculate the gradient instead of storing it. + // This is inefficient(since it is called every time), but allows infinite chunk generation. + // TODO: Make this faster + private static float getGradient(int x, int y, int i) { + r.setSeed(seed); + r.setSeed(r.nextLong()*x+r.nextLong()*y+r.nextLong()*i); + return 2 * r.nextFloat() - 1; + } + /* Function to linearly interpolate between a0 and a1 + * Weight w should be in the range [0.0, 1.0] + * + * as an alternative, this slightly faster equivalent function (macro) can be used: + * #define lerp(a0, a1, w) (a0 + w*(a1 - a0)) + */ + private static float lerp(float a0, float a1, float w) { + return (1.0f - w)*a0 + w*a1; + } + + // Computes the dot product of the distance and gradient vectors. + private static float dotGridGradient(int ix, int iy, int x, int y) { + + // Precomputed (or otherwise) gradient vectors at each grid node + + // Compute the distance vector + float dx = x/(float)resolution - ix; + float dy = y/(float)resolution - iy; + + // Compute the dot-product + float gx = getGradient(ix, iy, 0); + float gy = getGradient(ix, iy, 1); + float gr = (float)Math.sqrt((gx*gx+gy*gy)); + gx /= gr; + gy /= gr; + return (dx*gx + dy*gy); + } + + // Compute Perlin noise at coordinates x, y + private static float perlin(int x, int y) { + + // Determine grid cell coordinates + int x0 = x/resolution; + if(x < 0) { + x0 = (x-resolution2)/resolution; + } + int x1 = x0 + 1; + int y0 = y/resolution; + if(y < 0) { + y0 = (y-resolution2)/resolution; + } + int y1 = y0 + 1; + + // Determine interpolation weights + // Could also use higher order polynomial/s-curve here + float sx = (x&resolution2)/(float)resolution; + float sy = (y&resolution2)/(float)resolution; + + // Interpolate between grid point gradients + float n0, n1, ix0, ix1, value; + + n0 = dotGridGradient(x0, y0, x, y); + n1 = dotGridGradient(x1, y0, x, y); + ix0 = lerp(n0, n1, sx); + + n0 = dotGridGradient(x0, y1, x, y); + n1 = dotGridGradient(x1, y1, x, y); + ix1 = lerp(n0, n1, sx); + + value = lerp(ix0, ix1, sy); + value = 0.5F * value + 0.5F; + if(value > 1) + value = 1; + return value; + } public static float[][] generateMap(int width, int height, int scale, int seed) { return generateMapFragment(0, 0, width, height, scale, seed); @@ -82,10 +162,14 @@ public class Noise { public static float[][] generateMapFragment(int x, int y, int width, int height, int scale, int seed) { float[][] map = new float[width][height]; + resolution = scale; + resolution2 = resolution-1; + Noise.seed = seed; for (int x1 = x; x1 < width + x; x1++) { for (int y1 = y; y1 < height + y; y1++) { - map[x1 - x][y1 - y] = get2DPerlinNoiseValue(x1, y1, scale, seed); + //map[x1 - x][y1 - y] = get2DPerlinNoiseValue(x1, y1, scale, seed); + map[x1 - x][y1 - y] = perlin(x1, y1); } }