cubiomes/noise.c

552 lines
15 KiB
C
Raw Normal View History

#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;
}
UNREACHABLE();
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;
uint8_t *idx = noise->d;
for (i = 0; i < 256; i++)
{
idx[i] = i;
}
for (i = 0; i < 256; i++)
{
int j = nextInt(seed, 256 - i) + i;
uint8_t n = idx[i];
idx[i] = idx[j];
idx[j] = n;
idx[i + 256] = idx[i];
}
}
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;
uint8_t *idx = noise->d;
for (i = 0; i < 256; i++)
{
idx[i] = i;
}
for (i = 0; i < 256; i++)
{
int j = xNextInt(xr, 256 - i) + i;
uint8_t n = idx[i];
idx[i] = idx[j];
idx[j] = n;
idx[i + 256] = idx[i];
}
}
double samplePerlin(const PerlinNoise *noise, double d1, double d2, double d3,
double yamp, double ymin)
{
d1 += noise->a;
d2 += noise->b;
d3 += noise->c;
const uint8_t *idx = noise->d;
int i1 = (int) floor(d1);
int i2 = (int) floor(d2);
int i3 = (int) floor(d3);
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;
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);
}
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;
}
}
}
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;
}
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
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,
};
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
double lacuna = lacuna_ini[-omin];
double persist = persist_ini[len];
uint64_t xlo = xNextLong(xr);
uint64_t xhi = xNextLong(xr);
int i = 0, n = 0;
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;
}
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;
}
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);
}
}
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;
}