2021-11-19 06:13:44 +08:00
|
|
|
|
|
|
|
#include "noise.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
|
|
// grad()
|
|
|
|
/*
|
|
|
|
static double indexedLerp(int idx, double d1, double d2, double d3)
|
|
|
|
{
|
|
|
|
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 };
|
|
|
|
const double cEdgeZ[] = { 0.0, 0.0, 0.0, 0.0, 1.0, 1.0,-1.0,-1.0,
|
|
|
|
1.0, 1.0,-1.0,-1.0, 0.0, 1.0, 0.0,-1.0 };
|
|
|
|
|
|
|
|
idx &= 0xf;
|
|
|
|
return cEdgeX[idx] * d1 + cEdgeY[idx] * d2 + cEdgeZ[idx] * d3;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
ATTR(hot, const, always_inline, artificial)
|
|
|
|
static inline double indexedLerp(int idx, double a, double b, double c)
|
|
|
|
{
|
|
|
|
switch (idx & 0xf)
|
|
|
|
{
|
|
|
|
case 0: return a + b;
|
|
|
|
case 1: return -a + b;
|
|
|
|
case 2: return a - b;
|
|
|
|
case 3: return -a - b;
|
|
|
|
case 4: return a + c;
|
|
|
|
case 5: return -a + c;
|
|
|
|
case 6: return a - c;
|
|
|
|
case 7: return -a - c;
|
|
|
|
case 8: return b + c;
|
|
|
|
case 9: return -b + c;
|
|
|
|
case 10: return b - c;
|
|
|
|
case 11: return -b - c;
|
|
|
|
case 12: return a + b;
|
|
|
|
case 13: return -b + c;
|
|
|
|
case 14: return -a + b;
|
|
|
|
case 15: return -b - c;
|
|
|
|
}
|
2022-08-14 19:52:26 +08:00
|
|
|
UNREACHABLE();
|
2021-11-19 06:13:44 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void perlinInit(PerlinNoise *noise, uint64_t *seed)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
//memset(noise, 0, sizeof(*noise));
|
|
|
|
noise->a = nextDouble(seed) * 256.0;
|
|
|
|
noise->b = nextDouble(seed) * 256.0;
|
|
|
|
noise->c = nextDouble(seed) * 256.0;
|
|
|
|
noise->amplitude = 1.0;
|
|
|
|
noise->lacunarity = 1.0;
|
|
|
|
|
2023-01-15 21:01:57 +08:00
|
|
|
uint8_t *idx = noise->d;
|
2021-11-19 06:13:44 +08:00
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
{
|
2023-01-15 21:01:57 +08:00
|
|
|
idx[i] = i;
|
2021-11-19 06:13:44 +08:00
|
|
|
}
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
{
|
|
|
|
int j = nextInt(seed, 256 - i) + i;
|
2023-01-15 21:01:57 +08:00
|
|
|
uint8_t n = idx[i];
|
|
|
|
idx[i] = idx[j];
|
|
|
|
idx[j] = n;
|
|
|
|
idx[i + 256] = idx[i];
|
2021-11-19 06:13:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void xPerlinInit(PerlinNoise *noise, Xoroshiro *xr)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
//memset(noise, 0, sizeof(*noise));
|
|
|
|
noise->a = xNextDouble(xr) * 256.0;
|
|
|
|
noise->b = xNextDouble(xr) * 256.0;
|
|
|
|
noise->c = xNextDouble(xr) * 256.0;
|
|
|
|
noise->amplitude = 1.0;
|
|
|
|
noise->lacunarity = 1.0;
|
|
|
|
|
2023-01-15 21:01:57 +08:00
|
|
|
uint8_t *idx = noise->d;
|
2021-11-19 06:13:44 +08:00
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
{
|
2023-01-15 21:01:57 +08:00
|
|
|
idx[i] = i;
|
2021-11-19 06:13:44 +08:00
|
|
|
}
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
{
|
|
|
|
int j = xNextInt(xr, 256 - i) + i;
|
2023-01-15 21:01:57 +08:00
|
|
|
uint8_t n = idx[i];
|
|
|
|
idx[i] = idx[j];
|
|
|
|
idx[j] = n;
|
|
|
|
idx[i + 256] = idx[i];
|
2021-11-19 06:13:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
double samplePerlin(const PerlinNoise *noise, double d1, double d2, double d3,
|
|
|
|
double yamp, double ymin)
|
|
|
|
{
|
|
|
|
d1 += noise->a;
|
|
|
|
d2 += noise->b;
|
|
|
|
d3 += noise->c;
|
2023-01-15 21:01:57 +08:00
|
|
|
const uint8_t *idx = noise->d;
|
|
|
|
int i1 = (int) floor(d1);
|
|
|
|
int i2 = (int) floor(d2);
|
|
|
|
int i3 = (int) floor(d3);
|
2021-11-19 06:13:44 +08:00
|
|
|
d1 -= i1;
|
|
|
|
d2 -= i2;
|
|
|
|
d3 -= i3;
|
|
|
|
double t1 = d1*d1*d1 * (d1 * (d1*6.0-15.0) + 10.0);
|
|
|
|
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;
|
|
|
|
|
2023-01-15 21:01:57 +08:00
|
|
|
int a1 = idx[i1] + i2;
|
|
|
|
int b1 = idx[i1+1] + i2;
|
|
|
|
|
|
|
|
int a2 = idx[a1] + i3;
|
|
|
|
int b2 = idx[b1] + i3;
|
|
|
|
|
|
|
|
double l1 = indexedLerp(idx[a2], d1, d2, d3);
|
|
|
|
double l2 = indexedLerp(idx[b2], d1-1, d2, d3);
|
|
|
|
double l5 = indexedLerp(idx[a2+1], d1, d2, d3-1);
|
|
|
|
double l6 = indexedLerp(idx[b2+1], d1-1, d2, d3-1);
|
Added noise generation functions for Beta 1.7 terrain
(biome_tree.c)
- Moved a curly brace to maintain style consistent with repo
(generator.h)
- Removed SurfaceNoiseBeta struct from Generator
(generator.c)
- applySeed(): Removed initSurfaceNoiseBeta() call
- genBiomes():
Now declares and initializes SurfaceNoiseBeta when mc <= MC_B1_7
Returns err when g->dim == DIM_END and mc < MC_1_0
(layers.c)
- biomeExists(): Added new checks for mc <= B1_7
- initSurfaceNoiseBeta(): Implemented
- New comment added to sampleBiomeNoiseBeta()
(noise.h)
- Added headers for four new functions defined in noise.c
(noise.c)
- samplePerlin(): Placed all lerps used for 3D noise but not used for 2D noise
behind a (d2 != 0) condition. I'm anticipating needing every bit of
optimization I can find to make ocean-finding practically efficient.
- added new samplePerlinOldBetaTerrain3D() function:
used to generate the octmin, octmax, and octmain noise near sea level
for the given noise column x and z. The necessity for this function will
be explained more in-depth below.
- added new initOctaveOldBetaTerrain() function:
Used to initialize the five Perlin OctaveNoise structs in
SurfaceNoiseBeta. Unlike climate noise, these are Perlin noise maps
instead of Simplex, but the existing octaveInit() cannot be used because
the starting lacunarity values are not integer powers of 2.
- added new sampleOctave2D() function:
Nearly identical to sampleOctave(), except that ay is set to -(p->b),
forcing samplePerlin()'s d2 variable to be 0, resulting in a 2D noise
sample.
- added new sampleOctaveOldBetaTerrain3D() function:
Used to sample the three 3D noisemaps in SurfaceNoiseBeta. The function
is passed a 2-element array of doubles, which is passed to
samplePerlinOldBetaTerrain3D() where the different octaves' values are
added. The only samples made are the ones necessary to determine the
blocks at sea level.
samplePerlinOldBetaTerrain3D() is necessary because when generating these
versions' overworld octmin, octmax, and octmain noise, lower levels affect
higher levels based on whether the lower 8 bits of i2 are equal to the lower
8 bits of i2 from the previous iteration. Each level is only affected by the
immediately previous level where (i2 & 255 !- previ2), so I was able to make an
optimization -- the first several y-level iterations are split into a separate
for loop that only looks at i2, and finds the highest y-level that affects the
final output at sea level. In my Java test program using Minecraft's code, this
optimization brough the time needed to produce an image of a 4096 x 4096 region
down to 1/3 of what it was before the optimization.
The yLacFlag argument determines whether or not lacunarity is cut in half for
the y dimension -- octmain uses lacunarity values for y that are half of the
x/z lacunarity value.
In a future commit, I may extend samplePerlinOldBetaTerrain3D() and
sampleOctaveOldBetaTerrain3D() to generate partial noise columns between ymin
and ymax bounds, in order to eventually implement 4x4 surface height finding.
2023-01-28 09:17:38 +08:00
|
|
|
|
|
|
|
if (d2 != 0) {
|
|
|
|
int a3 = idx[a1+1] + i3;
|
|
|
|
int b3 = idx[b1+1] + i3;
|
|
|
|
|
|
|
|
double l3 = indexedLerp(idx[a3], d1, d2-1, d3);
|
|
|
|
double l4 = indexedLerp(idx[b3], d1-1, d2-1, d3);
|
|
|
|
double l7 = indexedLerp(idx[a3+1], d1, d2-1, d3-1);
|
|
|
|
double l8 = indexedLerp(idx[b3+1], d1-1, d2-1, d3-1);
|
|
|
|
|
|
|
|
l3 = lerp(t1, l3, l4);
|
|
|
|
l7 = lerp(t1, l7, l8);
|
|
|
|
|
|
|
|
l1 = lerp(t2, l1, l3);
|
|
|
|
l5 = lerp(t2, l5, l7);
|
|
|
|
}
|
2021-11-19 06:13:44 +08:00
|
|
|
|
|
|
|
l1 = lerp(t1, l1, l2);
|
|
|
|
l5 = lerp(t1, l5, l6);
|
|
|
|
|
|
|
|
return lerp(t3, l1, l5);
|
|
|
|
}
|
|
|
|
|
Added noise generation functions for Beta 1.7 terrain
(biome_tree.c)
- Moved a curly brace to maintain style consistent with repo
(generator.h)
- Removed SurfaceNoiseBeta struct from Generator
(generator.c)
- applySeed(): Removed initSurfaceNoiseBeta() call
- genBiomes():
Now declares and initializes SurfaceNoiseBeta when mc <= MC_B1_7
Returns err when g->dim == DIM_END and mc < MC_1_0
(layers.c)
- biomeExists(): Added new checks for mc <= B1_7
- initSurfaceNoiseBeta(): Implemented
- New comment added to sampleBiomeNoiseBeta()
(noise.h)
- Added headers for four new functions defined in noise.c
(noise.c)
- samplePerlin(): Placed all lerps used for 3D noise but not used for 2D noise
behind a (d2 != 0) condition. I'm anticipating needing every bit of
optimization I can find to make ocean-finding practically efficient.
- added new samplePerlinOldBetaTerrain3D() function:
used to generate the octmin, octmax, and octmain noise near sea level
for the given noise column x and z. The necessity for this function will
be explained more in-depth below.
- added new initOctaveOldBetaTerrain() function:
Used to initialize the five Perlin OctaveNoise structs in
SurfaceNoiseBeta. Unlike climate noise, these are Perlin noise maps
instead of Simplex, but the existing octaveInit() cannot be used because
the starting lacunarity values are not integer powers of 2.
- added new sampleOctave2D() function:
Nearly identical to sampleOctave(), except that ay is set to -(p->b),
forcing samplePerlin()'s d2 variable to be 0, resulting in a 2D noise
sample.
- added new sampleOctaveOldBetaTerrain3D() function:
Used to sample the three 3D noisemaps in SurfaceNoiseBeta. The function
is passed a 2-element array of doubles, which is passed to
samplePerlinOldBetaTerrain3D() where the different octaves' values are
added. The only samples made are the ones necessary to determine the
blocks at sea level.
samplePerlinOldBetaTerrain3D() is necessary because when generating these
versions' overworld octmin, octmax, and octmain noise, lower levels affect
higher levels based on whether the lower 8 bits of i2 are equal to the lower
8 bits of i2 from the previous iteration. Each level is only affected by the
immediately previous level where (i2 & 255 !- previ2), so I was able to make an
optimization -- the first several y-level iterations are split into a separate
for loop that only looks at i2, and finds the highest y-level that affects the
final output at sea level. In my Java test program using Minecraft's code, this
optimization brough the time needed to produce an image of a 4096 x 4096 region
down to 1/3 of what it was before the optimization.
The yLacFlag argument determines whether or not lacunarity is cut in half for
the y dimension -- octmain uses lacunarity values for y that are half of the
x/z lacunarity value.
In a future commit, I may extend samplePerlinOldBetaTerrain3D() and
sampleOctaveOldBetaTerrain3D() to generate partial noise columns between ymin
and ymax bounds, in order to eventually implement 4x4 surface height finding.
2023-01-28 09:17:38 +08:00
|
|
|
void samplePerlinOldBetaTerrain3D(const PerlinNoise *noise, double *v,
|
|
|
|
double d1, double d3, int yLacFlag)
|
|
|
|
{
|
|
|
|
int genFlag = -1;
|
|
|
|
double l1 = 0;
|
|
|
|
double l3 = 0;
|
|
|
|
double l5 = 0;
|
|
|
|
double l7 = 0;
|
|
|
|
|
|
|
|
d1 += noise->a;
|
|
|
|
d3 += noise->c;
|
|
|
|
const uint8_t *idx = noise->d;
|
|
|
|
int i1 = (int) floor(d1);
|
|
|
|
int i3 = (int) floor(d3);
|
|
|
|
d1 -= i1;
|
|
|
|
d3 -= i3;
|
|
|
|
double t1 = d1*d1*d1 * (d1 * (d1*6.0-15.0) + 10.0);
|
|
|
|
double t3 = d3*d3*d3 * (d3 * (d3*6.0-15.0) + 10.0);
|
|
|
|
|
|
|
|
i1 &= 0xff;
|
|
|
|
i3 &= 0xff;
|
|
|
|
|
|
|
|
double d2;
|
|
|
|
int i2, yi, yic, gfCopy;
|
|
|
|
for (yi = 0; yi <= 7; yi++)
|
|
|
|
{
|
|
|
|
d2 = yi*(noise->lacunarity*((yLacFlag) ? 0.5 : 1))+noise->b;
|
|
|
|
i2 = ((int) floor(d2)) & 0xff;
|
|
|
|
if (yi == 0 || i2 != genFlag)
|
|
|
|
{
|
|
|
|
yic = yi;
|
|
|
|
gfCopy = genFlag;
|
|
|
|
genFlag = i2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
genFlag = gfCopy;
|
|
|
|
|
|
|
|
double t2;
|
|
|
|
for (yi = yic; yi <= 8; yi++)
|
|
|
|
{
|
|
|
|
d2 = yi*(noise->lacunarity*((yLacFlag) ? 0.5 : 1))+noise->b;
|
|
|
|
i2 = (int) floor(d2);
|
|
|
|
d2 -= i2;
|
|
|
|
t2 = d2*d2*d2 * (d2 * (d2*6.0-15.0) + 10.0);
|
|
|
|
|
|
|
|
i2 &= 0xff;
|
|
|
|
|
|
|
|
if (yi == 0 || i2 != genFlag)
|
|
|
|
{
|
|
|
|
genFlag = i2;
|
|
|
|
int a1 = idx[i1] + i2;
|
|
|
|
int b1 = idx[i1+1] + i2;
|
|
|
|
|
|
|
|
int a2 = idx[a1] + i3;
|
|
|
|
int a3 = idx[a1+1] + i3;
|
|
|
|
int b2 = idx[b1] + i3;
|
|
|
|
int b3 = idx[b1+1] + i3;
|
|
|
|
|
|
|
|
double m1 = indexedLerp(idx[a2], d1, d2, d3);
|
|
|
|
double l2 = indexedLerp(idx[b2], d1-1, d2, d3);
|
|
|
|
double m3 = indexedLerp(idx[a3], d1, d2-1, d3);
|
|
|
|
double l4 = indexedLerp(idx[b3], d1-1, d2-1, d3);
|
|
|
|
double m5 = indexedLerp(idx[a2+1], d1, d2, d3-1);
|
|
|
|
double l6 = indexedLerp(idx[b2+1], d1-1, d2, d3-1);
|
|
|
|
double m7 = indexedLerp(idx[a3+1], d1, d2-1, d3-1);
|
|
|
|
double l8 = indexedLerp(idx[b3+1], d1-1, d2-1, d3-1);
|
|
|
|
|
|
|
|
l1 = lerp(t1, m1, l2);
|
|
|
|
l3 = lerp(t1, m3, l4);
|
|
|
|
l5 = lerp(t1, m5, l6);
|
|
|
|
l7 = lerp(t1, m7, l8);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (yi >= 7)
|
|
|
|
{
|
|
|
|
double n1 = lerp(t2, l1, l3);
|
|
|
|
double n5 = lerp(t2, l5, l7);
|
|
|
|
|
|
|
|
v[yi-7] += lerp(t3, n1, n5) * noise->amplitude;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-19 06:13:44 +08:00
|
|
|
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 *noise, 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 = noise->d[0xff & (hz)];
|
|
|
|
int gi1 = noise->d[0xff & (hz + offz)];
|
|
|
|
int gi2 = noise->d[0xff & (hz + 1)];
|
|
|
|
gi0 = noise->d[0xff & (gi0 + hx)];
|
|
|
|
gi1 = noise->d[0xff & (gi1 + hx + offx)];
|
|
|
|
gi2 = noise->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 octaveInit(OctaveNoise *noise, uint64_t *seed, PerlinNoise *octaves,
|
|
|
|
int omin, int len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int end = omin+len-1;
|
|
|
|
double persist = 1.0 / ((1LL << len) - 1.0);
|
|
|
|
double lacuna = pow(2.0, end);
|
|
|
|
|
|
|
|
if (len < 1 || end > 0)
|
|
|
|
{
|
|
|
|
printf("octavePerlinInit(): unsupported octave range\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end == 0)
|
|
|
|
{
|
|
|
|
perlinInit(&octaves[0], seed);
|
|
|
|
octaves[0].amplitude = persist;
|
|
|
|
octaves[0].lacunarity = lacuna;
|
|
|
|
persist *= 2.0;
|
|
|
|
lacuna *= 0.5;
|
|
|
|
i = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
skipNextN(seed, -end*262);
|
|
|
|
i = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; i < len; i++)
|
|
|
|
{
|
|
|
|
perlinInit(&octaves[i], seed);
|
|
|
|
octaves[i].amplitude = persist;
|
|
|
|
octaves[i].lacunarity = lacuna;
|
|
|
|
persist *= 2.0;
|
|
|
|
lacuna *= 0.5;
|
|
|
|
}
|
|
|
|
|
|
|
|
noise->octaves = octaves;
|
|
|
|
noise->octcnt = len;
|
|
|
|
}
|
|
|
|
|
Add biome table, new funcs for beta climate maps
(biome_tree.c)
- Add lookup table for pre-B1.8 biomes
- Add getOldBetaBiome function to look up biome from climate noise values
(layers.h)
- Add header for getOldBetaBiome function defined in biome_tree.c
- Fix small error from previous commit (semicolons instead of commas)
(noise.c)
- Add new octaveInitOldBetaBiome function
- Add new sampleOctaveOldBetaBiome function
(noise.h)
- Add headers for two new functions defined in noise.c
This commit adds new functions required to generate Alpha 1.2 - Beta 1.7
climate maps, as well as a lookup table for biomes.
The pre- Beta 1.8 biome system uses three 2D simplex noise maps to determine
biomes -- two 4-octave maps for temperature and humidity, and an additional
2-octave map as a source of high-frequency noise that is applied to the raw
output of both climate maps as they are mapped to the range [-1, 1] before
being used to determine the biome at each coordinate sample.
Beta 1.7.3's NoiseGeneratorOctaves2 class defines the lacunarity and
amplitude of each octave of a map in a way that seems incompatible with the
existing octaveInit function. The lacunarity of each octave is the provided
starting value multiplied by a power of a provided modifier value, starting
at modifier^0 and ending at modifier^(octcnt-1). Amplitude works similarly, but
the starting value and modifier are hardcoded. As far as I can tell, there's no
way to get the correct results using octaveInit, so a new
octaveInitOldBetaBiome is used here to initialize the octaves correctly.
Given that Cubiomes has no pre-existing function for sampling simplex
OctaveNoise, I added sampleOctaveOldBetaBiome. It works the same as
sampleOctave, except that it calls sampleSimplex2D instead of samplePerlin,
and it adds the noisemap's random values 'a' and 'b' onto ax and az
respectively in order to match Minecraft b1.7's simplex sampling algorithm.
2023-01-20 09:02:29 +08:00
|
|
|
void octaveInitOldBetaBiome(OctaveNoise *noise, uint64_t *seed,
|
|
|
|
PerlinNoise *octaves, int octcnt, double lacBase, double lacStretch)
|
|
|
|
{
|
|
|
|
double persist = 1;
|
|
|
|
double lacMult = 1;
|
|
|
|
lacBase /= 1.5;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < octcnt; i++)
|
|
|
|
{
|
|
|
|
perlinInit(&octaves[i], seed);
|
|
|
|
octaves[i].amplitude = 0.55 / persist;
|
|
|
|
octaves[i].lacunarity = lacBase*lacMult;
|
|
|
|
persist /= 2;
|
|
|
|
lacMult *= lacStretch;
|
|
|
|
}
|
|
|
|
|
|
|
|
noise->octaves = octaves;
|
|
|
|
noise->octcnt = octcnt;
|
|
|
|
}
|
|
|
|
|
Added noise generation functions for Beta 1.7 terrain
(biome_tree.c)
- Moved a curly brace to maintain style consistent with repo
(generator.h)
- Removed SurfaceNoiseBeta struct from Generator
(generator.c)
- applySeed(): Removed initSurfaceNoiseBeta() call
- genBiomes():
Now declares and initializes SurfaceNoiseBeta when mc <= MC_B1_7
Returns err when g->dim == DIM_END and mc < MC_1_0
(layers.c)
- biomeExists(): Added new checks for mc <= B1_7
- initSurfaceNoiseBeta(): Implemented
- New comment added to sampleBiomeNoiseBeta()
(noise.h)
- Added headers for four new functions defined in noise.c
(noise.c)
- samplePerlin(): Placed all lerps used for 3D noise but not used for 2D noise
behind a (d2 != 0) condition. I'm anticipating needing every bit of
optimization I can find to make ocean-finding practically efficient.
- added new samplePerlinOldBetaTerrain3D() function:
used to generate the octmin, octmax, and octmain noise near sea level
for the given noise column x and z. The necessity for this function will
be explained more in-depth below.
- added new initOctaveOldBetaTerrain() function:
Used to initialize the five Perlin OctaveNoise structs in
SurfaceNoiseBeta. Unlike climate noise, these are Perlin noise maps
instead of Simplex, but the existing octaveInit() cannot be used because
the starting lacunarity values are not integer powers of 2.
- added new sampleOctave2D() function:
Nearly identical to sampleOctave(), except that ay is set to -(p->b),
forcing samplePerlin()'s d2 variable to be 0, resulting in a 2D noise
sample.
- added new sampleOctaveOldBetaTerrain3D() function:
Used to sample the three 3D noisemaps in SurfaceNoiseBeta. The function
is passed a 2-element array of doubles, which is passed to
samplePerlinOldBetaTerrain3D() where the different octaves' values are
added. The only samples made are the ones necessary to determine the
blocks at sea level.
samplePerlinOldBetaTerrain3D() is necessary because when generating these
versions' overworld octmin, octmax, and octmain noise, lower levels affect
higher levels based on whether the lower 8 bits of i2 are equal to the lower
8 bits of i2 from the previous iteration. Each level is only affected by the
immediately previous level where (i2 & 255 !- previ2), so I was able to make an
optimization -- the first several y-level iterations are split into a separate
for loop that only looks at i2, and finds the highest y-level that affects the
final output at sea level. In my Java test program using Minecraft's code, this
optimization brough the time needed to produce an image of a 4096 x 4096 region
down to 1/3 of what it was before the optimization.
The yLacFlag argument determines whether or not lacunarity is cut in half for
the y dimension -- octmain uses lacunarity values for y that are half of the
x/z lacunarity value.
In a future commit, I may extend samplePerlinOldBetaTerrain3D() and
sampleOctaveOldBetaTerrain3D() to generate partial noise columns between ymin
and ymax bounds, in order to eventually implement 4x4 surface height finding.
2023-01-28 09:17:38 +08:00
|
|
|
void octaveInitOldBetaTerrain(OctaveNoise *noise, uint64_t *seed,
|
|
|
|
PerlinNoise *octaves, int octcnt, double lacBase)
|
|
|
|
{
|
|
|
|
double modifier = 1;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < octcnt; i++)
|
|
|
|
{
|
|
|
|
perlinInit(&octaves[i], seed);
|
|
|
|
octaves[i].amplitude = 1 / modifier;
|
|
|
|
octaves[i].lacunarity = lacBase*modifier;
|
|
|
|
modifier /= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
noise->octaves = octaves;
|
|
|
|
noise->octcnt = octcnt;
|
|
|
|
}
|
|
|
|
|
2021-11-19 06:13:44 +08:00
|
|
|
int xOctaveInit(OctaveNoise *noise, Xoroshiro *xr, PerlinNoise *octaves,
|
|
|
|
const double *amplitudes, int omin, int len)
|
|
|
|
{
|
|
|
|
const uint64_t md5_octave_n[][2] = {
|
|
|
|
{0xb198de63a8012672, 0x7b84cad43ef7b5a8}, // md5 "octave_-12"
|
|
|
|
{0x0fd787bfbc403ec3, 0x74a4a31ca21b48b8}, // md5 "octave_-11"
|
|
|
|
{0x36d326eed40efeb2, 0x5be9ce18223c636a}, // md5 "octave_-10"
|
|
|
|
{0x082fe255f8be6631, 0x4e96119e22dedc81}, // md5 "octave_-9"
|
|
|
|
{0x0ef68ec68504005e, 0x48b6bf93a2789640}, // md5 "octave_-8"
|
|
|
|
{0xf11268128982754f, 0x257a1d670430b0aa}, // md5 "octave_-7"
|
|
|
|
{0xe51c98ce7d1de664, 0x5f9478a733040c45}, // md5 "octave_-6"
|
|
|
|
{0x6d7b49e7e429850a, 0x2e3063c622a24777}, // md5 "octave_-5"
|
|
|
|
{0xbd90d5377ba1b762, 0xc07317d419a7548d}, // md5 "octave_-4"
|
|
|
|
{0x53d39c6752dac858, 0xbcd1c5a80ab65b3e}, // md5 "octave_-3"
|
|
|
|
{0xb4a24d7a84e7677b, 0x023ff9668e89b5c4}, // md5 "octave_-2"
|
|
|
|
{0xdffa22b534c5f608, 0xb9b67517d3665ca9}, // md5 "octave_-1"
|
|
|
|
{0xd50708086cef4d7c, 0x6e1651ecc7f43309}, // md5 "octave_0"
|
|
|
|
};
|
2023-01-08 23:14:08 +08:00
|
|
|
const double lacuna_ini[] = { // -omin = 3..12
|
2022-12-30 20:20:29 +08:00
|
|
|
1, .5, .25, 1./8, 1./16, 1./32, 1./64, 1./128, 1./256, 1./512, 1./1024,
|
2023-01-08 23:14:08 +08:00
|
|
|
1./2048, 1./4096,
|
2022-12-30 20:20:29 +08:00
|
|
|
};
|
|
|
|
const double persist_ini[] = { // len = 4..9
|
|
|
|
0, 1, 2./3, 4./7, 8./15, 16./31, 32./63, 64./127, 128./255, 256./511,
|
|
|
|
};
|
2023-01-08 23:14:08 +08:00
|
|
|
#if DEBUG
|
|
|
|
if (-omin < 0 || -omin >= (int) (sizeof(lacuna_ini)/sizeof(double)) ||
|
|
|
|
len < 0 || len >= (int) (sizeof(persist_ini)/sizeof(double)))
|
|
|
|
{
|
|
|
|
printf("Fatal: octave initialization out of range\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
#endif
|
2022-12-30 20:20:29 +08:00
|
|
|
double lacuna = lacuna_ini[-omin];
|
|
|
|
double persist = persist_ini[len];
|
2021-11-19 06:13:44 +08:00
|
|
|
uint64_t xlo = xNextLong(xr);
|
|
|
|
uint64_t xhi = xNextLong(xr);
|
2022-12-30 20:20:29 +08:00
|
|
|
int i = 0, n = 0;
|
2021-11-19 06:13:44 +08:00
|
|
|
|
|
|
|
for (; i < len; i++, lacuna *= 2.0, persist *= 0.5)
|
|
|
|
{
|
|
|
|
if (amplitudes[i] == 0)
|
|
|
|
continue;
|
|
|
|
Xoroshiro pxr;
|
|
|
|
pxr.lo = xlo ^ md5_octave_n[12 + omin + i][0];
|
|
|
|
pxr.hi = xhi ^ md5_octave_n[12 + omin + i][1];
|
|
|
|
xPerlinInit(&octaves[n], &pxr);
|
|
|
|
octaves[n].amplitude = amplitudes[i] * persist;
|
|
|
|
octaves[n].lacunarity = lacuna;
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
noise->octaves = octaves;
|
|
|
|
noise->octcnt = n;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-30 20:20:29 +08:00
|
|
|
double sampleOctaveAmp(const OctaveNoise *noise, double x, double y, double z,
|
|
|
|
double yamp, double ymin, int ydefault)
|
|
|
|
{
|
|
|
|
double v = 0;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < noise->octcnt; i++)
|
|
|
|
{
|
|
|
|
PerlinNoise *p = noise->octaves + i;
|
|
|
|
double lf = p->lacunarity;
|
|
|
|
double ax = maintainPrecision(x * lf);
|
|
|
|
double ay = ydefault ? -p->b : maintainPrecision(y * lf);
|
|
|
|
double az = maintainPrecision(z * lf);
|
|
|
|
double pv = samplePerlin(p, ax, ay, az, yamp * lf, ymin * lf);
|
|
|
|
v += p->amplitude * pv;
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2021-11-19 06:13:44 +08:00
|
|
|
double sampleOctave(const OctaveNoise *noise, double x, double y, double z)
|
|
|
|
{
|
|
|
|
double v = 0;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < noise->octcnt; i++)
|
|
|
|
{
|
|
|
|
PerlinNoise *p = noise->octaves + i;
|
|
|
|
double lf = p->lacunarity;
|
|
|
|
double ax = maintainPrecision(x * lf);
|
|
|
|
double ay = maintainPrecision(y * lf);
|
|
|
|
double az = maintainPrecision(z * lf);
|
|
|
|
double pv = samplePerlin(p, ax, ay, az, 0, 0);
|
|
|
|
v += p->amplitude * pv;
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
Added noise generation functions for Beta 1.7 terrain
(biome_tree.c)
- Moved a curly brace to maintain style consistent with repo
(generator.h)
- Removed SurfaceNoiseBeta struct from Generator
(generator.c)
- applySeed(): Removed initSurfaceNoiseBeta() call
- genBiomes():
Now declares and initializes SurfaceNoiseBeta when mc <= MC_B1_7
Returns err when g->dim == DIM_END and mc < MC_1_0
(layers.c)
- biomeExists(): Added new checks for mc <= B1_7
- initSurfaceNoiseBeta(): Implemented
- New comment added to sampleBiomeNoiseBeta()
(noise.h)
- Added headers for four new functions defined in noise.c
(noise.c)
- samplePerlin(): Placed all lerps used for 3D noise but not used for 2D noise
behind a (d2 != 0) condition. I'm anticipating needing every bit of
optimization I can find to make ocean-finding practically efficient.
- added new samplePerlinOldBetaTerrain3D() function:
used to generate the octmin, octmax, and octmain noise near sea level
for the given noise column x and z. The necessity for this function will
be explained more in-depth below.
- added new initOctaveOldBetaTerrain() function:
Used to initialize the five Perlin OctaveNoise structs in
SurfaceNoiseBeta. Unlike climate noise, these are Perlin noise maps
instead of Simplex, but the existing octaveInit() cannot be used because
the starting lacunarity values are not integer powers of 2.
- added new sampleOctave2D() function:
Nearly identical to sampleOctave(), except that ay is set to -(p->b),
forcing samplePerlin()'s d2 variable to be 0, resulting in a 2D noise
sample.
- added new sampleOctaveOldBetaTerrain3D() function:
Used to sample the three 3D noisemaps in SurfaceNoiseBeta. The function
is passed a 2-element array of doubles, which is passed to
samplePerlinOldBetaTerrain3D() where the different octaves' values are
added. The only samples made are the ones necessary to determine the
blocks at sea level.
samplePerlinOldBetaTerrain3D() is necessary because when generating these
versions' overworld octmin, octmax, and octmain noise, lower levels affect
higher levels based on whether the lower 8 bits of i2 are equal to the lower
8 bits of i2 from the previous iteration. Each level is only affected by the
immediately previous level where (i2 & 255 !- previ2), so I was able to make an
optimization -- the first several y-level iterations are split into a separate
for loop that only looks at i2, and finds the highest y-level that affects the
final output at sea level. In my Java test program using Minecraft's code, this
optimization brough the time needed to produce an image of a 4096 x 4096 region
down to 1/3 of what it was before the optimization.
The yLacFlag argument determines whether or not lacunarity is cut in half for
the y dimension -- octmain uses lacunarity values for y that are half of the
x/z lacunarity value.
In a future commit, I may extend samplePerlinOldBetaTerrain3D() and
sampleOctaveOldBetaTerrain3D() to generate partial noise columns between ymin
and ymax bounds, in order to eventually implement 4x4 surface height finding.
2023-01-28 09:17:38 +08:00
|
|
|
double sampleOctave2D(const OctaveNoise *noise, double x, double z)
|
|
|
|
{
|
|
|
|
double v = 0;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < noise->octcnt; i++)
|
|
|
|
{
|
|
|
|
PerlinNoise *p = noise->octaves + i;
|
|
|
|
double lf = p->lacunarity;
|
|
|
|
double ax = maintainPrecision(x * lf);
|
|
|
|
double ay = -(p->b);
|
|
|
|
double az = maintainPrecision(z * lf);
|
|
|
|
double pv = samplePerlin(p, ax, ay, az, 0, 0);
|
|
|
|
v += p->amplitude * pv;
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
Add biome table, new funcs for beta climate maps
(biome_tree.c)
- Add lookup table for pre-B1.8 biomes
- Add getOldBetaBiome function to look up biome from climate noise values
(layers.h)
- Add header for getOldBetaBiome function defined in biome_tree.c
- Fix small error from previous commit (semicolons instead of commas)
(noise.c)
- Add new octaveInitOldBetaBiome function
- Add new sampleOctaveOldBetaBiome function
(noise.h)
- Add headers for two new functions defined in noise.c
This commit adds new functions required to generate Alpha 1.2 - Beta 1.7
climate maps, as well as a lookup table for biomes.
The pre- Beta 1.8 biome system uses three 2D simplex noise maps to determine
biomes -- two 4-octave maps for temperature and humidity, and an additional
2-octave map as a source of high-frequency noise that is applied to the raw
output of both climate maps as they are mapped to the range [-1, 1] before
being used to determine the biome at each coordinate sample.
Beta 1.7.3's NoiseGeneratorOctaves2 class defines the lacunarity and
amplitude of each octave of a map in a way that seems incompatible with the
existing octaveInit function. The lacunarity of each octave is the provided
starting value multiplied by a power of a provided modifier value, starting
at modifier^0 and ending at modifier^(octcnt-1). Amplitude works similarly, but
the starting value and modifier are hardcoded. As far as I can tell, there's no
way to get the correct results using octaveInit, so a new
octaveInitOldBetaBiome is used here to initialize the octaves correctly.
Given that Cubiomes has no pre-existing function for sampling simplex
OctaveNoise, I added sampleOctaveOldBetaBiome. It works the same as
sampleOctave, except that it calls sampleSimplex2D instead of samplePerlin,
and it adds the noisemap's random values 'a' and 'b' onto ax and az
respectively in order to match Minecraft b1.7's simplex sampling algorithm.
2023-01-20 09:02:29 +08:00
|
|
|
double sampleOctaveOldBetaBiome(const OctaveNoise *noise, double x, double z)
|
|
|
|
{
|
|
|
|
double v = 0;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < noise->octcnt; i++)
|
|
|
|
{
|
|
|
|
PerlinNoise *p = noise->octaves + i;
|
|
|
|
double lf = p->lacunarity;
|
|
|
|
double ax = maintainPrecision(x * lf) + p->a;
|
|
|
|
double az = maintainPrecision(z * lf) + p->b;
|
|
|
|
double pv = sampleSimplex2D(p, ax, az);
|
|
|
|
v += p->amplitude * pv;
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
Added noise generation functions for Beta 1.7 terrain
(biome_tree.c)
- Moved a curly brace to maintain style consistent with repo
(generator.h)
- Removed SurfaceNoiseBeta struct from Generator
(generator.c)
- applySeed(): Removed initSurfaceNoiseBeta() call
- genBiomes():
Now declares and initializes SurfaceNoiseBeta when mc <= MC_B1_7
Returns err when g->dim == DIM_END and mc < MC_1_0
(layers.c)
- biomeExists(): Added new checks for mc <= B1_7
- initSurfaceNoiseBeta(): Implemented
- New comment added to sampleBiomeNoiseBeta()
(noise.h)
- Added headers for four new functions defined in noise.c
(noise.c)
- samplePerlin(): Placed all lerps used for 3D noise but not used for 2D noise
behind a (d2 != 0) condition. I'm anticipating needing every bit of
optimization I can find to make ocean-finding practically efficient.
- added new samplePerlinOldBetaTerrain3D() function:
used to generate the octmin, octmax, and octmain noise near sea level
for the given noise column x and z. The necessity for this function will
be explained more in-depth below.
- added new initOctaveOldBetaTerrain() function:
Used to initialize the five Perlin OctaveNoise structs in
SurfaceNoiseBeta. Unlike climate noise, these are Perlin noise maps
instead of Simplex, but the existing octaveInit() cannot be used because
the starting lacunarity values are not integer powers of 2.
- added new sampleOctave2D() function:
Nearly identical to sampleOctave(), except that ay is set to -(p->b),
forcing samplePerlin()'s d2 variable to be 0, resulting in a 2D noise
sample.
- added new sampleOctaveOldBetaTerrain3D() function:
Used to sample the three 3D noisemaps in SurfaceNoiseBeta. The function
is passed a 2-element array of doubles, which is passed to
samplePerlinOldBetaTerrain3D() where the different octaves' values are
added. The only samples made are the ones necessary to determine the
blocks at sea level.
samplePerlinOldBetaTerrain3D() is necessary because when generating these
versions' overworld octmin, octmax, and octmain noise, lower levels affect
higher levels based on whether the lower 8 bits of i2 are equal to the lower
8 bits of i2 from the previous iteration. Each level is only affected by the
immediately previous level where (i2 & 255 !- previ2), so I was able to make an
optimization -- the first several y-level iterations are split into a separate
for loop that only looks at i2, and finds the highest y-level that affects the
final output at sea level. In my Java test program using Minecraft's code, this
optimization brough the time needed to produce an image of a 4096 x 4096 region
down to 1/3 of what it was before the optimization.
The yLacFlag argument determines whether or not lacunarity is cut in half for
the y dimension -- octmain uses lacunarity values for y that are half of the
x/z lacunarity value.
In a future commit, I may extend samplePerlinOldBetaTerrain3D() and
sampleOctaveOldBetaTerrain3D() to generate partial noise columns between ymin
and ymax bounds, in order to eventually implement 4x4 surface height finding.
2023-01-28 09:17:38 +08:00
|
|
|
void sampleOctaveOldBetaTerrain3D(const OctaveNoise *noise, double *v, double x, double z,
|
|
|
|
int yLacFlag)
|
|
|
|
{
|
|
|
|
v[0] = 0.0;
|
|
|
|
v[1] = 0.0;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < noise->octcnt; i++)
|
|
|
|
{
|
|
|
|
PerlinNoise *p = noise->octaves + i;
|
|
|
|
double lf = p->lacunarity;
|
|
|
|
double ax = maintainPrecision(x * lf);
|
|
|
|
double az = maintainPrecision(z * lf);
|
|
|
|
samplePerlinOldBetaTerrain3D(p, v, ax, az, yLacFlag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-19 06:13:44 +08:00
|
|
|
|
|
|
|
void doublePerlinInit(DoublePerlinNoise *noise, uint64_t *seed,
|
|
|
|
PerlinNoise *octavesA, PerlinNoise *octavesB, int omin, int len)
|
|
|
|
{ // require: len >= 1 && omin+len <= 0
|
|
|
|
noise->amplitude = (10.0 / 6.0) * len / (len + 1);
|
|
|
|
octaveInit(&noise->octA, seed, octavesA, omin, len);
|
|
|
|
octaveInit(&noise->octB, seed, octavesB, omin, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets up a DoublePerlinNoise generator (MC 1.18).
|
|
|
|
* @noise: Object to be initialized
|
|
|
|
* @xr: Xoroshiro random object
|
|
|
|
* @octaves: Octaves buffer, size has to be 2x (No. non-zeros in amplitudes)
|
|
|
|
* @amplitudes: Octave amplitude, needs at least one non-zero
|
|
|
|
* @omin: First octave
|
|
|
|
* @len: Length of amplitudes array
|
|
|
|
* @return Number of octaves used (see octaves buffer size).
|
|
|
|
*/
|
|
|
|
int xDoublePerlinInit(DoublePerlinNoise *noise, Xoroshiro *xr,
|
|
|
|
PerlinNoise *octaves, const double *amplitudes,
|
|
|
|
int omin, int len)
|
|
|
|
{
|
|
|
|
int i, n = 0;
|
|
|
|
n += xOctaveInit(&noise->octA, xr, octaves+n, amplitudes, omin, len);
|
|
|
|
n += xOctaveInit(&noise->octB, xr, octaves+n, amplitudes, omin, len);
|
|
|
|
|
|
|
|
// trim amplitudes of zero
|
|
|
|
for (i = len-1; i >= 0 && amplitudes[i] == 0.0; i--)
|
|
|
|
len--;
|
|
|
|
for (i = 0; amplitudes[i] == 0.0; i++)
|
|
|
|
len--;
|
|
|
|
noise->amplitude = (10.0 / 6.0) * len / (len + 1);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
double sampleDoublePerlin(const DoublePerlinNoise *noise,
|
|
|
|
double x, double y, double z)
|
|
|
|
{
|
|
|
|
const double f = 337.0 / 331.0;
|
|
|
|
double v = 0;
|
|
|
|
|
|
|
|
v += sampleOctave(&noise->octA, x, y, z);
|
|
|
|
v += sampleOctave(&noise->octB, x*f, y*f, z*f);
|
|
|
|
|
|
|
|
return v * noise->amplitude;
|
|
|
|
}
|
|
|
|
|