2021-11-19 06:13:44 +08:00
|
|
|
|
|
|
|
#include "noise.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
// grad()
|
2023-02-05 01:09:05 +08:00
|
|
|
#if 0
|
2021-11-19 06:13:44 +08:00
|
|
|
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;
|
|
|
|
}
|
2023-02-05 01:09:05 +08:00
|
|
|
#else
|
|
|
|
ATTR(hot, const)
|
2024-01-29 02:34:56 +08:00
|
|
|
static inline double indexedLerp(uint8_t idx, double a, double b, double c)
|
2021-11-19 06:13:44 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2023-02-05 01:09:05 +08:00
|
|
|
#endif
|
2021-11-19 06:13:44 +08:00
|
|
|
|
|
|
|
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;
|
2021-11-19 06:13:44 +08:00
|
|
|
}
|
2024-01-29 02:34:56 +08:00
|
|
|
idx[256] = idx[0];
|
|
|
|
double i2 = floor(noise->b);
|
|
|
|
double d2 = noise->b - i2;
|
|
|
|
noise->h2 = (int) i2;
|
|
|
|
noise->d2 = d2;
|
|
|
|
noise->t2 = d2*d2*d2 * (d2 * (d2*6.0-15.0) + 10.0);
|
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;
|
2021-11-19 06:13:44 +08:00
|
|
|
}
|
2024-01-29 02:34:56 +08:00
|
|
|
idx[256] = idx[0];
|
|
|
|
double i2 = floor(noise->b);
|
|
|
|
double d2 = noise->b - i2;
|
|
|
|
noise->h2 = (int) i2;
|
|
|
|
noise->d2 = d2;
|
|
|
|
noise->t2 = d2*d2*d2 * (d2 * (d2*6.0-15.0) + 10.0);
|
2021-11-19 06:13:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
double samplePerlin(const PerlinNoise *noise, double d1, double d2, double d3,
|
|
|
|
double yamp, double ymin)
|
|
|
|
{
|
2024-01-29 02:34:56 +08:00
|
|
|
uint8_t h1, h2, h3;
|
|
|
|
double t1, t2, t3;
|
|
|
|
|
|
|
|
if (d2 == 0.0)
|
|
|
|
{
|
|
|
|
d2 = noise->d2;
|
|
|
|
h2 = noise->h2;
|
|
|
|
t2 = noise->t2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
d2 += noise->b;
|
|
|
|
double i2 = floor(d2);
|
|
|
|
d2 -= i2;
|
|
|
|
h2 = (int) i2;
|
|
|
|
t2 = d2*d2*d2 * (d2 * (d2*6.0-15.0) + 10.0);
|
|
|
|
}
|
|
|
|
|
2021-11-19 06:13:44 +08:00
|
|
|
d1 += noise->a;
|
|
|
|
d3 += noise->c;
|
2024-01-29 02:34:56 +08:00
|
|
|
|
|
|
|
double i1 = floor(d1);
|
|
|
|
double i3 = floor(d3);
|
2021-11-19 06:13:44 +08:00
|
|
|
d1 -= i1;
|
|
|
|
d3 -= i3;
|
2024-01-29 02:34:56 +08:00
|
|
|
|
|
|
|
h1 = (int) i1;
|
|
|
|
h3 = (int) i3;
|
|
|
|
|
|
|
|
t1 = d1*d1*d1 * (d1 * (d1*6.0-15.0) + 10.0);
|
|
|
|
t3 = d3*d3*d3 * (d3 * (d3*6.0-15.0) + 10.0);
|
2021-11-19 06:13:44 +08:00
|
|
|
|
|
|
|
if (yamp)
|
|
|
|
{
|
|
|
|
double yclamp = ymin < d2 ? ymin : d2;
|
|
|
|
d2 -= floor(yclamp / yamp) * yamp;
|
|
|
|
}
|
|
|
|
|
2024-01-29 02:34:56 +08:00
|
|
|
const uint8_t *idx = noise->d;
|
2021-11-19 06:13:44 +08:00
|
|
|
|
2024-01-29 02:34:56 +08:00
|
|
|
#if 1
|
|
|
|
// try to promote optimizations that can utilize the {xh, xl} registers
|
|
|
|
typedef struct vec2 { uint8_t a, b; } vec2;
|
|
|
|
|
|
|
|
vec2 v1 = { idx[h1], idx[h1+1] };
|
|
|
|
v1.a += h2;
|
|
|
|
v1.b += h2;
|
|
|
|
|
|
|
|
vec2 v2 = { idx[v1.a], idx[v1.a+1] };
|
|
|
|
vec2 v3 = { idx[v1.b], idx[v1.b+1] };
|
|
|
|
v2.a += h3;
|
|
|
|
v2.b += h3;
|
|
|
|
v3.a += h3;
|
|
|
|
v3.b += h3;
|
|
|
|
|
|
|
|
vec2 v4 = { idx[v2.a], idx[v2.a+1] };
|
|
|
|
vec2 v5 = { idx[v2.b], idx[v2.b+1] };
|
|
|
|
vec2 v6 = { idx[v3.a], idx[v3.a+1] };
|
|
|
|
vec2 v7 = { idx[v3.b], idx[v3.b+1] };
|
|
|
|
|
|
|
|
double l1 = indexedLerp(v4.a, d1, d2, d3);
|
|
|
|
double l5 = indexedLerp(v4.b, d1, d2, d3-1);
|
|
|
|
double l2 = indexedLerp(v6.a, d1-1, d2, d3);
|
|
|
|
double l6 = indexedLerp(v6.b, d1-1, d2, d3-1);
|
|
|
|
double l3 = indexedLerp(v5.a, d1, d2-1, d3);
|
|
|
|
double l7 = indexedLerp(v5.b, d1, d2-1, d3-1);
|
|
|
|
double l4 = indexedLerp(v7.a, d1-1, d2-1, d3);
|
|
|
|
double l8 = indexedLerp(v7.b, d1-1, d2-1, d3-1);
|
|
|
|
#else
|
|
|
|
uint8_t a1 = idx[h1] + h2;
|
|
|
|
uint8_t b1 = idx[h1+1] + h2;
|
2023-01-15 21:01:57 +08:00
|
|
|
|
2024-01-29 02:34:56 +08:00
|
|
|
uint8_t a2 = idx[a1] + h3;
|
|
|
|
uint8_t b2 = idx[b1] + h3;
|
|
|
|
uint8_t a3 = idx[a1+1] + h3;
|
|
|
|
uint8_t b3 = idx[b1+1] + h3;
|
2023-01-15 21:01:57 +08:00
|
|
|
|
|
|
|
double l1 = indexedLerp(idx[a2], d1, d2, d3);
|
|
|
|
double l2 = indexedLerp(idx[b2], d1-1, d2, d3);
|
Added ocean detection for Beta 1.7
(generator.c)
- getMinCacheSize(): Allocate extra cache for Beta ocean column noise buffer
(noise.c)
- samplePerlin(): Reverted previous change, as the attempted optimization had
unintended side-effects for 1.18+ generation and had no measurable
performance improvement.
(layers.h)
- Renamed two OctaveNoise members of struct SurfaceNoiseBeta
- Added new struct SeaLevelColumnNoiseBeta
- Added headers for four new functions defined in layers.c
(layers.c)
- initSurfaceNoiseBeta(): Reflect changes to names in layers.h
- sampleBiomeNoiseBeta(): When nptype == 1, returned humidity value is now
multiplied by temperature to reflect the behavior seen in biome gen
- Added new function genColumnNoise():
Generates values used to calculate terrain noise columns and places
them in a SeaLevelColumnNoiseBeta struct pointed to by the provided
pointer.
- Added new function processColumnNoise():
Generates a partial noise column for finding the block at sea level
using data pointed to by the provided SeaLevelColumnNoiseBeta pointer
- Added new function sampleBlocks():
Using adjacent noise columns, determine whether the blocks at y=64 are
solid and place results in array pointed to by provided pointer
- Added new function sampleBetaBiomeOneBlock():
Generates new noise columns for each sampled block and skips the
diagonal traversal with cache buffer, as no noise columns data can be
re-used when scale >= 8.
- genBetaBiomeNoiseScaled():
Previous "no ocean" code is now locked behind (noOcean || scale >= 8)
condition. Calls sampleBiomeNoiseBeta() when noOcean is true, and calls
sampleBetaBiomeOneBlock() otherwise.
Else, the generated region is traversed in 4x4 block sections
corresponding to terrain noise columns. Columns are saved for re-use in
future passes in order to minimize unnecessary Perlin generation, and
region traversal is done in diagonal stripes in order to minimize extra
cache size needed (on average) for the saved noise sample buffer.
My original plan for the diagonal traversal was to store SeaLevelColumnNoise
*pointers* in the extra cache space, which would be dynamically allocated
when generated, but since extra cache space will only exceed the original
cache space when the generated area is so small that memory concerns are
trivial, this plan was abandoned in favor of storing the structs directly in
the cache. This approach also eliminates the risk of memory leaks.
Currently, with oceans, this code takes about 20 seconds to generate the
biomes for a 4096 x 4096 block region. Since this approach to ocean-finding
deals in 4x4 noise columns, scales 2-4 are still quite slow for the same
scaled region -- about 13 seconds at 1:4 scale. At higher scales, the
diagonal approach is abandoned and four new noise columns are generated for
every midpoint block that is sampled.
I made a quick test build of Cubiomes Viewer, and while the ocean-finding
code works, the regions are very slow to show up in the view window, and
changing the zoom level can cause regions to reset when zooming in both
directions. Perhaps some optimizations can be made in Cubiomes Viewer in
terms of handling cache and displaying generated biomes at different scales,
but I'm not familiar enough with that project to suggest anything specific.
I tried to cut out as much unnecessary processing as possible in my
implementation of the inherently cpu-intensive ocean-finding algorithm, but
it's certainly possible optimizations can be made in the generation code
itself.
2023-02-02 08:00:03 +08:00
|
|
|
double l3 = indexedLerp(idx[a3], d1, d2-1, d3);
|
|
|
|
double l4 = indexedLerp(idx[b3], d1-1, d2-1, d3);
|
2023-01-15 21:01:57 +08:00
|
|
|
double l5 = indexedLerp(idx[a2+1], d1, d2, d3-1);
|
|
|
|
double l6 = indexedLerp(idx[b2+1], d1-1, d2, d3-1);
|
Added ocean detection for Beta 1.7
(generator.c)
- getMinCacheSize(): Allocate extra cache for Beta ocean column noise buffer
(noise.c)
- samplePerlin(): Reverted previous change, as the attempted optimization had
unintended side-effects for 1.18+ generation and had no measurable
performance improvement.
(layers.h)
- Renamed two OctaveNoise members of struct SurfaceNoiseBeta
- Added new struct SeaLevelColumnNoiseBeta
- Added headers for four new functions defined in layers.c
(layers.c)
- initSurfaceNoiseBeta(): Reflect changes to names in layers.h
- sampleBiomeNoiseBeta(): When nptype == 1, returned humidity value is now
multiplied by temperature to reflect the behavior seen in biome gen
- Added new function genColumnNoise():
Generates values used to calculate terrain noise columns and places
them in a SeaLevelColumnNoiseBeta struct pointed to by the provided
pointer.
- Added new function processColumnNoise():
Generates a partial noise column for finding the block at sea level
using data pointed to by the provided SeaLevelColumnNoiseBeta pointer
- Added new function sampleBlocks():
Using adjacent noise columns, determine whether the blocks at y=64 are
solid and place results in array pointed to by provided pointer
- Added new function sampleBetaBiomeOneBlock():
Generates new noise columns for each sampled block and skips the
diagonal traversal with cache buffer, as no noise columns data can be
re-used when scale >= 8.
- genBetaBiomeNoiseScaled():
Previous "no ocean" code is now locked behind (noOcean || scale >= 8)
condition. Calls sampleBiomeNoiseBeta() when noOcean is true, and calls
sampleBetaBiomeOneBlock() otherwise.
Else, the generated region is traversed in 4x4 block sections
corresponding to terrain noise columns. Columns are saved for re-use in
future passes in order to minimize unnecessary Perlin generation, and
region traversal is done in diagonal stripes in order to minimize extra
cache size needed (on average) for the saved noise sample buffer.
My original plan for the diagonal traversal was to store SeaLevelColumnNoise
*pointers* in the extra cache space, which would be dynamically allocated
when generated, but since extra cache space will only exceed the original
cache space when the generated area is so small that memory concerns are
trivial, this plan was abandoned in favor of storing the structs directly in
the cache. This approach also eliminates the risk of memory leaks.
Currently, with oceans, this code takes about 20 seconds to generate the
biomes for a 4096 x 4096 block region. Since this approach to ocean-finding
deals in 4x4 noise columns, scales 2-4 are still quite slow for the same
scaled region -- about 13 seconds at 1:4 scale. At higher scales, the
diagonal approach is abandoned and four new noise columns are generated for
every midpoint block that is sampled.
I made a quick test build of Cubiomes Viewer, and while the ocean-finding
code works, the regions are very slow to show up in the view window, and
changing the zoom level can cause regions to reset when zooming in both
directions. Perhaps some optimizations can be made in Cubiomes Viewer in
terms of handling cache and displaying generated biomes at different scales,
but I'm not familiar enough with that project to suggest anything specific.
I tried to cut out as much unnecessary processing as possible in my
implementation of the inherently cpu-intensive ocean-finding algorithm, but
it's certainly possible optimizations can be made in the generation code
itself.
2023-02-02 08:00:03 +08:00
|
|
|
double l7 = indexedLerp(idx[a3+1], d1, d2-1, d3-1);
|
|
|
|
double l8 = indexedLerp(idx[b3+1], d1-1, d2-1, d3-1);
|
2024-01-29 02:34:56 +08:00
|
|
|
#endif
|
2021-11-19 06:13:44 +08:00
|
|
|
|
|
|
|
l1 = lerp(t1, l1, l2);
|
Added ocean detection for Beta 1.7
(generator.c)
- getMinCacheSize(): Allocate extra cache for Beta ocean column noise buffer
(noise.c)
- samplePerlin(): Reverted previous change, as the attempted optimization had
unintended side-effects for 1.18+ generation and had no measurable
performance improvement.
(layers.h)
- Renamed two OctaveNoise members of struct SurfaceNoiseBeta
- Added new struct SeaLevelColumnNoiseBeta
- Added headers for four new functions defined in layers.c
(layers.c)
- initSurfaceNoiseBeta(): Reflect changes to names in layers.h
- sampleBiomeNoiseBeta(): When nptype == 1, returned humidity value is now
multiplied by temperature to reflect the behavior seen in biome gen
- Added new function genColumnNoise():
Generates values used to calculate terrain noise columns and places
them in a SeaLevelColumnNoiseBeta struct pointed to by the provided
pointer.
- Added new function processColumnNoise():
Generates a partial noise column for finding the block at sea level
using data pointed to by the provided SeaLevelColumnNoiseBeta pointer
- Added new function sampleBlocks():
Using adjacent noise columns, determine whether the blocks at y=64 are
solid and place results in array pointed to by provided pointer
- Added new function sampleBetaBiomeOneBlock():
Generates new noise columns for each sampled block and skips the
diagonal traversal with cache buffer, as no noise columns data can be
re-used when scale >= 8.
- genBetaBiomeNoiseScaled():
Previous "no ocean" code is now locked behind (noOcean || scale >= 8)
condition. Calls sampleBiomeNoiseBeta() when noOcean is true, and calls
sampleBetaBiomeOneBlock() otherwise.
Else, the generated region is traversed in 4x4 block sections
corresponding to terrain noise columns. Columns are saved for re-use in
future passes in order to minimize unnecessary Perlin generation, and
region traversal is done in diagonal stripes in order to minimize extra
cache size needed (on average) for the saved noise sample buffer.
My original plan for the diagonal traversal was to store SeaLevelColumnNoise
*pointers* in the extra cache space, which would be dynamically allocated
when generated, but since extra cache space will only exceed the original
cache space when the generated area is so small that memory concerns are
trivial, this plan was abandoned in favor of storing the structs directly in
the cache. This approach also eliminates the risk of memory leaks.
Currently, with oceans, this code takes about 20 seconds to generate the
biomes for a 4096 x 4096 block region. Since this approach to ocean-finding
deals in 4x4 noise columns, scales 2-4 are still quite slow for the same
scaled region -- about 13 seconds at 1:4 scale. At higher scales, the
diagonal approach is abandoned and four new noise columns are generated for
every midpoint block that is sampled.
I made a quick test build of Cubiomes Viewer, and while the ocean-finding
code works, the regions are very slow to show up in the view window, and
changing the zoom level can cause regions to reset when zooming in both
directions. Perhaps some optimizations can be made in Cubiomes Viewer in
terms of handling cache and displaying generated biomes at different scales,
but I'm not familiar enough with that project to suggest anything specific.
I tried to cut out as much unnecessary processing as possible in my
implementation of the inherently cpu-intensive ocean-finding algorithm, but
it's certainly possible optimizations can be made in the generation code
itself.
2023-02-02 08:00:03 +08:00
|
|
|
l3 = lerp(t1, l3, l4);
|
2021-11-19 06:13:44 +08:00
|
|
|
l5 = lerp(t1, l5, l6);
|
Added ocean detection for Beta 1.7
(generator.c)
- getMinCacheSize(): Allocate extra cache for Beta ocean column noise buffer
(noise.c)
- samplePerlin(): Reverted previous change, as the attempted optimization had
unintended side-effects for 1.18+ generation and had no measurable
performance improvement.
(layers.h)
- Renamed two OctaveNoise members of struct SurfaceNoiseBeta
- Added new struct SeaLevelColumnNoiseBeta
- Added headers for four new functions defined in layers.c
(layers.c)
- initSurfaceNoiseBeta(): Reflect changes to names in layers.h
- sampleBiomeNoiseBeta(): When nptype == 1, returned humidity value is now
multiplied by temperature to reflect the behavior seen in biome gen
- Added new function genColumnNoise():
Generates values used to calculate terrain noise columns and places
them in a SeaLevelColumnNoiseBeta struct pointed to by the provided
pointer.
- Added new function processColumnNoise():
Generates a partial noise column for finding the block at sea level
using data pointed to by the provided SeaLevelColumnNoiseBeta pointer
- Added new function sampleBlocks():
Using adjacent noise columns, determine whether the blocks at y=64 are
solid and place results in array pointed to by provided pointer
- Added new function sampleBetaBiomeOneBlock():
Generates new noise columns for each sampled block and skips the
diagonal traversal with cache buffer, as no noise columns data can be
re-used when scale >= 8.
- genBetaBiomeNoiseScaled():
Previous "no ocean" code is now locked behind (noOcean || scale >= 8)
condition. Calls sampleBiomeNoiseBeta() when noOcean is true, and calls
sampleBetaBiomeOneBlock() otherwise.
Else, the generated region is traversed in 4x4 block sections
corresponding to terrain noise columns. Columns are saved for re-use in
future passes in order to minimize unnecessary Perlin generation, and
region traversal is done in diagonal stripes in order to minimize extra
cache size needed (on average) for the saved noise sample buffer.
My original plan for the diagonal traversal was to store SeaLevelColumnNoise
*pointers* in the extra cache space, which would be dynamically allocated
when generated, but since extra cache space will only exceed the original
cache space when the generated area is so small that memory concerns are
trivial, this plan was abandoned in favor of storing the structs directly in
the cache. This approach also eliminates the risk of memory leaks.
Currently, with oceans, this code takes about 20 seconds to generate the
biomes for a 4096 x 4096 block region. Since this approach to ocean-finding
deals in 4x4 noise columns, scales 2-4 are still quite slow for the same
scaled region -- about 13 seconds at 1:4 scale. At higher scales, the
diagonal approach is abandoned and four new noise columns are generated for
every midpoint block that is sampled.
I made a quick test build of Cubiomes Viewer, and while the ocean-finding
code works, the regions are very slow to show up in the view window, and
changing the zoom level can cause regions to reset when zooming in both
directions. Perhaps some optimizations can be made in Cubiomes Viewer in
terms of handling cache and displaying generated biomes at different scales,
but I'm not familiar enough with that project to suggest anything specific.
I tried to cut out as much unnecessary processing as possible in my
implementation of the inherently cpu-intensive ocean-finding algorithm, but
it's certainly possible optimizations can be made in the generation code
itself.
2023-02-02 08:00:03 +08:00
|
|
|
l7 = lerp(t1, l7, l8);
|
|
|
|
|
|
|
|
l1 = lerp(t2, l1, l3);
|
|
|
|
l5 = lerp(t2, l5, l7);
|
2021-11-19 06:13:44 +08:00
|
|
|
|
|
|
|
return lerp(t3, l1, l5);
|
|
|
|
}
|
|
|
|
|
2023-02-08 05:02:46 +08:00
|
|
|
static
|
|
|
|
void samplePerlinBeta17Terrain(const PerlinNoise *noise, double *v,
|
|
|
|
double d1, double d3, double yLacAmp)
|
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
|
|
|
{
|
|
|
|
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;
|
2023-10-12 00:27:10 +08:00
|
|
|
int i2, yi, yic = 0, gfCopy = 0;
|
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
|
|
|
for (yi = 0; yi <= 7; yi++)
|
|
|
|
{
|
2023-02-08 05:02:46 +08:00
|
|
|
d2 = yi*noise->lacunarity*yLacAmp+noise->b;
|
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
|
|
|
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++)
|
|
|
|
{
|
2023-02-08 05:02:46 +08:00
|
|
|
d2 = yi*noise->lacunarity*yLacAmp+noise->b;
|
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
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-02-08 05:02:46 +08:00
|
|
|
void octaveInitBeta(OctaveNoise *noise, uint64_t *seed, PerlinNoise *octaves,
|
|
|
|
int octcnt, double lac, double lacMul, double persist, double persistMul)
|
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
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < octcnt; i++)
|
|
|
|
{
|
|
|
|
perlinInit(&octaves[i], seed);
|
2023-02-08 05:02:46 +08:00
|
|
|
octaves[i].amplitude = persist;
|
|
|
|
octaves[i].lacunarity = lac;
|
|
|
|
persist *= persistMul;
|
|
|
|
lac *= lacMul;
|
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
|
|
|
}
|
|
|
|
noise->octaves = octaves;
|
|
|
|
noise->octcnt = octcnt;
|
|
|
|
}
|
|
|
|
|
2021-11-19 06:13:44 +08:00
|
|
|
int xOctaveInit(OctaveNoise *noise, Xoroshiro *xr, PerlinNoise *octaves,
|
2023-02-05 01:09:05 +08:00
|
|
|
const double *amplitudes, int omin, int len, int nmax)
|
2021-11-19 06:13:44 +08:00
|
|
|
{
|
2023-02-05 01:09:05 +08:00
|
|
|
static const uint64_t md5_octave_n[][2] = {
|
2021-11-19 06:13:44 +08:00
|
|
|
{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-02-05 01:09:05 +08:00
|
|
|
static 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
|
|
|
};
|
2023-02-05 01:09:05 +08:00
|
|
|
static const double persist_ini[] = { // len = 4..9
|
2022-12-30 20:20:29 +08:00
|
|
|
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
|
|
|
|
2023-02-05 01:09:05 +08:00
|
|
|
for (; i < len && n != nmax; i++, lacuna *= 2.0, persist *= 0.5)
|
2021-11-19 06:13:44 +08:00
|
|
|
{
|
|
|
|
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,
|
2023-02-08 05:02:46 +08:00
|
|
|
double yamp, double ymin, int ydefault)
|
2022-12-30 20:20:29 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-02-08 05:02:46 +08:00
|
|
|
double sampleOctaveBeta17Biome(const OctaveNoise *noise, double x, double z)
|
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 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;
|
|
|
|
}
|
|
|
|
|
2023-02-08 05:02:46 +08:00
|
|
|
void sampleOctaveBeta17Terrain(const OctaveNoise *noise, double *v,
|
|
|
|
double x, double z, int yLacFlag, double lacmin)
|
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
|
|
|
{
|
|
|
|
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;
|
2023-02-08 05:02:46 +08:00
|
|
|
if (lacmin && lf > lacmin)
|
|
|
|
continue;
|
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 ax = maintainPrecision(x * lf);
|
|
|
|
double az = maintainPrecision(z * lf);
|
2023-02-08 05:02:46 +08:00
|
|
|
samplePerlinBeta17Terrain(p, v, ax, az, yLacFlag ? 0.5 : 1.0);
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-02-05 01:09:05 +08:00
|
|
|
* Sets up a DoublePerlinNoise generator (MC 1.18+).
|
2021-11-19 06:13:44 +08:00
|
|
|
* @noise: Object to be initialized
|
|
|
|
* @xr: Xoroshiro random object
|
2023-02-05 01:09:05 +08:00
|
|
|
* @octaves: Octaves buffer, size has to be 2x (# non-zeros in amplitudes)
|
2021-11-19 06:13:44 +08:00
|
|
|
* @amplitudes: Octave amplitude, needs at least one non-zero
|
|
|
|
* @omin: First octave
|
|
|
|
* @len: Length of amplitudes array
|
2023-02-05 01:09:05 +08:00
|
|
|
* @nmax: Number of octaves available in buffer (can be <=0 to ignore)
|
2021-11-19 06:13:44 +08:00
|
|
|
* @return Number of octaves used (see octaves buffer size).
|
|
|
|
*/
|
|
|
|
int xDoublePerlinInit(DoublePerlinNoise *noise, Xoroshiro *xr,
|
2023-02-05 01:09:05 +08:00
|
|
|
PerlinNoise *octaves, const double *amplitudes, int omin, int len, int nmax)
|
2021-11-19 06:13:44 +08:00
|
|
|
{
|
2023-02-05 01:09:05 +08:00
|
|
|
int i, n = 0, na = -1, nb = -1;
|
|
|
|
if (nmax > 0)
|
|
|
|
{
|
|
|
|
na = (nmax + 1) >> 1;
|
|
|
|
nb = nmax - na;
|
|
|
|
}
|
|
|
|
n += xOctaveInit(&noise->octA, xr, octaves+n, amplitudes, omin, len, na);
|
|
|
|
n += xOctaveInit(&noise->octB, xr, octaves+n, amplitudes, omin, len, nb);
|
2021-11-19 06:13:44 +08:00
|
|
|
|
|
|
|
// 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--;
|
2023-02-05 01:09:05 +08:00
|
|
|
static const double amp_ini[] = { // (5 ./ 3) * len / (len + 1), len = 2..9
|
|
|
|
0, 5./6, 10./9, 15./12, 20./15, 25./18, 30./21, 35./24, 40./27, 45./30,
|
|
|
|
};
|
|
|
|
noise->amplitude = amp_ini[len];
|
2021-11-19 06:13:44 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|