From 4195eab9860bfbb008b59230cc89ba5c03cadf1f Mon Sep 17 00:00:00 2001 From: Cubitect Date: Thu, 3 Jun 2021 16:38:08 +0200 Subject: [PATCH] Added viability check for end cities + some functionality for end surface height --- finders.c | 108 ++++++++++++++++++++ finders.h | 5 +- layers.c | 294 ++++++++++++++++++++++++++++++++++++++++++++++-------- layers.h | 37 ++++++- 4 files changed, 395 insertions(+), 49 deletions(-) diff --git a/finders.c b/finders.c index 7922c8a..94b5fcd 100644 --- a/finders.c +++ b/finders.c @@ -1796,6 +1796,114 @@ int isViableNetherStructurePos(int structureType, int mc, NetherNoise *nn, } +/* Given bordering noise columns and a fractional position between those, + * determine the surface block height (i.e. where the interpolated noise > 0). + * Note that the noise columns should be of size: ncolxz[ colheight+1 ] + */ +int getSurfaceHeight( + const double ncol00[], const double ncol01[], + const double ncol10[], const double ncol11[], int colheight, + int blockspercell, double dx, double dz); + +void sampleNoiseColumnEnd(double column[33], const SurfaceNoise *sn, + const EndNoise *en, int x, int z); + +int isViableEndCityPos(int mc, EndNoise *en, SurfaceNoise *sn, + int64_t seed, int blockX, int blockZ) +{ + if (mc < MC_1_9) + return 0; + + setEndSeed(en, seed); + + // end biomes vary only on a per-chunk scale (1:16) + // voronoi shouldn't matter as the check will be near the chunk center + int id; + int chunkX = blockX >> 4, chunkZ = blockZ >> 4; + mapEndBiome(en, &id, chunkX, chunkZ, 1, 1); + if (!isViableFeatureBiome(mc, End_City, id)) + return 0; + + if (!sn) + return 1; + + initSurfaceNoiseEnd(sn, seed); + + blockX = (chunkX << 4) + 7; + blockZ = (chunkZ << 4) + 7; + int cellx = (blockX >> 3); + int cellz = (blockZ >> 3); + double ncol[3][3][33]; + + sampleNoiseColumnEnd(ncol[0][0], sn, en, cellx, cellz); + sampleNoiseColumnEnd(ncol[0][1], sn, en, cellx, cellz+1); + sampleNoiseColumnEnd(ncol[1][0], sn, en, cellx+1, cellz); + sampleNoiseColumnEnd(ncol[1][1], sn, en, cellx+1, cellz+1); + + int h00, h01, h10, h11; + h00 = getSurfaceHeight(ncol[0][0], ncol[0][1], ncol[1][0], ncol[1][1], + 32, 4, (blockX & 7) / 8.0, (blockZ & 7) / 8.0); + + int64_t cs; + setSeed(&cs, chunkX + chunkZ * 10387313LL); + switch (nextInt(&cs, 4)) + { + case 0: // (++) 0 + sampleNoiseColumnEnd(ncol[0][2], sn, en, cellx+0, cellz+2); + sampleNoiseColumnEnd(ncol[1][2], sn, en, cellx+1, cellz+2); + sampleNoiseColumnEnd(ncol[2][0], sn, en, cellx+2, cellz+0); + sampleNoiseColumnEnd(ncol[2][1], sn, en, cellx+2, cellz+1); + sampleNoiseColumnEnd(ncol[2][1], sn, en, cellx+2, cellz+2); + h01 = getSurfaceHeight(ncol[0][1], ncol[0][2], ncol[1][1], ncol[1][2], + 32, 4, ((blockX ) & 7) / 8.0, ((blockZ + 5) & 7) / 8.0); + h10 = getSurfaceHeight(ncol[1][0], ncol[1][1], ncol[2][0], ncol[2][1], + 32, 4, ((blockX + 5) & 7) / 8.0, ((blockZ ) & 7) / 8.0); + h11 = getSurfaceHeight(ncol[1][1], ncol[1][2], ncol[2][1], ncol[2][2], + 32, 4, ((blockX + 5) & 7) / 8.0, ((blockZ + 5) & 7) / 8.0); + break; + + case 1: // (-+) 90 + sampleNoiseColumnEnd(ncol[0][2], sn, en, cellx+0, cellz+2); + sampleNoiseColumnEnd(ncol[1][2], sn, en, cellx+1, cellz+2); + h01 = getSurfaceHeight(ncol[0][1], ncol[0][2], ncol[1][1], ncol[1][2], + 32, 4, ((blockX ) & 7) / 8.0, ((blockZ + 5) & 7) / 8.0); + h10 = getSurfaceHeight(ncol[0][0], ncol[0][1], ncol[1][0], ncol[1][1], + 32, 4, ((blockX - 5) & 7) / 8.0, ((blockZ ) & 7) / 8.0); + h11 = getSurfaceHeight(ncol[0][1], ncol[0][2], ncol[1][1], ncol[1][2], + 32, 4, ((blockX - 5) & 7) / 8.0, ((blockZ + 5) & 7) / 8.0); + break; + + case 2: // (--) 180 + h01 = getSurfaceHeight(ncol[0][0], ncol[0][1], ncol[1][0], ncol[1][1], + 32, 4, ((blockX ) & 7) / 8.0, ((blockZ - 5) & 7) / 8.0); + h10 = getSurfaceHeight(ncol[0][0], ncol[0][1], ncol[1][0], ncol[1][1], + 32, 4, ((blockX - 5) & 7) / 8.0, ((blockZ ) & 7) / 8.0); + h11 = getSurfaceHeight(ncol[0][0], ncol[0][1], ncol[1][0], ncol[1][1], + 32, 4, ((blockX - 5) & 7) / 8.0, ((blockZ - 5) & 7) / 8.0); + break; + + case 3: // (+-) 270 + sampleNoiseColumnEnd(ncol[2][0], sn, en, cellx+2, cellz+0); + sampleNoiseColumnEnd(ncol[2][1], sn, en, cellx+2, cellz+1); + h01 = getSurfaceHeight(ncol[0][0], ncol[0][1], ncol[1][0], ncol[1][1], + 32, 4, ((blockX ) & 7) / 8.0, ((blockZ - 5) & 7) / 8.0); + h10 = getSurfaceHeight(ncol[1][0], ncol[1][1], ncol[2][0], ncol[2][1], + 32, 4, ((blockX + 5) & 7) / 8.0, ((blockZ ) & 7) / 8.0); + h11 = getSurfaceHeight(ncol[1][0], ncol[1][1], ncol[2][0], ncol[2][1], + 32, 4, ((blockX + 5) & 7) / 8.0, ((blockZ - 5) & 7) / 8.0); + break; + + default: + return 0; // error + } + + if (h01 < h00) h00 = h01; + if (h10 < h00) h00 = h10; + if (h11 < h00) h00 = h11; + return h00 >= 60; +} + + //============================================================================== // Finding Properties of Structures //============================================================================== diff --git a/finders.h b/finders.h index ec54bfc..93a3a1a 100644 --- a/finders.h +++ b/finders.h @@ -627,7 +627,8 @@ int isViableStructurePos(int structureType, int mc, LayerStack *g, int64_t seed, int blockX, int blockZ); int isViableNetherStructurePos(int structureType, int mc, NetherNoise *nn, int64_t seed, int blockX, int blockZ); -// TODO: viability checks for end cities +int isViableEndCityPos(int mc, EndNoise *en, SurfaceNoise *sn, + int64_t seed, int blockX, int blockZ); /* Checks if the specified structure type could generate in the given biome. */ @@ -726,7 +727,7 @@ int canBiomeGenerate(int layerId, int mc, int biomeID); /* Given a biome 'id' at a generation 'layer', this functions finds which * biomes may generate from it. The result is stored in the bitfields: * mL : for ids 0-63 - * mM : for ids 128-192 + * mM : for ids 128-191 */ void genPotential(uint64_t *mL, uint64_t *mM, int layer, int mc, int id); diff --git a/layers.c b/layers.c index 8f7c9c4..98a6d6d 100644 --- a/layers.c +++ b/layers.c @@ -365,6 +365,32 @@ static double lerp(double part, double from, double to) return from + part * (to - from); } +static double lerp2(double dx, double dy, double v00, double v10, double v01, double v11) +{ + return lerp(dy, lerp(dx, v00, v10), lerp(dx, v01, v11)); +} + +static double lerp3(double dx, double dy, double dz, + double v000, double v100, double v010, double v110, + double v001, double v101, double v011, double v111) +{ + v000 = lerp2(dx, dy, v000, v100, v010, v110); + v001 = lerp2(dx, dy, v001, v101, v011, v111); + return lerp(dz, v000, v001); +} + +static double clampedLerp(double part, double from, double to) +{ + if (part <= 0) return from; + if (part >= 1) return to; + return lerp(part, from, to); +} + +static double maintainPrecision(double x) +{ + return x - floor(x / 33554432.0 + 0.5) * 33554432.0; +} + /* Table of vectors to cube edge centres (12 + 4 extra) */ const double cEdgeX[] = {1.0,-1.0, 1.0,-1.0, 1.0,-1.0, 1.0,-1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0,-1.0, 0.0}; const double cEdgeY[] = {1.0, 1.0,-1.0,-1.0, 0.0, 0.0, 0.0, 0.0, 1.0,-1.0, 1.0,-1.0, 1.0,-1.0, 1.0,-1.0}; @@ -377,6 +403,7 @@ static double indexedLerp(int idx, double d1, double d2, double d3) return cEdgeX[idx] * d1 + cEdgeY[idx] * d2 + cEdgeZ[idx] * d3; } + void perlinInit(PerlinNoise *rnd, int64_t *seed) { int i = 0; @@ -400,7 +427,8 @@ void perlinInit(PerlinNoise *rnd, int64_t *seed) } -double samplePerlin(const PerlinNoise *rnd, double d1, double d2, double d3) +double samplePerlin(const PerlinNoise *rnd, double d1, double d2, double d3, + double yamp, double ymin) { d1 += rnd->a; d2 += rnd->b; @@ -415,6 +443,12 @@ double samplePerlin(const PerlinNoise *rnd, double d1, double d2, double d3) double t2 = d2*d2*d2 * (d2 * (d2*6.0-15.0) + 10.0); double t3 = d3*d3*d3 * (d3 * (d3*6.0-15.0) + 10.0); + if (yamp) + { + double yclamp = ymin < d2 ? ymin : d2; + d2 -= floor(yclamp / yamp) * yamp; + } + i1 &= 0xff; i2 &= 0xff; i3 &= 0xff; @@ -486,77 +520,137 @@ double sampleSimplex2D(const PerlinNoise *rnd, double x, double y) } -void doublePerlinInit(DoublePerlinNoise *rnd, int64_t *seed, - PerlinNoise *octavesA, PerlinNoise *octavesB, int omin, int len) +void octaveInit(OctaveNoise *rnd, int64_t *seed, PerlinNoise *octaves, + int omin, int len) { - if (len < 1 || omin+len > 0) + int end = omin+len-1; + int i; + if (len < 1 || end > 0) { - printf("doublePerlinInit(): unsupported octave range\n"); + printf("octavePerlinInit(): unsupported octave range\n"); return; } - rnd->octaves[0] = octavesA; - rnd->octaves[1] = octavesB; + rnd->octaves = octaves; rnd->octcnt = len; - rnd->amplitude = (10.0 / 6.0) * len / (len + 1); - - int ab, i; - for (ab = 0; ab < 2; ab++) + if (end == 0) { - // octave zero is initialized first - if (omin <= 0 && omin+len > 0) - perlinInit(&rnd->octaves[ab][-omin], seed); - else - skipNextN(seed, 262); - - if (omin+len < 0) - skipNextN(seed, -(omin+len) * 262); - for (i = 0; i < len; i++) - if (i+omin < 0) - perlinInit(&rnd->octaves[ab][i], seed); - - rnd->persist[ab] = pow(2.0, omin+len - 1); - rnd->lacuna[ab] = 1.0 / ((1LL << len) - 1.0); + perlinInit(&rnd->octaves[0], seed); + i = 1; } + else + { + skipNextN(seed, -end*262); + i = 0; + } + for (; i < len; i++) + { + perlinInit(&rnd->octaves[i], seed); + } + rnd->persist = pow(2.0, end); + rnd->lacuna = 1.0 / ((1LL << len) - 1.0); } - -static double sampleOctave(const PerlinNoise *octaves, int len, - double x, double y, double z, double persist, double lacuna) +double sampleOctave(const OctaveNoise *rnd, double x, double y, double z) { + double persist = rnd->persist; + double lacuna = rnd->lacuna; double v = 0; int i; - for (i = 0; i < len; i++) + for (i = 0; i < rnd->octcnt; i++) { - double ax = x * persist; - double ay = y * persist; - double az = z * persist; - ax -= floor(ax / 33554432.0 + 0.5) * 33554432.0; - ay -= floor(ay / 33554432.0 + 0.5) * 33554432.0; - az -= floor(az / 33554432.0 + 0.5) * 33554432.0; - v += lacuna * samplePerlin(octaves+i, ax, ay, az); + double ax = maintainPrecision(x * persist); + double ay = maintainPrecision(y * persist); + double az = maintainPrecision(z * persist); + v += lacuna * samplePerlin(rnd->octaves+i, ax, ay, az, 0, 0); persist *= 0.5; lacuna *= 2.0; } return v; } + +void doublePerlinInit(DoublePerlinNoise *rnd, int64_t *seed, + PerlinNoise *octavesA, PerlinNoise *octavesB, int omin, int len) +{ // require: len >= 1 && omin+len <= 0 + rnd->amplitude = (10.0 / 6.0) * len / (len + 1); + octaveInit(&rnd->octA, seed, octavesA, omin, len); + octaveInit(&rnd->octB, seed, octavesB, omin, len); +} + double sampleDoublePerlin(const DoublePerlinNoise *rnd, double x, double y, double z) { const double f = 337.0 / 331.0; double v = 0; - v += sampleOctave(rnd->octaves[0], rnd->octcnt, x, y, z, - rnd->persist[0], rnd->lacuna[0]); - - v += sampleOctave(rnd->octaves[1], rnd->octcnt, x*f, y*f, z*f, - rnd->persist[1], rnd->lacuna[1]); + v += sampleOctave(&rnd->octA, x, y, z); + v += sampleOctave(&rnd->octB, x*f, y*f, z*f); return v * rnd->amplitude; } +void initSurfaceNoise(SurfaceNoise *rnd, int64_t *seed, + double xzScale, double yScale, double xzFactor, double yFactor) +{ + rnd->xzScale = xzScale; + rnd->yScale = yScale; + rnd->xzFactor = xzFactor; + rnd->yFactor = yFactor; + octaveInit(&rnd->octmin, seed, rnd->oct+0, -15, 16); + octaveInit(&rnd->octmax, seed, rnd->oct+16, -15, 16); + octaveInit(&rnd->octmain, seed, rnd->oct+32, -7, 8); +} + +void initSurfaceNoiseEnd(SurfaceNoise *rnd, int64_t seed) +{ + int64_t s; + setSeed(&s, seed); + initSurfaceNoise(rnd, &s, 2.0, 1.0, 80.0, 160.0); +} + +double sampleSurfaceNoise(const SurfaceNoise *rnd, int x, int y, int z) +{ + double xzScale = 684.412 * rnd->xzScale; + double yScale = 684.412 * rnd->yScale; + double xzStep = xzScale / rnd->xzFactor; + double yStep = yScale / rnd->yFactor; + + double minNoise = 0; + double maxNoise = 0; + double mainNoise = 0; + double persist = 1.0; + double dx, dy, dz, sy, ty; + int i; + + for (i = 0; i < 16; i++) + { + dx = maintainPrecision(x * xzScale * persist); + dy = maintainPrecision(y * yScale * persist); + dz = maintainPrecision(z * xzScale * persist); + sy = yScale * persist; + ty = y * sy; + + minNoise += samplePerlin(&rnd->octmin.octaves[i], dx, dy, dz, sy, ty) / persist; + maxNoise += samplePerlin(&rnd->octmax.octaves[i], dx, dy, dz, sy, ty) / persist; + + if (i < 8) + { + dx = maintainPrecision(x * xzStep * persist); + dy = maintainPrecision(y * yStep * persist); + dz = maintainPrecision(z * xzStep * persist); + sy = yStep * persist; + ty = y * sy; + mainNoise += samplePerlin(&rnd->octmain.octaves[i], dx, dy, dz, sy, ty) / persist; + } + persist /= 2.0; + } + + return clampedLerp(0.5 + 0.05*mainNoise, minNoise/512.0, maxNoise/512.0); +} + + //============================================================================== // Nether (1.16+) and End (1.9+) Biome Generation //============================================================================== @@ -817,6 +911,122 @@ int mapEnd(const EndNoise *en, int *out, int x, int z, int w, int h) return 0; } +float getEndHeightNoise(const EndNoise *en, int x, int z) +{ + int hx = x / 2; + int hz = z / 2; + int oddx = x % 2; + int oddz = z % 2; + int i, j; + + int64_t h = 64 * (x*(int64_t)x + z*(int64_t)z); + + for (j = -12; j <= 12; j++) + { + for (i = -12; i <= 12; i++) + { + int64_t rx = hx + i; + int64_t rz = hz + j; + uint16_t v = 0; + if (rx*rx + rz*rz > 4096 && sampleSimplex2D(en, rx, rz) < -0.9f) + { + v = (llabs(rx) * 3439 + llabs(rz) * 147) % 13 + 9; + rx = (oddx - i * 2); + rz = (oddz - j * 2); + int64_t noise = (rx*rx + rz*rz) * v*v; + if (noise < h) + h = noise; + } + } + } + + float ret = 100 - sqrtf((float) h); + if (ret < -100) ret = -100; + if (ret > 80) ret = 80; + return ret; +} + +void sampleNoiseColumnEnd(double column[33], const SurfaceNoise *sn, + const EndNoise *en, int x, int z) +{ + double depth = getEndHeightNoise(en, x, z) - 8.0f; + int y; + for (y = 0; y <= 32; y++) + { + double noise = sampleSurfaceNoise(sn, x, y, z); + noise += depth; // falloff for the End is just the depth + // clamp top and bottom slides from End settings + noise = clampedLerp((32 + 46 - y) / 64.0, -3000, noise); + noise = clampedLerp((y - 1) / 7.0, -30, noise); + column[y] = noise; + } +} + +/* Given bordering noise columns and a fractional position between those, + * determine the surface block height (i.e. where the interpolated noise > 0). + * Note that the noise columns should be of size: ncolxz[ colheight+1 ] + */ +int getSurfaceHeight( + const double ncol00[], const double ncol01[], + const double ncol10[], const double ncol11[], int colheight, + int blockspercell, double dx, double dz) +{ + int y, celly; + for (celly = colheight-1; celly >= 0; celly--) + { + double v000 = ncol00[celly]; + double v001 = ncol01[celly]; + double v100 = ncol10[celly]; + double v101 = ncol11[celly]; + double v010 = ncol00[celly+1]; + double v011 = ncol01[celly+1]; + double v110 = ncol10[celly+1]; + double v111 = ncol11[celly+1]; + + for (y = blockspercell - 1; y >= 0; y--) + { + double dy = y / (double) blockspercell; + double noise = lerp3(dy, dx, dz, // Note: not x, y, z + v000, v010, v100, v110, + v001, v011, v101, v111); + if (noise > 0) + return celly * blockspercell + y; + } + } + + return 0; +} + +int getSurfaceHeightEnd(int mc, int64_t seed, int x, int z) +{ + (void) mc; + + EndNoise en; + setEndSeed(&en, seed); + + SurfaceNoise sn; + initSurfaceNoiseEnd(&sn, seed); + + // end noise columns vary on a grid of cell size = eight + int cellx = (x >> 3); + int cellz = (z >> 3); + double dx = (x & 7) / 8.0; + double dz = (z & 7) / 8.0; + + double ncol00[33]; + double ncol01[33]; + double ncol10[33]; + double ncol11[33]; + sampleNoiseColumnEnd(ncol00, &sn, &en, cellx, cellz); + sampleNoiseColumnEnd(ncol01, &sn, &en, cellx, cellz+1); + sampleNoiseColumnEnd(ncol10, &sn, &en, cellx+1, cellz); + sampleNoiseColumnEnd(ncol11, &sn, &en, cellx+1, cellz+1); + + return getSurfaceHeight(ncol00, ncol01, ncol10, ncol11, 32, 4, dx, dz); +} + + + //============================================================================== // Layers //============================================================================== @@ -2351,7 +2561,7 @@ int mapOceanTemp(const Layer * l, int * out, int x, int z, int w, int h) { for (i = 0; i < w; i++) { - double tmp = samplePerlin(rnd, (i + x) / 8.0, (j + z) / 8.0, 0); + double tmp = samplePerlin(rnd, (i + x) / 8.0, (j + z) / 8.0, 0, 0, 0); if (tmp > 0.4) out[i + j*w] = warm_ocean; diff --git a/layers.h b/layers.h index 25064eb..b770521 100644 --- a/layers.h +++ b/layers.h @@ -142,13 +142,19 @@ STRUCT(PerlinNoise) double a, b, c; }; +STRUCT(OctaveNoise) +{ + double lacuna; + double persist; + int octcnt; + PerlinNoise *octaves; +}; + STRUCT(DoublePerlinNoise) { double amplitude; - double lacuna[2]; - double persist[2]; - PerlinNoise *octaves[2]; - int octcnt; + OctaveNoise octA; + OctaveNoise octB; }; struct Layer; @@ -184,6 +190,15 @@ STRUCT(NetherNoise) typedef PerlinNoise EndNoise; +STRUCT(SurfaceNoise) +{ + double xzScale, yScale; + double xzFactor, yFactor; + OctaveNoise octmin; + OctaveNoise octmax; + OctaveNoise octmain; + PerlinNoise oct[16+16+8]; +}; #ifdef __cplusplus extern "C" @@ -205,14 +220,25 @@ void setLayerSeed(Layer *layer, int64_t worldSeed); //============================================================================== void perlinInit(PerlinNoise *rnd, int64_t *seed); -double samplePerlin(const PerlinNoise *rnd, double x, double y, double z); +double samplePerlin(const PerlinNoise *rnd, double x, double y, double z, + double yamp, double ymin); double sampleSimplex2D(const PerlinNoise *rnd, double x, double y); +void octaveInit(OctaveNoise *rnd, int64_t *seed, PerlinNoise *octaves, + int omin, int len); +double sampleOctave(const OctaveNoise *rnd, double x, double y, double z); + void doublePerlinInit(DoublePerlinNoise *rnd, int64_t *seed, PerlinNoise *octavesA, PerlinNoise *octavesB, int omin, int len); double sampleDoublePerlin(const DoublePerlinNoise *rnd, double x, double y, double z); +void initSurfaceNoise(SurfaceNoise *rnd, int64_t *seed, + double xzScale, double yScale, double xzFactor, double yFactor); +void initSurfaceNoiseEnd(SurfaceNoise *rnd, int64_t seed); +double sampleSurfaceNoise(const SurfaceNoise *rnd, int x, int y, int z); + + //============================================================================== // Nether (1.16+) and End (1.9+) Biome Generation //============================================================================== @@ -251,6 +277,7 @@ int mapNether3D(const NetherNoise *nn, int *out, int x, int z, int w, int h, int void setEndSeed(EndNoise *en, int64_t seed); int mapEndBiome(const EndNoise *en, int *out, int x, int z, int w, int h); int mapEnd(const EndNoise *en, int *out, int x, int z, int w, int h); +int getSurfaceHeightEnd(int mc, int64_t seed, int x, int z); //============================================================================== // Seed Helpers