Added optimized End biome generation

This commit is contained in:
Cubitect 2021-03-18 09:21:11 +01:00
parent c86145a640
commit 19aa9b4944
2 changed files with 207 additions and 10 deletions

190
layers.c
View File

@ -260,6 +260,45 @@ double samplePerlin(const PerlinNoise *rnd, double d1, double d2, double d3)
return lerp(t3, l1, l5);
}
static double simplexGrad(int idx, double x, double y, double z, double d)
{
double con = d - x*x - y*y - z*z;
if (con < 0)
return 0;
con *= con;
return con * con * indexedLerp(idx, x, y, z);
}
double sampleSimplex2D(const PerlinNoise *rnd, double x, double y)
{
const double SKEW = 0.5 * (sqrt(3) - 1.0);
const double UNSKEW = (3.0 - sqrt(3)) / 6.0;
double hf = (x + y) * SKEW;
int hx = (int)floor(x + hf);
int hz = (int)floor(y + hf);
double mhxz = (hx + hz) * UNSKEW;
double x0 = x - (hx - mhxz);
double y0 = y - (hz - mhxz);
int offx = (x0 > y0);
int offz = !offx;
double x1 = x0 - offx + UNSKEW;
double y1 = y0 - offz + UNSKEW;
double x2 = x0 - 1.0 + 2.0 * UNSKEW;
double y2 = y0 - 1.0 + 2.0 * UNSKEW;
int gi0 = rnd->d[0xff & (hz)];
int gi1 = rnd->d[0xff & (hz + offz)];
int gi2 = rnd->d[0xff & (hz + 1)];
gi0 = rnd->d[0xff & (gi0 + hx)];
gi1 = rnd->d[0xff & (gi1 + hx + offx)];
gi2 = rnd->d[0xff & (gi2 + hx + 1)];
double t = 0;
t += simplexGrad(gi0 % 12, x0, y0, 0.0, 0.5);
t += simplexGrad(gi1 % 12, x1, y1, 0.0, 0.5);
t += simplexGrad(gi2 % 12, x2, y2, 0.0, 0.5);
return 70.0 * t;
}
void doublePerlinInit(DoublePerlinNoise *rnd, int64_t *seed,
PerlinNoise *octavesA, PerlinNoise *octavesB, int omin, int len)
@ -332,6 +371,10 @@ double sampleDoublePerlin(const DoublePerlinNoise *rnd,
}
//==============================================================================
// Nether (1.16+) and End (1.9+) Biome Generation
//==============================================================================
void setNetherSeed(NetherNoise *nn, int64_t seed)
{
int64_t s;
@ -341,7 +384,6 @@ void setNetherSeed(NetherNoise *nn, int64_t seed)
doublePerlinInit(&nn->humidity, &s, &nn->oct[4], &nn->oct[6], -7, 2);
}
static float distsq(const float *a, const float *b, int n)
{
float dsq = 0;
@ -389,6 +431,134 @@ int getNetherBiome(const NetherNoise *nn, int x, int y, int z)
}
void setEndSeed(EndNoise *en, int64_t seed)
{
int64_t s;
setSeed(&s, seed);
skipNextN(&s, 17292);
perlinInit(en, &s);
}
__attribute__(( optimize("unroll-loops") ))
static int getEndBiome(int hx, int hz, const uint16_t *hmap, int hw)
{
int i, j;
const uint16_t ds[26] = { // (25-2*i)*(25-2*i)
// 0 1 2 3 4 5 6 7 8 9 10 11 12
625, 529, 441, 361, 289, 225, 169, 121, 81, 49, 25, 9, 1,
// 13 14 15 16 17 18 19 20 21 22 23 24, 25
1, 9, 25, 49, 81, 121, 169, 225, 289, 361, 441, 529, 625,
};
const uint16_t *p_dsi = ds + (hx < 0);
const uint16_t *p_dsj = ds + (hz < 0);
const uint16_t *p_elev = hmap;
uint32_t h;
if (abs(hx) > 15 || abs(hz) > 15)
h = 14401;
else
h = 64 * (hx*hx + hz*hz);
for (j = 0; j < 25; j++)
{
uint16_t hdsj = p_dsj[j];
for (i = 0; i < 25; i++)
{
uint16_t elev = p_elev[i];
if (elev)
{
uint32_t hds = (p_dsi[i] + (uint32_t)hdsj) * elev;
if (hds < h)
h = hds;
}
}
p_elev += hw;
}
if (h < 3600)
return end_highlands;
else if (h <= 10000)
return end_midlands;
else if (h <= 14400)
return end_barrens;
return small_end_islands;
}
int mapEndBiome(const EndNoise *en, int *out, int x, int z, int w, int h)
{
int i, j;
int hw = w + 26;
int hh = h + 26;
uint16_t *hmap = (uint16_t*) malloc(hw * hh * sizeof(*hmap));
for (j = 0; j < hh; j++)
{
for (i = 0; i < hw; i++)
{
int64_t rx = x + i - 12;
int64_t rz = z + j - 12;
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;
v *= v;
}
hmap[j*hw+i] = v;
}
}
for (j = 0; j < h; j++)
{
for (i = 0; i < w; i++)
{
int64_t hx = (i+x);
int64_t hz = (j+z);
if (hx*hx + hz*hz <= 4096L)
out[j*w+i] = the_end;
else
{
hx = 2*hx + 1;
hz = 2*hz + 1;
uint16_t *p_elev = &hmap[(hz/2-z)*hw + (hx/2-x)];
out[j*w+i] = getEndBiome(hx, hz, p_elev, hw);
}
}
}
free(hmap);
return 0;
}
int mapEnd(const EndNoise *en, int *out, int x, int z, int w, int h)
{
int cx = x >> 2;
int cz = z >> 2;
int cw = ((x+w) >> 2) + 1 - cx;
int ch = ((z+h) >> 2) + 1 - cz;
int *buf = (int*) malloc(cw * ch * sizeof(int));
mapEndBiome(en, buf, cx, cz, cw, ch);
int i, j;
for (j = 0; j < h; j++)
{
int cj = ((z+j) >> 2) - cz;
for (i = 0; i < w; i++)
{
int ci = ((x+i) >> 2) - cx;
int v = buf[cj*cw+ci];
out[j*w+i] = v;
}
}
free(buf);
return 0;
}
//==============================================================================
// Layers
//==============================================================================
@ -2026,9 +2196,12 @@ int mapVoronoiZoom(const Layer * l, int * out, int x, int z, int w, int h)
int pW = ((x + w) >> 2) - pX + 2;
int pH = ((z + h) >> 2) - pZ + 2;
int err = l->p->getMap(l->p, out, pX, pZ, pW, pH);
if U(err != 0)
return err;
if (l->p)
{
int err = l->p->getMap(l->p, out, pX, pZ, pW, pH);
if (err != 0)
return err;
}
int64_t sha = l->startSalt;
int *buf = (int *) malloc(w*h*sizeof(*buf));
@ -2189,9 +2362,12 @@ int mapVoronoiZoom114(const Layer * l, int * out, int x, int z, int w, int h)
int pW = ((x + w) >> 2) - pX + 2;
int pH = ((z + h) >> 2) - pZ + 2;
int err = l->p->getMap(l->p, out, pX, pZ, pW, pH);
if U(err != 0)
return err;
if (l->p)
{
int err = l->p->getMap(l->p, out, pX, pZ, pW, pH);
if (err != 0)
return err;
}
int newW = pW << 2;
int newH = pH << 2;

View File

@ -187,6 +187,8 @@ STRUCT(NetherNoise)
PerlinNoise oct[8]; // buffer for octaves in double perlin noise
};
typedef PerlinNoise EndNoise;
#ifdef __cplusplus
extern "C"
@ -213,16 +215,34 @@ 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 sampleSimplex2D(const PerlinNoise *rnd, double x, double y);
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);
// nether noise is scale 1:4
//==============================================================================
// Nether (1.16+) and End (1.9+) Biome Generation
//==============================================================================
/**
* Nether biomes are 3D, and generated at scale 1:4. Use voronoiAccess3D() to
* get coordinates at 1:1 scale. Biome checks for structures are generally done
* at y=0.
*/
void setNetherSeed(NetherNoise *nn, int64_t seed);
int getNetherBiome(const NetherNoise *nn, int x, int y, int z);
/**
* End biome generation is based on simplex noise and varies only at a 1:16
* chunk scale which can be generated with mapEndBiome(). The function mapEnd()
* is a variation which also scales this up on a regular grid to 1:4. The final
* access at a 1:1 scale is the standard voronoi layer.
*/
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);
//==============================================================================
// Seed Helpers
@ -415,8 +435,9 @@ int mapOceanMix (const Layer *, int *, int, int, int, int);
int mapVoronoiZoom (const Layer *, int *, int, int, int, int);
int mapVoronoiZoom114 (const Layer *, int *, int, int, int, int);
// With 1.15, biomes are only generated up to scale 1:4 OceanMix on the server-
// side while voronoi is primarily treated as a client-side access pattern.
// With 1.15 voronoi changed in preparation for 3D biome generation.
// Biome generation now stops at scale 1:4 OceanMix and voronoi is just an
// access algorithm, mapping the 1:1 scale onto its 1:4 correspondent.
// It is seeded by the first 8-bytes of the SHA-256 hash of the world seed.
int64_t getVoroniSHA(int64_t worldSeed);
void voronoiAccess3D(int64_t sha, int x, int y, int z, int *x4, int *y4, int *z4);