mirror of
https://github.com/Cubitect/cubiomes.git
synced 2025-01-09 03:57:46 +08:00
Major redesign and 1.18 biome generation.
Different versions, dimensions and variations are now bundled into a single Generator structure. Areas (x,z,w,h) are largely replaced with a Range structure that supports 3D cuboids with scaling. The scale now acts as a generalization that replaces layer access. Direct layer access will have to be version specific, using the LayerStack. (I.e. this update will unfortunately break much of the API.)
This commit is contained in:
parent
cf14767ca3
commit
9166974ccf
2294
biome_tree.c
Normal file
2294
biome_tree.c
Normal file
File diff suppressed because it is too large
Load Diff
724
finders.c
724
finders.c
@ -931,85 +931,80 @@ int scanForQuads(
|
||||
//==============================================================================
|
||||
|
||||
|
||||
int getBiomeAtPos(const LayerStack *g, const Pos pos)
|
||||
Pos locateBiome(
|
||||
const Generator *g, int x, int y, int z, int radius,
|
||||
const char *validBiomes, uint64_t *rng, int *passes)
|
||||
{
|
||||
int *ids = allocCache(g->entry_1, 1, 1);
|
||||
genArea(g->entry_1, ids, pos.x, pos.z, 1, 1);
|
||||
int biomeID = ids[0];
|
||||
free(ids);
|
||||
return biomeID;
|
||||
}
|
||||
|
||||
Pos findBiomePosition(
|
||||
const int mcversion,
|
||||
const Layer *l,
|
||||
int *cache,
|
||||
const int centerX,
|
||||
const int centerZ,
|
||||
const int range,
|
||||
const char *isValid,
|
||||
uint64_t *seed,
|
||||
int *passes
|
||||
)
|
||||
{
|
||||
int x1 = (centerX-range) >> 2;
|
||||
int z1 = (centerZ-range) >> 2;
|
||||
int x2 = (centerX+range) >> 2;
|
||||
int z2 = (centerZ+range) >> 2;
|
||||
int width = x2 - x1 + 1;
|
||||
int height = z2 - z1 + 1;
|
||||
int *ids;
|
||||
Pos out = {x, z};
|
||||
int i, j, found;
|
||||
|
||||
Pos out;
|
||||
|
||||
if (l->scale != 4)
|
||||
{
|
||||
printf("WARN findBiomePosition: require scale = 4, but have %d.\n",
|
||||
l->scale);
|
||||
}
|
||||
|
||||
ids = cache ? cache : allocCache(l, width, height);
|
||||
|
||||
genArea(l, ids, x1, z1, width, height);
|
||||
|
||||
out.x = centerX;
|
||||
out.z = centerZ;
|
||||
found = 0;
|
||||
|
||||
if (mcversion >= MC_1_13)
|
||||
if (g->mc >= MC_1_18)
|
||||
{
|
||||
for (i = 0, j = 2; i < width*height; i++)
|
||||
x >>= 2;
|
||||
z >>= 2;
|
||||
radius >>= 2;
|
||||
|
||||
for (i = -radius; i <= radius; i++)
|
||||
{
|
||||
if (!isValid[ids[i]]) continue;
|
||||
if ((found == 0 || nextInt(seed, j++) == 0))
|
||||
for (j = -radius; j <= radius; j++)
|
||||
{
|
||||
out.x = (x1 + i%width) << 2;
|
||||
out.z = (z1 + i/width) << 2;
|
||||
found = 1;
|
||||
int id = getBiomeAt(g, 4, x+i, y, z+j);
|
||||
if (!validBiomes[id]) continue;
|
||||
if (found == 0 || nextInt(rng, found+1) == 0)
|
||||
{
|
||||
out.x = (x+i) << 2;
|
||||
out.z = (z+j) << 2;
|
||||
found++;
|
||||
}
|
||||
}
|
||||
}
|
||||
found = j - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < width*height; i++)
|
||||
int x1 = (x-radius) >> 2;
|
||||
int z1 = (z-radius) >> 2;
|
||||
int x2 = (x+radius) >> 2;
|
||||
int z2 = (z+radius) >> 2;
|
||||
int width = x2 - x1 + 1;
|
||||
int height = z2 - z1 + 1;
|
||||
|
||||
Range r = {4, x1, z1, width, height, y, 1};
|
||||
int *ids = allocCache(g, r);
|
||||
genBiomes(g, ids, r);
|
||||
|
||||
if (g->mc >= MC_1_13)
|
||||
{
|
||||
if (isValid[ids[i]] && (found == 0 || nextInt(seed, found + 1) == 0))
|
||||
for (i = 0, j = 2; i < width*height; i++)
|
||||
{
|
||||
out.x = (x1 + i%width) << 2;
|
||||
out.z = (z1 + i/width) << 2;
|
||||
++found;
|
||||
if (!validBiomes[ids[i]]) continue;
|
||||
if (found == 0 || nextInt(rng, j++) == 0)
|
||||
{
|
||||
out.x = (x1 + i%width) << 2;
|
||||
out.z = (z1 + i/width) << 2;
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
found = j - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < width*height; i++)
|
||||
{
|
||||
if (!validBiomes[ids[i]]) continue;
|
||||
if (found == 0 || nextInt(rng, found + 1) == 0)
|
||||
{
|
||||
out.x = (x1 + i%width) << 2;
|
||||
out.z = (z1 + i/width) << 2;
|
||||
++found;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (cache == NULL)
|
||||
{
|
||||
free(ids);
|
||||
}
|
||||
|
||||
|
||||
if (passes != NULL)
|
||||
{
|
||||
*passes = found;
|
||||
@ -1019,48 +1014,80 @@ Pos findBiomePosition(
|
||||
}
|
||||
|
||||
|
||||
int areBiomesViable(
|
||||
const Layer * l,
|
||||
int * cache,
|
||||
const int posX,
|
||||
const int posZ,
|
||||
const int radius,
|
||||
const char * isValid
|
||||
)
|
||||
{
|
||||
int x1 = (posX - radius) >> 2;
|
||||
int z1 = (posZ - radius) >> 2;
|
||||
int x2 = (posX + radius) >> 2;
|
||||
int z2 = (posZ + radius) >> 2;
|
||||
int width = x2 - x1 + 1;
|
||||
int height = z2 - z1 + 1;
|
||||
int i;
|
||||
int *ids;
|
||||
int viable;
|
||||
|
||||
if (l->scale != 4)
|
||||
static inline int valid_1x1(const Generator *g, int x, int y, int z,
|
||||
Range r, int *buf, const char *valid)
|
||||
{
|
||||
int *p = buf + (x-r.x) + (z-r.z)*r.sx + (y-r.y)*(r.sx*r.sz);
|
||||
if (*p)
|
||||
return 1;
|
||||
*p = -1;
|
||||
int id = getBiomeAt(g, 4, x, y, z);
|
||||
return valid[id];
|
||||
}
|
||||
|
||||
int areBiomesViable(
|
||||
const Generator *g, int x, int y, int z, int rad,
|
||||
const char *validBiomes, int approx)
|
||||
{
|
||||
int x1 = (x - rad) >> 2, x2 = (x + rad) >> 2, sx = x2 - x1 + 1;
|
||||
int z1 = (z - rad) >> 2, z2 = (z + rad) >> 2, sz = z2 - z1 + 1;
|
||||
int y1, y2, sy;
|
||||
if (g->mc >= MC_1_18)
|
||||
{
|
||||
printf("WARN areBiomesViable: require scale = 4, but have %d.\n",
|
||||
l->scale);
|
||||
y1 = (y - rad) >> 2, y2 = (y + rad) >> 2, sy = y2 - y1 + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
y1 = y2 = 0, sy = 1;
|
||||
}
|
||||
|
||||
ids = cache ? cache : allocCache(l, width, height);
|
||||
viable = !genArea(l, ids, x1, z1, width, height);
|
||||
Range r = {4, x1, z1, sx, sz, y1, sy};
|
||||
int *ids = allocCache(g, r);
|
||||
int i, j, k;
|
||||
int viable = 1;
|
||||
const char *v = validBiomes;
|
||||
|
||||
if (viable)
|
||||
// check corners
|
||||
if (!valid_1x1(g, x1, y1, z1, r, ids, v)) goto L_no;
|
||||
if (!valid_1x1(g, x2, y2, z2, r, ids, v)) goto L_no;
|
||||
if (!valid_1x1(g, x1, y1, z2, r, ids, v)) goto L_no;
|
||||
if (!valid_1x1(g, x2, y2, z1, r, ids, v)) goto L_no;
|
||||
if (g->mc >= MC_1_18)
|
||||
{ // 3D
|
||||
if (!valid_1x1(g, x1, y2, z1, r, ids, v)) goto L_no;
|
||||
if (!valid_1x1(g, x2, y1, z2, r, ids, v)) goto L_no;
|
||||
if (!valid_1x1(g, x1, y2, z2, r, ids, v)) goto L_no;
|
||||
if (!valid_1x1(g, x2, y1, z1, r, ids, v)) goto L_no;
|
||||
}
|
||||
if (approx >= 1) goto L_yes;
|
||||
|
||||
if (g->mc >= MC_1_18)
|
||||
{
|
||||
for (i = 0; i < width*height; i++)
|
||||
for (i = 0; i < sx; i++)
|
||||
{
|
||||
if (!isValid[ ids[i] ])
|
||||
for (j = 0; j < sy; j++)
|
||||
{
|
||||
viable = 0;
|
||||
break;
|
||||
for (k = 0; k < sz; k++)
|
||||
{
|
||||
if (!valid_1x1(g, x1+i, y1+j, z1+k, r, ids, v))
|
||||
goto L_no;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((viable = !genBiomes(g, ids, r)))
|
||||
{
|
||||
for (i = 0; i < sx*sy*sz; i++)
|
||||
{
|
||||
if (!v[ids[i]])
|
||||
goto L_no;
|
||||
}
|
||||
}
|
||||
|
||||
if (cache == NULL)
|
||||
free(ids);
|
||||
if (0) L_yes: viable = 1;
|
||||
if (0) L_no: viable = 0;
|
||||
free(ids);
|
||||
return viable;
|
||||
}
|
||||
|
||||
@ -1145,10 +1172,9 @@ Pos initFirstStronghold(StrongholdIter *sh, int mc, uint64_t s48)
|
||||
return p;
|
||||
}
|
||||
|
||||
int nextStronghold(StrongholdIter *sh, const LayerStack *g, int *cache)
|
||||
int nextStronghold(StrongholdIter *sh, const Generator *g)
|
||||
{
|
||||
sh->pos = findBiomePosition(sh->mc, &g->layers[L_RIVER_MIX_4], cache,
|
||||
sh->nextapprox.x, sh->nextapprox.z, 112,
|
||||
sh->pos = locateBiome(g, sh->nextapprox.x, 0, sh->nextapprox.z, 112,
|
||||
getValidStrongholdBiomes(sh->mc), &sh->rnds, NULL);
|
||||
|
||||
sh->ringidx++;
|
||||
@ -1181,82 +1207,6 @@ int nextStronghold(StrongholdIter *sh, const LayerStack *g, int *cache)
|
||||
return (sh->mc >= MC_1_9 ? 128 : 3) - (sh->index-1);
|
||||
}
|
||||
|
||||
int findStrongholds(const int mc, const LayerStack *g, int *cache,
|
||||
Pos *locations, uint64_t worldSeed, int maxSH, int maxRing)
|
||||
{
|
||||
const char *validStrongholdBiomes = getValidStrongholdBiomes(mc);
|
||||
int i, x, z;
|
||||
double distance;
|
||||
|
||||
int currentRing = 0;
|
||||
int currentCount = 0;
|
||||
int perRing = 3;
|
||||
uint64_t rnd;
|
||||
|
||||
setSeed(&rnd, worldSeed);
|
||||
double angle = nextDouble(&rnd) * PI * 2.0;
|
||||
|
||||
const Layer *l = &g->layers[L_RIVER_MIX_4];
|
||||
|
||||
if (mc >= MC_1_9)
|
||||
{
|
||||
if (maxSH <= 0) maxSH = 128;
|
||||
|
||||
for (i = 0; i < maxSH; i++)
|
||||
{
|
||||
distance = (4.0 * 32.0) + (6.0 * currentRing * 32.0) +
|
||||
(nextDouble(&rnd) - 0.5) * 32 * 2.5;
|
||||
|
||||
x = (int)round(cos(angle) * distance);
|
||||
z = (int)round(sin(angle) * distance);
|
||||
|
||||
locations[i] = findBiomePosition(mc, l, cache,
|
||||
(x << 4) + 8, (z << 4) + 8, 112, validStrongholdBiomes,
|
||||
&rnd, NULL);
|
||||
|
||||
angle += 2 * PI / perRing;
|
||||
|
||||
currentCount++;
|
||||
if (currentCount == perRing)
|
||||
{
|
||||
// Current ring is complete, move to next ring.
|
||||
currentRing++;
|
||||
if (currentRing == maxRing)
|
||||
{
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
|
||||
currentCount = 0;
|
||||
perRing = perRing + 2*perRing/(currentRing+1);
|
||||
if (perRing > 128-i)
|
||||
perRing = 128-i;
|
||||
angle = angle + nextDouble(&rnd) * PI * 2.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (maxSH <= 0) maxSH = 3;
|
||||
|
||||
for (i = 0; i < maxSH; i++)
|
||||
{
|
||||
distance = (1.25 + nextDouble(&rnd)) * 32.0;
|
||||
|
||||
x = (int)round(cos(angle) * distance);
|
||||
z = (int)round(sin(angle) * distance);
|
||||
|
||||
locations[i] = findBiomePosition(mc, l, cache,
|
||||
(x << 4) + 8, (z << 4) + 8, 112, validStrongholdBiomes,
|
||||
&rnd, NULL);
|
||||
|
||||
angle += 2 * PI / 3.0;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static double getGrassProbability(uint64_t seed, int biome, int x, int z)
|
||||
{
|
||||
@ -1320,7 +1270,9 @@ static double getGrassProbability(uint64_t seed, int biome, int x, int z)
|
||||
|
||||
static const char* getValidSpawnBiomes()
|
||||
{
|
||||
static const int biomesToSpawnIn[] = {forest, plains, taiga, taiga_hills, wooded_hills, jungle, jungle_hills};
|
||||
static const int biomesToSpawnIn[] = {
|
||||
forest, plains, taiga, taiga_hills, wooded_hills, jungle, jungle_hills
|
||||
};
|
||||
static char isValid[256];
|
||||
unsigned int i;
|
||||
|
||||
@ -1332,19 +1284,75 @@ static const char* getValidSpawnBiomes()
|
||||
}
|
||||
|
||||
|
||||
Pos getSpawn(const int mcversion, const LayerStack *g, int *cache, uint64_t worldSeed)
|
||||
static int findServerSpawn(const Generator *g, int chunkX, int chunkZ,
|
||||
double *bx, double *bz, double *bn, double *accum)
|
||||
{
|
||||
int x, z;
|
||||
|
||||
if (g->mc >= MC_1_18)
|
||||
{
|
||||
// It seems the search for spawn in 1.18 looks for a block with a
|
||||
// solid top and a height above sea level. We can approximate this by
|
||||
// looking for a non-ocean biome at y=63 ~> y=16 at scale 1:4.
|
||||
for (x = 0; x < 4; x++)
|
||||
{
|
||||
for (z = 0; z < 4; z++)
|
||||
{
|
||||
int x4 = (chunkX << 2) + x, z4 = (chunkZ << 2) + z;
|
||||
int id = getBiomeAt(g, 4, x4, 16, z4);
|
||||
if (isOceanic(id) || id == river)
|
||||
continue;
|
||||
*bx = x4 << 2;
|
||||
*bz = z4 << 2;
|
||||
*bn = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Range r = {1, chunkX << 4, chunkZ << 4, 16, 16, 0, 1};
|
||||
int *area = allocCache(g, r);
|
||||
genBiomes(g, area, r);
|
||||
|
||||
for (x = 0; x < 16; x++)
|
||||
{
|
||||
for (z = 0; z < 16; z++)
|
||||
{
|
||||
Pos pos = {r.x+x, r.z+z};
|
||||
int id = area[z*16 + x];
|
||||
double gp = getGrassProbability(g->mc, id, pos.x, pos.z);
|
||||
if (gp == 0)
|
||||
continue;
|
||||
|
||||
*bx += *accum * gp * pos.x;
|
||||
*bz += *accum * gp * pos.z;
|
||||
*bn += *accum * gp;
|
||||
|
||||
*accum *= 1 - gp;
|
||||
if (*accum < 0.001)
|
||||
{
|
||||
free(area);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(area);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Pos getSpawn(const Generator *g)
|
||||
{
|
||||
const char *isSpawnBiome = getValidSpawnBiomes();
|
||||
Pos spawn;
|
||||
int found;
|
||||
int i;
|
||||
|
||||
const Layer *l = &g->layers[L_RIVER_MIX_4];
|
||||
uint64_t rnd;
|
||||
|
||||
setSeed(&rnd, worldSeed);
|
||||
spawn = findBiomePosition(mcversion, l, cache, 0, 0, 256, isSpawnBiome,
|
||||
&rnd, &found);
|
||||
setSeed(&rnd, g->seed);
|
||||
spawn = locateBiome(g, 0, 63, 0, 256, isSpawnBiome, &rnd, &found);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
@ -1356,70 +1364,41 @@ Pos getSpawn(const int mcversion, const LayerStack *g, int *cache, uint64_t worl
|
||||
double bx = 0;
|
||||
double bz = 0;
|
||||
double bn = 0;
|
||||
double gp;
|
||||
|
||||
if (mcversion >= MC_1_13)
|
||||
if (g->mc >= MC_1_13)
|
||||
{
|
||||
int *area = allocCache(g->entry_1, 16, 16);
|
||||
int n2 = 0;
|
||||
int n3 = 0;
|
||||
int n4 = 0;
|
||||
int n5 = -1;
|
||||
|
||||
int j, k, u, v;
|
||||
j = k = u = v = 0;
|
||||
for (i = 0; i < 1024; i++)
|
||||
{
|
||||
if (n2 > -16 && n2 <= 16 && n3 > -16 && n3 <= 16)
|
||||
if (j > -16 && j <= 16 && k > -16 && k <= 16)
|
||||
{
|
||||
int cx = ((spawn.x >> 4) + n2) << 4;
|
||||
int cz = ((spawn.z >> 4) + n3) << 4;
|
||||
int x, z;
|
||||
|
||||
genArea(g->entry_1, area, cx, cz, 16, 16);
|
||||
|
||||
for (x = 0; x < 16; x++)
|
||||
if (findServerSpawn(g, (spawn.x>>4)+j, (spawn.x>>4)+k,
|
||||
&bx, &bz, &bn, &accum))
|
||||
{
|
||||
for (z = 0; z < 16; z++)
|
||||
{
|
||||
Pos pos = {cx+x, cz+z};
|
||||
int biome = area[z*16 + x];
|
||||
double gp = getGrassProbability(worldSeed, biome,
|
||||
pos.x, pos.z);
|
||||
if (gp == 0)
|
||||
continue;
|
||||
|
||||
bx += accum * gp * pos.x;
|
||||
bz += accum * gp * pos.z;
|
||||
bn += accum * gp;
|
||||
|
||||
accum *= 1 - gp;
|
||||
if (accum < 0.001)
|
||||
{
|
||||
free(area);
|
||||
spawn.x = (int) round(bx / bn);
|
||||
spawn.z = (int) round(bz / bn);
|
||||
return spawn;
|
||||
}
|
||||
}
|
||||
spawn.x = (int) round(bx / bn);
|
||||
spawn.z = (int) round(bz / bn);
|
||||
return spawn;
|
||||
}
|
||||
}
|
||||
|
||||
if (n2 == n3 || (n2 < 0 && n2 == - n3) || (n2 > 0 && n2 == 1 - n3))
|
||||
if (j == k || (j < 0 && j == -k) || (j > 0 && j == 1 - k))
|
||||
{
|
||||
int n7 = n4;
|
||||
n4 = -n5;
|
||||
n5 = n7;
|
||||
int tmp = u;
|
||||
u = -v;
|
||||
v = tmp;
|
||||
}
|
||||
n2 += n4;
|
||||
n3 += n5;
|
||||
j += u;
|
||||
k += v;
|
||||
}
|
||||
|
||||
free(area);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < 1000; i++)
|
||||
{
|
||||
int biome = getBiomeAtPos(g, spawn);
|
||||
double gp = getGrassProbability(worldSeed, biome, spawn.x, spawn.z);
|
||||
int biome = getBiomeAt(g, 1, spawn.x, 0, spawn.z);
|
||||
gp = getGrassProbability(g->seed, biome, spawn.x, spawn.z);
|
||||
|
||||
bx += accum * gp * spawn.x;
|
||||
bz += accum * gp * spawn.z;
|
||||
@ -1442,24 +1421,21 @@ Pos getSpawn(const int mcversion, const LayerStack *g, int *cache, uint64_t worl
|
||||
}
|
||||
|
||||
|
||||
Pos estimateSpawn(const int mcversion, const LayerStack *g, int *cache, uint64_t worldSeed)
|
||||
Pos estimateSpawn(const Generator *g)
|
||||
{
|
||||
const char *isSpawnBiome = getValidSpawnBiomes();
|
||||
Pos spawn;
|
||||
int found;
|
||||
|
||||
const Layer *l = &g->layers[L_RIVER_MIX_4];
|
||||
uint64_t rnd;
|
||||
setSeed(&rnd, worldSeed);
|
||||
spawn = findBiomePosition(mcversion, l, cache, 0, 0, 256, isSpawnBiome,
|
||||
&rnd, &found);
|
||||
setSeed(&rnd, g->seed);
|
||||
spawn = locateBiome(g, 0, 63, 0, 256, isSpawnBiome, &rnd, &found);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
spawn.x = spawn.z = 8;
|
||||
}
|
||||
|
||||
if (mcversion >= MC_1_13)
|
||||
if (g->mc >= MC_1_13)
|
||||
{
|
||||
spawn.x &= ~0xf;
|
||||
spawn.z &= ~0xf;
|
||||
@ -1704,123 +1680,157 @@ static int mapViableShore(const Layer * l, int * out, int x, int z, int w, int h
|
||||
}
|
||||
|
||||
|
||||
int isViableStructurePos(int structureType, int mc, LayerStack *g,
|
||||
uint64_t seed, int blockX, int blockZ)
|
||||
int isViableStructurePos(int structureType, Generator *g, int x, int z)
|
||||
{
|
||||
int *ids = NULL;
|
||||
Layer *l;
|
||||
int approx = 0; // enables approximation levels
|
||||
int viable = 0;
|
||||
|
||||
int64_t chunkX = blockX >> 4;
|
||||
int64_t chunkZ = blockZ >> 4;
|
||||
int64_t chunkX = x >> 4;
|
||||
int64_t chunkZ = z >> 4;
|
||||
|
||||
// Structures are positioned at the chunk origin, but the biome check is
|
||||
// performed near the middle of the chunk [(9,9) in 1.13, TODO: check 1.7]
|
||||
// In 1.16 the biome check is always performed at (2,2) with layer scale=4.
|
||||
int biomeX, biomeZ;
|
||||
int sampleX, sampleZ;
|
||||
int id;
|
||||
|
||||
Layer lbiome = g->layers[L_BIOME_256];
|
||||
Layer lshore = g->layers[L_SHORE_16];
|
||||
|
||||
int data[2] = { structureType, mc };
|
||||
if (g->dim == -1) // Nether
|
||||
{
|
||||
if (structureType == Fortress)
|
||||
return 1; // fortresses generate in all Nether biomes and versions
|
||||
if (g->mc < MC_1_16)
|
||||
return 0;
|
||||
if (structureType == Ruined_Portal_N)
|
||||
return 1;
|
||||
|
||||
g->layers[L_BIOME_256].data = (void*) data;
|
||||
g->layers[L_BIOME_256].getMap = mapViableBiome;
|
||||
g->layers[L_SHORE_16].data = (void*) data;
|
||||
g->layers[L_SHORE_16].getMap = mapViableShore;
|
||||
sampleX = (chunkX << 2) + 2;
|
||||
sampleZ = (chunkZ << 2) + 2;
|
||||
id = getBiomeAt(g, 4, sampleX, 0, sampleZ);
|
||||
return isViableFeatureBiome(g->mc, structureType, id);
|
||||
}
|
||||
else if (g->dim == +1) // End
|
||||
{
|
||||
switch (structureType)
|
||||
{
|
||||
case End_City:
|
||||
if (g->mc < MC_1_9) return 0;
|
||||
break;
|
||||
case End_Gateway:
|
||||
if (g->mc < MC_1_13) return 0;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
// End biomes vary only on a per-chunk scale (1:16)
|
||||
// voronoi pre-1.15 shouldn't matter for End Cities as the check will
|
||||
// be near the chunk center
|
||||
id = getBiomeAt(g, 16, chunkX, 0, chunkZ);
|
||||
return isViableFeatureBiome(g->mc, structureType, id) ? id : 0;
|
||||
}
|
||||
|
||||
// Overworld
|
||||
|
||||
Layer lbiome, lshore, *entry = 0;
|
||||
int data[2] = { structureType, g->mc };
|
||||
|
||||
if (g->mc <= MC_1_17)
|
||||
{
|
||||
lbiome = g->ls.layers[L_BIOME_256];
|
||||
lshore = g->ls.layers[L_SHORE_16];
|
||||
entry = g->entry;
|
||||
|
||||
g->ls.layers[L_BIOME_256].data = (void*) data;
|
||||
g->ls.layers[L_BIOME_256].getMap = mapViableBiome;
|
||||
g->ls.layers[L_SHORE_16].data = (void*) data;
|
||||
g->ls.layers[L_SHORE_16].getMap = mapViableShore;
|
||||
}
|
||||
|
||||
switch (structureType)
|
||||
{
|
||||
case Ocean_Ruin:
|
||||
case Shipwreck:
|
||||
case Treasure:
|
||||
if (mc < MC_1_13) goto L_not_viable;
|
||||
if (g->mc < MC_1_13) goto L_not_viable;
|
||||
goto L_feature;
|
||||
case Igloo:
|
||||
if (mc < MC_1_9) goto L_not_viable;
|
||||
if (g->mc < MC_1_9) goto L_not_viable;
|
||||
goto L_feature;
|
||||
case Desert_Pyramid:
|
||||
case Jungle_Pyramid:
|
||||
case Swamp_Hut:
|
||||
L_feature:
|
||||
if (mc < MC_1_16)
|
||||
if (g->mc >= MC_1_16)
|
||||
{
|
||||
l = &g->layers[L_VORONOI_1];
|
||||
biomeX = (chunkX << 4) + 9;
|
||||
biomeZ = (chunkZ << 4) + 9;
|
||||
if (g->mc < MC_1_18)
|
||||
g->entry = &g->ls.layers[L_RIVER_MIX_4];
|
||||
sampleX = (chunkX << 2) + 2;
|
||||
sampleZ = (chunkZ << 2) + 2;
|
||||
}
|
||||
else
|
||||
{ // NOTE: L_RIVER_MIX_4 skips the ocean types, should be fine for
|
||||
// ocean ruins and ship wrecks.
|
||||
l = &g->layers[L_RIVER_MIX_4];
|
||||
biomeX = (chunkX << 2) + 2;
|
||||
biomeZ = (chunkZ << 2) + 2;
|
||||
{
|
||||
g->entry = &g->ls.layers[L_VORONOI_1];
|
||||
sampleX = (chunkX << 4) + 9;
|
||||
sampleZ = (chunkZ << 4) + 9;
|
||||
}
|
||||
setLayerSeed(l, seed);
|
||||
ids = allocCache(l, 1, 1);
|
||||
if (genArea(l, ids, biomeX, biomeZ, 1, 1))
|
||||
goto L_not_viable;
|
||||
if (!isViableFeatureBiome(mc, structureType, ids[0]))
|
||||
id = getBiomeAt(g, 0, sampleX, 0, sampleZ);
|
||||
if (id < 0 || !isViableFeatureBiome(g->mc, structureType, id))
|
||||
goto L_not_viable;
|
||||
goto L_viable;
|
||||
|
||||
case Village:
|
||||
l = &g->layers[L_RIVER_MIX_4];
|
||||
biomeX = (chunkX << 2) + 2;
|
||||
biomeZ = (chunkZ << 2) + 2;
|
||||
setLayerSeed(l, seed);
|
||||
ids = allocCache(l, 1, 1);
|
||||
if (genArea(l, ids, biomeX, biomeZ, 1, 1))
|
||||
if (g->mc <= MC_1_17)
|
||||
g->entry = &g->ls.layers[L_RIVER_MIX_4];
|
||||
sampleX = (chunkX << 2) + 2;
|
||||
sampleZ = (chunkZ << 2) + 2;
|
||||
id = getBiomeAt(g, 0, sampleX, 0, sampleZ);
|
||||
if (id < 0 || !isViableFeatureBiome(g->mc, structureType, id))
|
||||
goto L_not_viable;
|
||||
if (!isViableFeatureBiome(mc, structureType, ids[0]))
|
||||
goto L_not_viable;
|
||||
viable = ids[0]; // biome for viablility value as it may be useful for further analysis
|
||||
viable = id; // use biome for viablility, useful for further analysis
|
||||
goto L_viable;
|
||||
|
||||
case Outpost:
|
||||
{
|
||||
if (mc < MC_1_14)
|
||||
if (g->mc < MC_1_14)
|
||||
goto L_not_viable;
|
||||
uint64_t rnd = seed;
|
||||
setAttemptSeed(&rnd, chunkX, chunkZ);
|
||||
if (nextInt(&rnd, 5) != 0)
|
||||
uint64_t rng = g->seed;
|
||||
setAttemptSeed(&rng, chunkX, chunkZ);
|
||||
if (nextInt(&rng, 5) != 0)
|
||||
goto L_not_viable;
|
||||
if (mc < MC_1_16)
|
||||
if (g->mc >= MC_1_16)
|
||||
{
|
||||
l = &g->layers[L_VORONOI_1];
|
||||
biomeX = (chunkX << 4) + 9;
|
||||
biomeZ = (chunkZ << 4) + 9;
|
||||
if (g->mc < MC_1_18)
|
||||
g->entry = &g->ls.layers[L_RIVER_MIX_4];
|
||||
sampleX = (chunkX << 2) + 2;
|
||||
sampleZ = (chunkZ << 2) + 2;
|
||||
}
|
||||
else
|
||||
{ // NOTE: this skips the ocean type check
|
||||
l = &g->layers[L_RIVER_MIX_4];
|
||||
biomeX = (chunkX << 2) + 2;
|
||||
biomeZ = (chunkZ << 2) + 2;
|
||||
{
|
||||
g->entry = &g->ls.layers[L_VORONOI_1];
|
||||
sampleX = (chunkX << 4) + 9;
|
||||
sampleZ = (chunkZ << 4) + 9;
|
||||
}
|
||||
setLayerSeed(l, seed);
|
||||
ids = allocCache(l, 1, 1);
|
||||
if (genArea(l, ids, biomeX, biomeZ, 1, 1))
|
||||
goto L_not_viable;
|
||||
if (!isViableFeatureBiome(mc, structureType, ids[0]))
|
||||
id = getBiomeAt(g, 0, sampleX, 0, sampleZ);
|
||||
if (id < 0 || !isViableFeatureBiome(g->mc, structureType, id))
|
||||
goto L_not_viable;
|
||||
// look for villages within 10 chunks
|
||||
int cx0 = (chunkX-10), cx1 = (chunkX+10);
|
||||
int cz0 = (chunkZ-10), cz1 = (chunkZ+10);
|
||||
int rx, rz;
|
||||
StructureConfig vilconf;
|
||||
if (!getStructureConfig(Village, mc, &vilconf))
|
||||
if (!getStructureConfig(Village, g->mc, &vilconf))
|
||||
goto L_not_viable;
|
||||
for (rz = cz0 >> 5; rz <= cz1 >> 5; rz++)
|
||||
{
|
||||
for (rx = cx0 >> 5; rx <= cx1 >> 5; rx++)
|
||||
{
|
||||
Pos p = getFeaturePos(vilconf, seed, rx, rz);
|
||||
Pos p = getFeaturePos(vilconf, g->seed, rx, rz);
|
||||
int cx = p.x >> 4, cz = p.z >> 4;
|
||||
if (cx >= cx0 && cx <= cx1 && cz >= cz0 && cz <= cz1)
|
||||
{
|
||||
if (mc >= MC_1_16)
|
||||
if (g->mc >= MC_1_16)
|
||||
goto L_not_viable;
|
||||
if (isViableStructurePos(Village, mc, g, seed, p.x, p.z))
|
||||
if (isViableStructurePos(Village, g, p.x, p.z))
|
||||
goto L_not_viable;
|
||||
goto L_viable;
|
||||
}
|
||||
@ -1830,53 +1840,58 @@ L_feature:
|
||||
}
|
||||
|
||||
case Monument:
|
||||
if (mc < MC_1_8)
|
||||
if (g->mc < MC_1_8)
|
||||
goto L_not_viable;
|
||||
else if (mc == MC_1_8)
|
||||
else if (g->mc == MC_1_8)
|
||||
{ // In 1.8 monuments require only a single deep ocean block.
|
||||
l = g->entry_1;
|
||||
setLayerSeed(l, seed);
|
||||
ids = allocCache(l, 1, 1);
|
||||
if (genArea(l, ids, (chunkX << 4) + 8, (chunkZ << 4) + 8, 1, 1))
|
||||
id = getBiomeAt(g, 1, (chunkX << 4) + 8, 0, (chunkZ << 4) + 8);
|
||||
if (id < 0 || !isDeepOcean(id))
|
||||
goto L_not_viable;
|
||||
}
|
||||
else
|
||||
else if (g->mc <= MC_1_17)
|
||||
{ // Monuments require two viability checks with the ocean layer
|
||||
// branch => worth checking for potential deep ocean beforehand.
|
||||
l = &g->layers[L_SHORE_16];
|
||||
setLayerSeed(l, seed);
|
||||
ids = allocCache(l, 1, 1);
|
||||
if (genArea(l, ids, chunkX, chunkZ, 1, 1))
|
||||
g->entry = &g->ls.layers[L_SHORE_16];
|
||||
id = getBiomeAt(g, 0, chunkX, 0, chunkZ);
|
||||
if (id < 0 || !isDeepOcean(id))
|
||||
goto L_not_viable;
|
||||
}
|
||||
if (!isDeepOcean(ids[0]))
|
||||
goto L_not_viable;
|
||||
if (mc >= MC_1_13)
|
||||
l = &g->layers[L_OCEAN_MIX_4];
|
||||
else
|
||||
l = &g->layers[L_RIVER_MIX_4];
|
||||
biomeX = (chunkX << 4) + 8; // areBiomesViable expects block positions
|
||||
biomeZ = (chunkZ << 4) + 8;
|
||||
setLayerSeed(l, seed);
|
||||
if (mc < MC_1_9 || areBiomesViable(l, NULL, biomeX, biomeZ, 16, getValidMonumentBiomes2()))
|
||||
if (areBiomesViable(l, NULL, biomeX, biomeZ, 29, getValidMonumentBiomes1()))
|
||||
goto L_viable;
|
||||
goto L_not_viable;
|
||||
|
||||
case Mansion:
|
||||
if (mc < MC_1_11)
|
||||
goto L_not_viable;
|
||||
l = &g->layers[L_RIVER_MIX_4];
|
||||
biomeX = (chunkX << 4) + 8;
|
||||
biomeZ = (chunkZ << 4) + 8;
|
||||
setLayerSeed(l, seed);
|
||||
if (areBiomesViable(l, NULL, biomeX, biomeZ, 32, getValidMansionBiomes()))
|
||||
sampleX = (chunkX << 4) + 8;
|
||||
sampleZ = (chunkZ << 4) + 8;
|
||||
if (g->mc >= MC_1_9 && g->mc <= MC_1_17)
|
||||
{ // check for deep ocean center
|
||||
if (!areBiomesViable(g, sampleX, 63, sampleZ, 16, getValidMonumentBiomes2(), approx))
|
||||
goto L_not_viable;
|
||||
}
|
||||
if (areBiomesViable(g, sampleX, 63, sampleZ, 29, getValidMonumentBiomes1(), approx))
|
||||
goto L_viable;
|
||||
goto L_not_viable;
|
||||
|
||||
case Mansion:
|
||||
if (g->mc < MC_1_11)
|
||||
goto L_not_viable;
|
||||
sampleX = (chunkX << 4) + 8;
|
||||
sampleZ = (chunkZ << 4) + 8;
|
||||
if (g->mc <= MC_1_17)
|
||||
{
|
||||
if (!areBiomesViable(g, sampleX, 0, sampleZ, 32, getValidMansionBiomes(), approx))
|
||||
goto L_not_viable;
|
||||
}
|
||||
else
|
||||
{ // In 1.18 the generation gets the minimum surface height among the
|
||||
// four structure corners (note structure has rotation).
|
||||
// This minimum height has to be y >= 60. The biome check is done
|
||||
// at the center position at that height.
|
||||
// TODO: get surface height
|
||||
id = getBiomeAt(g, 4, sampleX>>2, 60>>2, sampleZ>>2);
|
||||
if (id < 0 || !isViableFeatureBiome(g->mc, structureType, id))
|
||||
goto L_not_viable;
|
||||
}
|
||||
goto L_viable;
|
||||
|
||||
case Ruined_Portal:
|
||||
case Ruined_Portal_N:
|
||||
if (mc >= MC_1_16)
|
||||
if (g->mc >= MC_1_16)
|
||||
goto L_viable;
|
||||
goto L_not_viable;
|
||||
|
||||
@ -1894,58 +1909,15 @@ L_viable:
|
||||
if (!viable)
|
||||
viable = 1;
|
||||
L_not_viable:
|
||||
|
||||
g->layers[L_BIOME_256] = lbiome;
|
||||
g->layers[L_SHORE_16] = lshore;
|
||||
if (ids)
|
||||
free(ids);
|
||||
|
||||
if (g->mc <= MC_1_17)
|
||||
{
|
||||
g->ls.layers[L_BIOME_256] = lbiome;
|
||||
g->ls.layers[L_SHORE_16] = lshore;
|
||||
g->entry = entry;
|
||||
}
|
||||
return viable;
|
||||
}
|
||||
|
||||
int isViableNetherStructurePos(int structureType, int mc, NetherNoise *nn,
|
||||
uint64_t seed, int blockX, int blockZ)
|
||||
{
|
||||
if (structureType == Fortress)
|
||||
return 1; // fortresses generate in all nether biomes and mc versions
|
||||
if (mc < MC_1_16)
|
||||
return 0;
|
||||
if (structureType == Ruined_Portal_N)
|
||||
return 1;
|
||||
|
||||
blockX = ((blockX >> 4) << 2) + 2;
|
||||
blockZ = ((blockZ >> 4) << 2) + 2;
|
||||
setNetherSeed(nn, seed);
|
||||
int biomeID = getNetherBiome(nn, blockX, 0, blockZ, NULL);
|
||||
return isViableFeatureBiome(mc, structureType, biomeID);
|
||||
}
|
||||
|
||||
int isViableEndStructurePos(int structureType, int mc, EndNoise *en,
|
||||
uint64_t seed, int blockX, int blockZ)
|
||||
{
|
||||
switch (structureType)
|
||||
{
|
||||
case End_City:
|
||||
if (mc < MC_1_9) return 0;
|
||||
break;
|
||||
case End_Gateway:
|
||||
if (mc < MC_1_13) return 0;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
setEndSeed(en, seed);
|
||||
|
||||
// end biomes vary only on a per-chunk scale (1:16)
|
||||
// voronoi pre-1.15 shouldn't matter for End Cities as the check will be
|
||||
// near the chunk center
|
||||
int id;
|
||||
int chunkX = blockX >> 4;
|
||||
int chunkZ = blockZ >> 4;
|
||||
mapEndBiome(en, &id, chunkX, chunkZ, 1, 1);
|
||||
return isViableFeatureBiome(mc, structureType, id) ? id : 0;
|
||||
}
|
||||
|
||||
/* Given bordering noise columns and a fractional position between those,
|
||||
* determine the surface block height (i.e. where the interpolated noise > 0).
|
||||
@ -2819,7 +2791,11 @@ L_HAS_PROTO_MUSHROOM:
|
||||
}
|
||||
|
||||
l = g->layers;
|
||||
int *ids = cache ? cache : allocCache(&l[layerID], w, h);
|
||||
int *ids;
|
||||
if (cache)
|
||||
ids = cache;
|
||||
else
|
||||
ids = (int*) calloc(getMinLayerCacheSize(&l[layerID], w, h), sizeof(int));
|
||||
|
||||
filter_data_t fd[9];
|
||||
swapMap(fd+0, &filter, l+L_OCEAN_MIX_4, mapFilterOceanMix);
|
||||
@ -2899,7 +2875,7 @@ int checkForTemps(LayerStack *g, uint64_t seed, int x, int z, int w, int h, cons
|
||||
|
||||
Layer *l = &g->layers[L_SPECIAL_1024];
|
||||
int ccnt[9] = {0};
|
||||
int *area = allocCache(l, w, h);
|
||||
int *area = (int*) calloc(getMinLayerCacheSize(l, w, h), sizeof(int));
|
||||
int ret = 1;
|
||||
|
||||
setLayerSeed(l, seed);
|
||||
|
157
finders.h
157
finders.h
@ -312,16 +312,16 @@ int getStructurePos(int structureType, int mc, uint64_t seed, int regX, int regZ
|
||||
* variants, which have a uniform distribution, while large structures
|
||||
* (monuments and mansions) have a triangular distribution.
|
||||
*/
|
||||
static inline __attribute__((const))
|
||||
static inline ATTR(const)
|
||||
Pos getFeaturePos(StructureConfig config, uint64_t seed, int regX, int regZ);
|
||||
|
||||
static inline __attribute__((const))
|
||||
static inline ATTR(const)
|
||||
Pos getFeatureChunkInRegion(StructureConfig config, uint64_t seed, int regX, int regZ);
|
||||
|
||||
static inline __attribute__((const))
|
||||
static inline ATTR(const)
|
||||
Pos getLargeStructurePos(StructureConfig config, uint64_t seed, int regX, int regZ);
|
||||
|
||||
static inline __attribute__((const))
|
||||
static inline ATTR(const)
|
||||
Pos getLargeStructureChunkInRegion(StructureConfig config, uint64_t seed, int regX, int regZ);
|
||||
|
||||
/* Checks a chunk area, starting at (chunkX, chunkZ) with size (chunkW, chunkH)
|
||||
@ -333,7 +333,7 @@ int getMineshafts(int mc, uint64_t seed, int chunkX, int chunkZ,
|
||||
int chunkW, int chunkH, Pos *out, int nout);
|
||||
|
||||
// not exacly a structure
|
||||
static inline __attribute__((const))
|
||||
static inline ATTR(const)
|
||||
int isSlimeChunk(uint64_t seed, int chunkX, int chunkZ)
|
||||
{
|
||||
uint64_t rnd = seed;
|
||||
@ -491,10 +491,6 @@ int scanForQuads(
|
||||
// Checking Biomes & Biome Helper Functions
|
||||
//==============================================================================
|
||||
|
||||
/* Returns the biome for the specified block position.
|
||||
* (Alternatives should be considered first in performance critical code.)
|
||||
*/
|
||||
int getBiomeAtPos(const LayerStack *g, const Pos pos);
|
||||
|
||||
/* Get the shadow seed.
|
||||
*/
|
||||
@ -507,47 +503,16 @@ static inline uint64_t getShadow(uint64_t seed)
|
||||
* This function is used to determine the positions of spawn and strongholds.
|
||||
* Warning: accurate, but slow!
|
||||
*
|
||||
* @mcversion : Minecraft version (changed in: 1.7, 1.13)
|
||||
* @l : entry layer with scale = 4
|
||||
* @cache : biome buffer, set to NULL for temporary allocation
|
||||
* @centreX, centreZ : origin for the search
|
||||
* @range : square 'radius' of the search
|
||||
* @isValid : boolean array of valid biome ids (size = 256)
|
||||
* @seed : seed used for the RNG
|
||||
* (initialise RNG using setSeed(&seed))
|
||||
* @passes : (output) number of valid biomes passed, NULL to ignore
|
||||
* @g : generator for Overworld biomes
|
||||
* @x,y,z : origin for the search
|
||||
* @radius : square 'radius' of the search
|
||||
* @validBiomes : boolean array of valid biome ids (size = 256)
|
||||
* @rnd : random obj, initialise using setSeed(rnd, world_seed)
|
||||
* @passes : (output) number of valid biomes passed, NULL to ignore
|
||||
*/
|
||||
Pos findBiomePosition(
|
||||
const int mcversion,
|
||||
const Layer * l,
|
||||
int * cache,
|
||||
const int centerX,
|
||||
const int centerZ,
|
||||
const int range,
|
||||
const char * isValid,
|
||||
uint64_t * seed,
|
||||
int * passes
|
||||
);
|
||||
|
||||
/* Determines if the given area contains only biomes specified by 'biomeList'.
|
||||
* This function is used to determine the positions of villages, ocean monuments
|
||||
* and mansions.
|
||||
* Warning: accurate, but slow!
|
||||
*
|
||||
* @l : entry layer with scale = 4: [L_RIVER_MIX_4|L13_OCEAN_MIX_4]
|
||||
* @cache : biome buffer, set to NULL for temporary allocation
|
||||
* @posX, posZ : centre for the check
|
||||
* @radius : 'radius' of the check area
|
||||
* @isValid : boolean array of valid biome ids (size = 256)
|
||||
*/
|
||||
int areBiomesViable(
|
||||
const Layer * l,
|
||||
int * cache,
|
||||
const int posX,
|
||||
const int posZ,
|
||||
const int radius,
|
||||
const char * isValid
|
||||
);
|
||||
Pos locateBiome(
|
||||
const Generator *g, int x, int y, int z, int radius,
|
||||
const char *validBiomes, uint64_t *rng, int *passes);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
@ -572,85 +537,35 @@ Pos initFirstStronghold(StrongholdIter *sh, int mc, uint64_t s48);
|
||||
* location, as well as the approximate location of the next stronghold.
|
||||
*
|
||||
* @sh : stronghold iteration state, holding position info
|
||||
* @g : generator layer stack [world seed should be applied before call!]
|
||||
* @cache : biome buffer, set to NULL for temporary allocation
|
||||
* @g : generator, should be initialized for Overworld generation
|
||||
*
|
||||
* Returns the number of further strongholds after this one.
|
||||
*/
|
||||
int nextStronghold(StrongholdIter *sh, const LayerStack *g, int *cache);
|
||||
|
||||
/* deprecated - use initFirstStronghold() and nextStronghold() instead
|
||||
* Finds the block positions of the strongholds in the world. Note that the
|
||||
* number of strongholds was increased from 3 to 128 in MC 1.9.
|
||||
* Warning: Slow!
|
||||
*
|
||||
* @mcversion : Minecraft version (changed in 1.7, 1.9, 1.13)
|
||||
* @g : generator layer stack [worldSeed should be applied before call!]
|
||||
* @cache : biome buffer, set to NULL for temporary allocation
|
||||
* @locations : output block positions
|
||||
* @worldSeed : world seed of the generator
|
||||
* @maxSH : Stop when this many strongholds have been found. A value of 0
|
||||
* defaults to 3 for mcversion <= MC_1_8, and to 128 for >= MC_1_9.
|
||||
* @maxRing : Stop after this many rings.
|
||||
*
|
||||
* Returned is the number of strongholds found.
|
||||
*/
|
||||
__attribute__((deprecated))
|
||||
int findStrongholds(
|
||||
const int mcversion,
|
||||
const LayerStack * g,
|
||||
int * cache,
|
||||
Pos * locations,
|
||||
uint64_t worldSeed,
|
||||
int maxSH,
|
||||
int maxRing
|
||||
);
|
||||
int nextStronghold(StrongholdIter *sh, const Generator *g);
|
||||
|
||||
/* Finds the spawn point in the world.
|
||||
* Warning: Slow, and may be inaccurate because the world spawn depends on
|
||||
* grass blocks!
|
||||
*
|
||||
* @mc : Minecraft version (changed in 1.7, 1.13)
|
||||
* @g : generator layer stack [worldSeed should be applied before call!]
|
||||
* @cache : biome buffer, set to NULL for temporary allocation
|
||||
* @worldSeed : world seed used for the generator
|
||||
*/
|
||||
Pos getSpawn(const int mc, const LayerStack *g, int *cache, uint64_t worldSeed);
|
||||
Pos getSpawn(const Generator *g);
|
||||
|
||||
/* Finds the approximate spawn point in the world.
|
||||
*
|
||||
* @mc : Minecraft version (changed in 1.7, 1.13)
|
||||
* @g : generator layer stack [worldSeed should be applied before call!]
|
||||
* @cache : biome buffer, set to NULL for temporary allocation
|
||||
* @worldSeed : world seed used for the generator
|
||||
*/
|
||||
Pos estimateSpawn(const int mc, const LayerStack *g, int *cache, uint64_t worldSeed);
|
||||
Pos estimateSpawn(const Generator *g);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// Validating Structure Positions
|
||||
//==============================================================================
|
||||
|
||||
|
||||
/* This function performs a biome check at the specified block coordinates to
|
||||
* determine whether the corresponding structure would spawn there. You can get
|
||||
* the block positions using getStructurePos().
|
||||
*
|
||||
* @structureType : structure type to be checked
|
||||
* @mc : minecraft version
|
||||
* @g : generator layer stack, seed will be applied to layers
|
||||
* @seed : world seed, will be applied to generator
|
||||
* @blockX, blockZ : block coordinates
|
||||
*
|
||||
* The return value is non-zero if the position is valid.
|
||||
/* Performs a biome check at the specified block coordinates to determine
|
||||
* whether a structure of the given type could spawn there. You can get the
|
||||
* block positions using getStructurePos().
|
||||
* The generator, 'g', should be initialized for a scale 1:1 generation of the
|
||||
* correct MC version, dimension and seed. The generator may be temporarily
|
||||
* modified during the function call, but will be restored upon return.
|
||||
*/
|
||||
int isViableStructurePos(int structureType, int mc, LayerStack *g,
|
||||
uint64_t seed, int blockX, int blockZ);
|
||||
|
||||
int isViableNetherStructurePos(int structureType, int mc, NetherNoise *nn,
|
||||
uint64_t seed, int blockX, int blockZ);
|
||||
int isViableEndStructurePos(int structureType, int mc, EndNoise *en,
|
||||
uint64_t seed, int blockX, int blockZ);
|
||||
int isViableStructurePos(int structType, Generator *g, int blockX, int blockZ);
|
||||
|
||||
/* Checks if the specified structure type could generate in the given biome.
|
||||
*/
|
||||
@ -697,7 +612,7 @@ uint64_t getHouseList(uint64_t worldSeed, int chunkX, int chunkZ, int *housesOut
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// Seed Filters
|
||||
// Seed Filters (for versions up to 1.17)
|
||||
//==============================================================================
|
||||
|
||||
|
||||
@ -739,7 +654,7 @@ int checkForBiomes(
|
||||
* if (tc[TEMP_CAT] < 0) avoid, there shall be no entries of this category
|
||||
* TEMP_CAT is any of:
|
||||
* Oceanic, Warm, Lush, Cold, Freeing, Special+Warm, Special+Lush, Special+Cold
|
||||
* For 1.7+ only.
|
||||
* For 1.7-1.17 only.
|
||||
*/
|
||||
int checkForTemps(LayerStack *g, uint64_t seed, int x, int z, int w, int h, const int tc[9]);
|
||||
|
||||
@ -763,7 +678,7 @@ void genPotential(uint64_t *mL, uint64_t *mM, int layer, int mc, int id);
|
||||
//==============================================================================
|
||||
|
||||
|
||||
static inline __attribute__((const))
|
||||
static inline ATTR(const)
|
||||
Pos getFeatureChunkInRegion(StructureConfig config, uint64_t seed, int regX, int regZ)
|
||||
{
|
||||
/*
|
||||
@ -802,7 +717,7 @@ Pos getFeatureChunkInRegion(StructureConfig config, uint64_t seed, int regX, int
|
||||
return pos;
|
||||
}
|
||||
|
||||
static inline __attribute__((const))
|
||||
static inline ATTR(const)
|
||||
Pos getFeaturePos(StructureConfig config, uint64_t seed, int regX, int regZ)
|
||||
{
|
||||
Pos pos = getFeatureChunkInRegion(config, seed, regX, regZ);
|
||||
@ -812,7 +727,7 @@ Pos getFeaturePos(StructureConfig config, uint64_t seed, int regX, int regZ)
|
||||
return pos;
|
||||
}
|
||||
|
||||
static inline __attribute__((const))
|
||||
static inline ATTR(const)
|
||||
Pos getLargeStructureChunkInRegion(StructureConfig config, uint64_t seed, int regX, int regZ)
|
||||
{
|
||||
Pos pos;
|
||||
@ -842,7 +757,7 @@ Pos getLargeStructureChunkInRegion(StructureConfig config, uint64_t seed, int re
|
||||
return pos;
|
||||
}
|
||||
|
||||
static inline __attribute__((const))
|
||||
static inline ATTR(const)
|
||||
Pos getLargeStructurePos(StructureConfig config, uint64_t seed, int regX, int regZ)
|
||||
{
|
||||
Pos pos = getLargeStructureChunkInRegion(config, seed, regX, regZ);
|
||||
@ -854,7 +769,7 @@ Pos getLargeStructurePos(StructureConfig config, uint64_t seed, int regX, int re
|
||||
|
||||
|
||||
|
||||
static __attribute__((const))
|
||||
static ATTR(const)
|
||||
float getEnclosingRadius(
|
||||
int x0, int z0, int x1, int z1, int x2, int z2, int x3, int z3,
|
||||
int ax, int ay, int az, int reg, int gap)
|
||||
@ -943,7 +858,7 @@ static inline float isQuadBase(const StructureConfig sconf, uint64_t seed, int r
|
||||
}
|
||||
|
||||
// optimised version for regionSize=32,chunkRange=24,radius=128
|
||||
static inline __attribute__((always_inline, const))
|
||||
static inline ATTR(always_inline, const)
|
||||
float isQuadBaseFeature24(const StructureConfig sconf, uint64_t seed,
|
||||
int ax, int ay, int az)
|
||||
{
|
||||
@ -995,7 +910,7 @@ float isQuadBaseFeature24(const StructureConfig sconf, uint64_t seed,
|
||||
}
|
||||
|
||||
// variant of isQuadBaseFeature24 which finds only the classic constellations
|
||||
static inline __attribute__((always_inline, const))
|
||||
static inline ATTR(always_inline, const)
|
||||
float isQuadBaseFeature24Classic(const StructureConfig sconf, uint64_t seed)
|
||||
{
|
||||
seed += sconf.salt;
|
||||
@ -1028,7 +943,7 @@ float isQuadBaseFeature24Classic(const StructureConfig sconf, uint64_t seed)
|
||||
return 1; // should actually return one of 122.781311 or 127.887650
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline, const))
|
||||
static inline ATTR(always_inline, const)
|
||||
float isQuadBaseFeature(const StructureConfig sconf, uint64_t seed,
|
||||
int ax, int ay, int az, int radius)
|
||||
{
|
||||
@ -1087,7 +1002,7 @@ float isQuadBaseFeature(const StructureConfig sconf, uint64_t seed,
|
||||
}
|
||||
|
||||
|
||||
static inline __attribute__((always_inline, const))
|
||||
static inline ATTR(always_inline, const)
|
||||
float isQuadBaseLarge(const StructureConfig sconf, uint64_t seed,
|
||||
int ax, int ay, int az, int radius)
|
||||
{
|
||||
|
692
generator.c
692
generator.c
@ -6,10 +6,236 @@
|
||||
#include <string.h>
|
||||
|
||||
|
||||
Layer *setupLayer(LayerStack *g, int layerId, mapfunc_t *map, int mc,
|
||||
int mapOceanMixMod(const Layer * l, int * out, int x, int z, int w, int h)
|
||||
{
|
||||
int *otyp;
|
||||
int i, j;
|
||||
l->p2->getMap(l->p2, out, x, z, w, h);
|
||||
|
||||
otyp = (int *) malloc(w*h*sizeof(int));
|
||||
memcpy(otyp, out, w*h*sizeof(int));
|
||||
|
||||
l->p->getMap(l->p, out, x, z, w, h);
|
||||
|
||||
|
||||
for (j = 0; j < h; j++)
|
||||
{
|
||||
for (i = 0; i < w; i++)
|
||||
{
|
||||
int landID, oceanID;
|
||||
|
||||
landID = out[i + j*w];
|
||||
|
||||
if (!isOceanic(landID))
|
||||
continue;
|
||||
|
||||
oceanID = otyp[i + j*w];
|
||||
|
||||
if (landID == deep_ocean)
|
||||
{
|
||||
switch (oceanID)
|
||||
{
|
||||
case lukewarm_ocean:
|
||||
oceanID = deep_lukewarm_ocean;
|
||||
break;
|
||||
case ocean:
|
||||
oceanID = deep_ocean;
|
||||
break;
|
||||
case cold_ocean:
|
||||
oceanID = deep_cold_ocean;
|
||||
break;
|
||||
case frozen_ocean:
|
||||
oceanID = deep_frozen_ocean;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out[i + j*w] = oceanID;
|
||||
}
|
||||
}
|
||||
|
||||
free(otyp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setupGenerator(Generator *g, int mc, uint32_t flags)
|
||||
{
|
||||
g->mc = mc;
|
||||
g->dim = 0;
|
||||
g->flags = flags;
|
||||
g->seed = 0;
|
||||
g->sha = 0;
|
||||
|
||||
if (mc <= MC_1_17)
|
||||
{
|
||||
setupLayerStack(&g->ls, mc, flags & LARGE_BIOMES);
|
||||
g->entry = NULL;
|
||||
if (flags & FORCE_OCEAN_VARIANTS && mc >= MC_1_13)
|
||||
{
|
||||
g->ls.entry_16 = setupLayer(
|
||||
g->xlayer+2, &mapOceanMixMod, mc, 1, 0, 0,
|
||||
g->ls.entry_16, &g->ls.layers[L_ZOOM_16_OCEAN]);
|
||||
|
||||
g->ls.entry_64 = setupLayer(
|
||||
g->xlayer+3, &mapOceanMixMod, mc, 1, 0, 0,
|
||||
g->ls.entry_64, &g->ls.layers[L_ZOOM_64_OCEAN]);
|
||||
|
||||
g->ls.entry_256 = setupLayer(
|
||||
g->xlayer+4, &mapOceanMixMod, mc, 1, 0, 0,
|
||||
g->ls.entry_256, &g->ls.layers[L_OCEAN_TEMP_256]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
initBiomeNoise(&g->bn, mc);
|
||||
}
|
||||
}
|
||||
|
||||
void applySeed(Generator *g, int dim, uint64_t seed)
|
||||
{
|
||||
g->dim = dim;
|
||||
g->seed = seed;
|
||||
g->sha = 0;
|
||||
|
||||
if (g->mc <= MC_1_17)
|
||||
{
|
||||
if (dim == 0)
|
||||
setLayerSeed(g->entry ? g->entry : g->ls.entry_1, seed);
|
||||
else if (dim == -1 && g->mc >= MC_1_16)
|
||||
setNetherSeed(&g->nn, seed);
|
||||
else if (dim == +1 && g->mc >= MC_1_9)
|
||||
setEndSeed(&g->en, seed);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dim == 0 || dim == -1)
|
||||
setBiomeSeed(&g->bn, seed, dim, g->flags & LARGE_BIOMES);
|
||||
else
|
||||
setEndSeed(&g->en, seed);
|
||||
}
|
||||
if (g->mc >= MC_1_15)
|
||||
{
|
||||
if (g->mc <= MC_1_17 && dim == 0 && !g->entry)
|
||||
g->sha = g->ls.entry_1->startSalt;
|
||||
else
|
||||
g->sha = getVoronoiSHA(seed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t getMinCacheSize(const Generator *g, int scale, int sx, int sy, int sz)
|
||||
{
|
||||
if (sy == 0)
|
||||
sy = 1;
|
||||
|
||||
size_t len = (size_t)sx * sz * sy;
|
||||
if (g->mc <= MC_1_17 && g->dim == 0)
|
||||
{ // recursively check the layer stack for the max buffer
|
||||
const Layer *entry = getLayerForScale(g, scale);
|
||||
if (!entry) {
|
||||
printf("getMinCacheSize(): failed to determine scaled entry\n");
|
||||
exit(1);
|
||||
}
|
||||
size_t len2d = getMinLayerCacheSize(entry, sx, sz);
|
||||
len += len2d - sx*sz;
|
||||
}
|
||||
else if (scale <= 1)
|
||||
{ // allocate space for temporary copy of voronoi source
|
||||
sx = ((sx+3) >> 2) + 2;
|
||||
sy = ((sy+3) >> 2) + 2;
|
||||
sz = ((sz+3) >> 2) + 2;
|
||||
len += sx * sy * sz;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int *allocCache(const Generator *g, Range r)
|
||||
{
|
||||
size_t len = getMinCacheSize(g, r.scale, r.sx, r.sy, r.sz);
|
||||
return (int*) calloc(len, sizeof(int));
|
||||
}
|
||||
|
||||
int genBiomes(const Generator *g, int *cache, Range r)
|
||||
{
|
||||
int err = 1;
|
||||
int i, k;
|
||||
|
||||
if (g->mc <= MC_1_17)
|
||||
{
|
||||
if (g->dim == 0)
|
||||
{
|
||||
const Layer *entry = getLayerForScale(g, r.scale);
|
||||
if (!entry) return -1;
|
||||
err = genArea(entry, cache, r.x, r.z, r.sx, r.sz);
|
||||
if (err) return err;
|
||||
for (k = 1; k < r.sy; k++)
|
||||
{ // overworld has no vertical noise: expanding 2D into 3D
|
||||
for (i = 0; i < r.sx*r.sz; i++)
|
||||
cache[k*r.sx*r.sz + i] = cache[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (g->dim == -1)
|
||||
{
|
||||
return genNetherScaled(&g->nn, cache, r, g->mc, g->sha);
|
||||
}
|
||||
else if (g->dim == +1)
|
||||
{
|
||||
return genEndScaled(&g->en, cache, r, g->mc, g->sha);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g->dim == 0 || g->dim == -1)
|
||||
{
|
||||
return genBiomeNoiseScaled(&g->bn, cache, r, g->dim, g->mc, g->sha);
|
||||
}
|
||||
else if (g->dim == +1)
|
||||
{
|
||||
return genEndScaled(&g->en, cache, r, g->mc, g->sha);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int getBiomeAt(const Generator *g, int scale, int x, int y, int z)
|
||||
{
|
||||
Range r = {scale, x, z, 1, 1, y, 1};
|
||||
int *ids = allocCache(g, r);
|
||||
int id = genBiomes(g, ids, r);
|
||||
if (id == 0)
|
||||
id = ids[0];
|
||||
else
|
||||
id = none;
|
||||
free(ids);
|
||||
return id;
|
||||
}
|
||||
|
||||
const Layer *getLayerForScale(const Generator *g, int scale)
|
||||
{
|
||||
if (g->mc > MC_1_17)
|
||||
return NULL;
|
||||
switch (scale)
|
||||
{
|
||||
case 0: return g->entry;
|
||||
case 1: return g->ls.entry_1;
|
||||
case 4: return g->ls.entry_4;
|
||||
case 16: return g->ls.entry_16;
|
||||
case 64: return g->ls.entry_64;
|
||||
case 256: return g->ls.entry_256;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Layer *setupLayer(Layer *l, mapfunc_t *map, int mc,
|
||||
int8_t zoom, int8_t edge, uint64_t saltbase, Layer *p, Layer *p2)
|
||||
{
|
||||
Layer *l = g->layers + layerId;
|
||||
//Layer *l = g->layers + layerId;
|
||||
l->getMap = map;
|
||||
l->mc = mc;
|
||||
l->zoom = zoom;
|
||||
@ -37,16 +263,14 @@ static void setupScale(Layer *l, int scale)
|
||||
setupScale(l->p2, scale * l->zoom);
|
||||
}
|
||||
|
||||
void setupGeneratorLargeBiomes(LayerStack *g, int mc, int largeBiomes)
|
||||
void setupLayerStack(LayerStack *g, int mc, int largeBiomes)
|
||||
{
|
||||
if (mc < MC_1_3)
|
||||
largeBiomes = 0;
|
||||
|
||||
memset(g, 0, sizeof(LayerStack));
|
||||
Layer *p;
|
||||
|
||||
// G: generator layer stack
|
||||
// L: layer ID
|
||||
Layer *p, *l = g->layers;
|
||||
// L: layer
|
||||
// M: mapping function
|
||||
// V: minecraft version
|
||||
// Z: zoom
|
||||
@ -55,224 +279,220 @@ void setupGeneratorLargeBiomes(LayerStack *g, int mc, int largeBiomes)
|
||||
// P1: parent 1
|
||||
// P2: parent 2
|
||||
|
||||
// G, L, M V Z E S P1 P2
|
||||
p = setupLayer(g, L_CONTINENT_4096, mapContinent, mc, 1, 0, 1, 0, 0);
|
||||
p = setupLayer(g, L_ZOOM_2048, mapZoomFuzzy, mc, 2, 3, 2000, p, 0);
|
||||
p = setupLayer(g, L_LAND_2048, mapLand, mc, 1, 2, 1, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_1024, mapZoom, mc, 2, 3, 2001, p, 0);
|
||||
p = setupLayer(g, L_LAND_1024_A, mapLand, mc, 1, 2, 2, p, 0);
|
||||
// L M V Z E S P1 P2
|
||||
p = setupLayer(l+L_CONTINENT_4096, mapContinent, mc, 1, 0, 1, 0, 0);
|
||||
p = setupLayer(l+L_ZOOM_2048, mapZoomFuzzy, mc, 2, 3, 2000, p, 0);
|
||||
p = setupLayer(l+L_LAND_2048, mapLand, mc, 1, 2, 1, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_1024, mapZoom, mc, 2, 3, 2001, p, 0);
|
||||
p = setupLayer(l+L_LAND_1024_A, mapLand, mc, 1, 2, 2, p, 0);
|
||||
|
||||
if (mc <= MC_1_6)
|
||||
{ // G L M V Z E S P1 P2
|
||||
p = setupLayer(g, L_SNOW_1024, mapSnow16, mc, 1, 2, 2, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_512, mapZoom, mc, 2, 3, 2002, p, 0);
|
||||
p = setupLayer(g, L_LAND_512, mapLand16, mc, 1, 2, 3, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_256, mapZoom, mc, 2, 3, 2003, p, 0);
|
||||
p = setupLayer(g, L_LAND_256, mapLand16, mc, 1, 2, 4, p, 0);
|
||||
p = setupLayer(g, L_MUSHROOM_256, mapMushroom, mc, 1, 2, 5, p, 0);
|
||||
p = setupLayer(g, L_BIOME_256, mapBiome, mc, 1, 0, 200, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_128, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_64, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
{ // L M V Z E S P1 P2
|
||||
p = setupLayer(l+L_SNOW_1024, mapSnow16, mc, 1, 2, 2, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_512, mapZoom, mc, 2, 3, 2002, p, 0);
|
||||
p = setupLayer(l+L_LAND_512, mapLand16, mc, 1, 2, 3, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_256, mapZoom, mc, 2, 3, 2003, p, 0);
|
||||
p = setupLayer(l+L_LAND_256, mapLand16, mc, 1, 2, 4, p, 0);
|
||||
p = setupLayer(l+L_MUSHROOM_256, mapMushroom, mc, 1, 2, 5, p, 0);
|
||||
p = setupLayer(l+L_BIOME_256, mapBiome, mc, 1, 0, 200, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_128, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_64, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
// river noise layer chain, also used to determine where hills generate
|
||||
p = setupLayer(g, L_NOISE_256, mapNoise, mc, 1, 0, 100,
|
||||
g->layers+L_MUSHROOM_256, 0);
|
||||
p = setupLayer(l+L_NOISE_256, mapNoise, mc, 1, 0, 100,
|
||||
l+L_MUSHROOM_256, 0);
|
||||
}
|
||||
else
|
||||
{ // G L M V Z E S P1 P2
|
||||
p = setupLayer(g, L_LAND_1024_B, mapLand, mc, 1, 2, 50, p, 0);
|
||||
p = setupLayer(g, L_LAND_1024_C, mapLand, mc, 1, 2, 70, p, 0);
|
||||
p = setupLayer(g, L_ISLAND_1024, mapIsland, mc, 1, 2, 2, p, 0);
|
||||
p = setupLayer(g, L_SNOW_1024, mapSnow, mc, 1, 2, 2, p, 0);
|
||||
p = setupLayer(g, L_LAND_1024_D, mapLand, mc, 1, 2, 3, p, 0);
|
||||
p = setupLayer(g, L_COOL_1024, mapCool, mc, 1, 2, 2, p, 0);
|
||||
p = setupLayer(g, L_HEAT_1024, mapHeat, mc, 1, 2, 2, p, 0);
|
||||
p = setupLayer(g, L_SPECIAL_1024, mapSpecial, mc, 1, 2, 3, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_512, mapZoom, mc, 2, 3, 2002, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_256, mapZoom, mc, 2, 3, 2003, p, 0);
|
||||
p = setupLayer(g, L_LAND_256, mapLand, mc, 1, 2, 4, p, 0);
|
||||
p = setupLayer(g, L_MUSHROOM_256, mapMushroom, mc, 1, 2, 5, p, 0);
|
||||
p = setupLayer(g, L_DEEP_OCEAN_256, mapDeepOcean, mc, 1, 2, 4, p, 0);
|
||||
p = setupLayer(g, L_BIOME_256, mapBiome, mc, 1, 0, 200, p, 0);
|
||||
{ // L M V Z E S P1 P2
|
||||
p = setupLayer(l+L_LAND_1024_B, mapLand, mc, 1, 2, 50, p, 0);
|
||||
p = setupLayer(l+L_LAND_1024_C, mapLand, mc, 1, 2, 70, p, 0);
|
||||
p = setupLayer(l+L_ISLAND_1024, mapIsland, mc, 1, 2, 2, p, 0);
|
||||
p = setupLayer(l+L_SNOW_1024, mapSnow, mc, 1, 2, 2, p, 0);
|
||||
p = setupLayer(l+L_LAND_1024_D, mapLand, mc, 1, 2, 3, p, 0);
|
||||
p = setupLayer(l+L_COOL_1024, mapCool, mc, 1, 2, 2, p, 0);
|
||||
p = setupLayer(l+L_HEAT_1024, mapHeat, mc, 1, 2, 2, p, 0);
|
||||
p = setupLayer(l+L_SPECIAL_1024, mapSpecial, mc, 1, 2, 3, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_512, mapZoom, mc, 2, 3, 2002, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_256, mapZoom, mc, 2, 3, 2003, p, 0);
|
||||
p = setupLayer(l+L_LAND_256, mapLand, mc, 1, 2, 4, p, 0);
|
||||
p = setupLayer(l+L_MUSHROOM_256, mapMushroom, mc, 1, 2, 5, p, 0);
|
||||
p = setupLayer(l+L_DEEP_OCEAN_256, mapDeepOcean, mc, 1, 2, 4, p, 0);
|
||||
p = setupLayer(l+L_BIOME_256, mapBiome, mc, 1, 0, 200, p, 0);
|
||||
if (mc >= MC_1_14)
|
||||
p = setupLayer(g, L_BAMBOO_256, mapBamboo, mc, 1, 0, 1001, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_128, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_64, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(g, L_BIOME_EDGE_64, mapBiomeEdge, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(l+L_BAMBOO_256, mapBamboo, mc, 1, 0, 1001, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_128, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_64, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(l+L_BIOME_EDGE_64, mapBiomeEdge, mc, 1, 2, 1000, p, 0);
|
||||
// river noise layer chain, also used to determine where hills generate
|
||||
p = setupLayer(g, L_RIVER_INIT_256, mapNoise, mc, 1, 0, 100,
|
||||
g->layers+L_DEEP_OCEAN_256, 0);
|
||||
p = setupLayer(l+L_RIVER_INIT_256, mapNoise, mc, 1, 0, 100,
|
||||
l+L_DEEP_OCEAN_256, 0);
|
||||
}
|
||||
|
||||
if (mc <= MC_1_12)
|
||||
{
|
||||
p = setupLayer(g, L_ZOOM_128_HILLS, mapZoom, mc, 2, 3, 0, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_64_HILLS, mapZoom, mc, 2, 3, 0, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_128_HILLS, mapZoom, mc, 2, 3, 0, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_64_HILLS, mapZoom, mc, 2, 3, 0, p, 0);
|
||||
}
|
||||
else if (mc >= MC_1_1)
|
||||
{
|
||||
p = setupLayer(g, L_ZOOM_128_HILLS, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_64_HILLS, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_128_HILLS, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_64_HILLS, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
}
|
||||
|
||||
if (mc <= MC_1_0)
|
||||
{ // G L M V Z E S P1 P2
|
||||
p = setupLayer(g, L_ZOOM_32, mapZoom, mc, 2, 3, 1000,
|
||||
g->layers+L_ZOOM_64, 0);
|
||||
p = setupLayer(g, L_LAND_32, mapLand16, mc, 1, 2, 3, p, 0);
|
||||
{ // L M V Z E S P1 P2
|
||||
p = setupLayer(l+L_ZOOM_32, mapZoom, mc, 2, 3, 1000,
|
||||
l+L_ZOOM_64, 0);
|
||||
p = setupLayer(l+L_LAND_32, mapLand16, mc, 1, 2, 3, p, 0);
|
||||
// NOTE: reusing slot for shore:16, but scale is 1:32
|
||||
p = setupLayer(g, L_SHORE_16, mapShore, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_16, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_8, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_4, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
p = setupLayer(l+L_SHORE_16, mapShore, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_16, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_8, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_4, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
|
||||
p = setupLayer(g, L_SMOOTH_4, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(l+L_SMOOTH_4, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
|
||||
// river layer chain
|
||||
p = setupLayer(g, L_ZOOM_128_RIVER, mapZoom, mc, 2, 3, 1000,
|
||||
g->layers+L_NOISE_256, 0);
|
||||
p = setupLayer(g, L_ZOOM_64_RIVER, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_32_RIVER, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_16_RIVER, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_8_RIVER, mapZoom, mc, 2, 3, 1004, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_4_RIVER, mapZoom, mc, 2, 3, 1005, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_128_RIVER, mapZoom, mc, 2, 3, 1000,
|
||||
l+L_NOISE_256, 0);
|
||||
p = setupLayer(l+L_ZOOM_64_RIVER, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_32_RIVER, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_16_RIVER, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_8_RIVER, mapZoom, mc, 2, 3, 1004, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_4_RIVER, mapZoom, mc, 2, 3, 1005, p, 0);
|
||||
|
||||
p = setupLayer(g, L_RIVER_4, mapRiver, mc, 1, 2, 1, p, 0);
|
||||
p = setupLayer(g, L_SMOOTH_4_RIVER, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(l+L_RIVER_4, mapRiver, mc, 1, 2, 1, p, 0);
|
||||
p = setupLayer(l+L_SMOOTH_4_RIVER, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
}
|
||||
else if (mc <= MC_1_6)
|
||||
{
|
||||
p = setupLayer(g, L_HILLS_64, mapHills, mc, 1, 2, 1000,
|
||||
g->layers+L_ZOOM_64, g->layers+L_ZOOM_64_HILLS);
|
||||
p = setupLayer(l+L_HILLS_64, mapHills, mc, 1, 2, 1000,
|
||||
l+L_ZOOM_64, l+L_ZOOM_64_HILLS);
|
||||
|
||||
p = setupLayer(g, L_ZOOM_32, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(g, L_LAND_32, mapLand16, mc, 1, 2, 3, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_16, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(g, L_SHORE_16, mapShore, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(g, L_SWAMP_RIVER_16, mapSwampRiver, mc, 1, 0, 1000, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_8, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_4, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_32, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(l+L_LAND_32, mapLand16, mc, 1, 2, 3, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_16, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(l+L_SHORE_16, mapShore, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(l+L_SWAMP_RIVER_16, mapSwampRiver, mc, 1, 0, 1000, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_8, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_4, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
|
||||
if (largeBiomes)
|
||||
{
|
||||
p = setupLayer(g, L_ZOOM_LARGE_A, mapZoom, mc, 2, 3, 1004, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_LARGE_B, mapZoom, mc, 2, 3, 1005, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_LARGE_A, mapZoom, mc, 2, 3, 1004, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_LARGE_B, mapZoom, mc, 2, 3, 1005, p, 0);
|
||||
}
|
||||
|
||||
p = setupLayer(g, L_SMOOTH_4, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(l+L_SMOOTH_4, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
|
||||
// river layer chain
|
||||
p = setupLayer(g, L_ZOOM_128_RIVER, mapZoom, mc, 2, 3, 1000,
|
||||
g->layers+L_NOISE_256, 0);
|
||||
p = setupLayer(g, L_ZOOM_64_RIVER, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_32_RIVER, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_16_RIVER, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_8_RIVER, mapZoom, mc, 2, 3, 1004, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_4_RIVER, mapZoom, mc, 2, 3, 1005, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_128_RIVER, mapZoom, mc, 2, 3, 1000,
|
||||
l+L_NOISE_256, 0);
|
||||
p = setupLayer(l+L_ZOOM_64_RIVER, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_32_RIVER, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_16_RIVER, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_8_RIVER, mapZoom, mc, 2, 3, 1004, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_4_RIVER, mapZoom, mc, 2, 3, 1005, p, 0);
|
||||
|
||||
if (largeBiomes)
|
||||
{
|
||||
p = setupLayer(g, L_ZOOM_L_RIVER_A, mapZoom, mc, 2, 3, 1006, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_L_RIVER_B, mapZoom, mc, 2, 3, 1007, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_L_RIVER_A, mapZoom, mc, 2, 3, 1006, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_L_RIVER_B, mapZoom, mc, 2, 3, 1007, p, 0);
|
||||
}
|
||||
|
||||
p = setupLayer(g, L_RIVER_4, mapRiver, mc, 1, 2, 1, p, 0);
|
||||
p = setupLayer(g, L_SMOOTH_4_RIVER, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(l+L_RIVER_4, mapRiver, mc, 1, 2, 1, p, 0);
|
||||
p = setupLayer(l+L_SMOOTH_4_RIVER, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = setupLayer(g, L_HILLS_64, mapHills, mc, 1, 2, 1000,
|
||||
g->layers+L_BIOME_EDGE_64, g->layers+L_ZOOM_64_HILLS);
|
||||
p = setupLayer(l+L_HILLS_64, mapHills, mc, 1, 2, 1000,
|
||||
l+L_BIOME_EDGE_64, l+L_ZOOM_64_HILLS);
|
||||
|
||||
p = setupLayer(g, L_SUNFLOWER_64, mapSunflower, mc, 1, 0, 1001, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_32, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(g, L_LAND_32, mapLand, mc, 1, 2, 3, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_16, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(g, L_SHORE_16, mapShore, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_8, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_4, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
p = setupLayer(l+L_SUNFLOWER_64, mapSunflower, mc, 1, 0, 1001, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_32, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(l+L_LAND_32, mapLand, mc, 1, 2, 3, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_16, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(l+L_SHORE_16, mapShore, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_8, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_4, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
|
||||
if (largeBiomes)
|
||||
{
|
||||
p = setupLayer(g, L_ZOOM_LARGE_A, mapZoom, mc, 2, 3, 1004, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_LARGE_B, mapZoom, mc, 2, 3, 1005, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_LARGE_A, mapZoom, mc, 2, 3, 1004, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_LARGE_B, mapZoom, mc, 2, 3, 1005, p, 0);
|
||||
}
|
||||
|
||||
p = setupLayer(g, L_SMOOTH_4, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(l+L_SMOOTH_4, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
|
||||
// river layer chain
|
||||
p = setupLayer(g, L_ZOOM_128_RIVER, mapZoom, mc, 2, 3, 1000,
|
||||
g->layers+L_RIVER_INIT_256, 0);
|
||||
p = setupLayer(g, L_ZOOM_64_RIVER, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_32_RIVER, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_16_RIVER, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_8_RIVER, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_4_RIVER, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_128_RIVER, mapZoom, mc, 2, 3, 1000,
|
||||
l+L_RIVER_INIT_256, 0);
|
||||
p = setupLayer(l+L_ZOOM_64_RIVER, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_32_RIVER, mapZoom, mc, 2, 3, 1000, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_16_RIVER, mapZoom, mc, 2, 3, 1001, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_8_RIVER, mapZoom, mc, 2, 3, 1002, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_4_RIVER, mapZoom, mc, 2, 3, 1003, p, 0);
|
||||
|
||||
if (largeBiomes && mc == MC_1_7)
|
||||
{
|
||||
p = setupLayer(g, L_ZOOM_L_RIVER_A, mapZoom, mc, 2, 3, 1004, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_L_RIVER_B, mapZoom, mc, 2, 3, 1005, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_L_RIVER_A, mapZoom, mc, 2, 3, 1004, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_L_RIVER_B, mapZoom, mc, 2, 3, 1005, p, 0);
|
||||
}
|
||||
|
||||
p = setupLayer(g, L_RIVER_4, mapRiver, mc, 1, 2, 1, p, 0);
|
||||
p = setupLayer(g, L_SMOOTH_4_RIVER, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
p = setupLayer(l+L_RIVER_4, mapRiver, mc, 1, 2, 1, p, 0);
|
||||
p = setupLayer(l+L_SMOOTH_4_RIVER, mapSmooth, mc, 1, 2, 1000, p, 0);
|
||||
}
|
||||
|
||||
p = setupLayer(g, L_RIVER_MIX_4, mapRiverMix, mc, 1, 0, 100,
|
||||
g->layers+L_SMOOTH_4, g->layers+L_SMOOTH_4_RIVER);
|
||||
p = setupLayer(l+L_RIVER_MIX_4, mapRiverMix, mc, 1, 0, 100,
|
||||
l+L_SMOOTH_4, l+L_SMOOTH_4_RIVER);
|
||||
|
||||
|
||||
if (mc <= MC_1_12)
|
||||
{
|
||||
p = setupLayer(g, L_VORONOI_1, mapVoronoi114, mc, 4, 3, 10, p, 0);
|
||||
p = setupLayer(l+L_VORONOI_1, mapVoronoi114, mc, 4, 3, 10, p, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ocean variants
|
||||
p = setupLayer(g, L_OCEAN_TEMP_256, mapOceanTemp, mc, 1, 0, 2, 0, 0);
|
||||
p = setupLayer(l+L_OCEAN_TEMP_256, mapOceanTemp, mc, 1, 0, 2, 0, 0);
|
||||
p->noise = &g->oceanRnd;
|
||||
p = setupLayer(g, L_ZOOM_128_OCEAN, mapZoom, mc, 2, 3, 2001, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_64_OCEAN, mapZoom, mc, 2, 3, 2002, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_32_OCEAN, mapZoom, mc, 2, 3, 2003, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_16_OCEAN, mapZoom, mc, 2, 3, 2004, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_8_OCEAN, mapZoom, mc, 2, 3, 2005, p, 0);
|
||||
p = setupLayer(g, L_ZOOM_4_OCEAN, mapZoom, mc, 2, 3, 2006, p, 0);
|
||||
p = setupLayer(g, L_OCEAN_MIX_4, mapOceanMix, mc, 1, 17, 100,
|
||||
g->layers+L_RIVER_MIX_4, g->layers+L_ZOOM_4_OCEAN);
|
||||
p = setupLayer(l+L_ZOOM_128_OCEAN, mapZoom, mc, 2, 3, 2001, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_64_OCEAN, mapZoom, mc, 2, 3, 2002, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_32_OCEAN, mapZoom, mc, 2, 3, 2003, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_16_OCEAN, mapZoom, mc, 2, 3, 2004, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_8_OCEAN, mapZoom, mc, 2, 3, 2005, p, 0);
|
||||
p = setupLayer(l+L_ZOOM_4_OCEAN, mapZoom, mc, 2, 3, 2006, p, 0);
|
||||
p = setupLayer(l+L_OCEAN_MIX_4, mapOceanMix, mc, 1, 17, 100,
|
||||
l+L_RIVER_MIX_4, l+L_ZOOM_4_OCEAN);
|
||||
|
||||
if (mc <= MC_1_14)
|
||||
p = setupLayer(g, L_VORONOI_1, mapVoronoi114, mc, 4, 3, 10, p, 0);
|
||||
p = setupLayer(l+L_VORONOI_1, mapVoronoi114, mc, 4, 3, 10, p, 0);
|
||||
else
|
||||
p = setupLayer(g, L_VORONOI_1, mapVoronoi, mc, 4, 3, LAYER_INIT_SHA, p, 0);
|
||||
p = setupLayer(l+L_VORONOI_1, mapVoronoi, mc, 4, 3, LAYER_INIT_SHA, p, 0);
|
||||
}
|
||||
|
||||
g->entry_1 = p;
|
||||
g->entry_4 = g->layers + (mc <= MC_1_12 ? L_RIVER_MIX_4 : L_OCEAN_MIX_4);
|
||||
g->entry_4 = l + (mc <= MC_1_12 ? L_RIVER_MIX_4 : L_OCEAN_MIX_4);
|
||||
if (largeBiomes)
|
||||
{
|
||||
g->entry_16 = g->layers + L_ZOOM_4;
|
||||
g->entry_64 = g->layers + (mc <= MC_1_6 ? L_SWAMP_RIVER_16 : L_SHORE_16);
|
||||
g->entry_256 = g->layers + (mc <= MC_1_7 ? L_HILLS_64 : L_SUNFLOWER_64);
|
||||
g->entry_16 = l + L_ZOOM_4;
|
||||
g->entry_64 = l + (mc <= MC_1_6 ? L_SWAMP_RIVER_16 : L_SHORE_16);
|
||||
g->entry_256 = l + (mc <= MC_1_7 ? L_HILLS_64 : L_SUNFLOWER_64);
|
||||
}
|
||||
else if (mc >= MC_1_1)
|
||||
{
|
||||
g->entry_16 = g->layers + (mc <= MC_1_6 ? L_SWAMP_RIVER_16 : L_SHORE_16);
|
||||
g->entry_64 = g->layers + (mc <= MC_1_7 ? L_HILLS_64 : L_SUNFLOWER_64);
|
||||
g->entry_256 = g->layers + (mc <= MC_1_14 ? L_BIOME_256 : L_BAMBOO_256);
|
||||
g->entry_16 = l + (mc <= MC_1_6 ? L_SWAMP_RIVER_16 : L_SHORE_16);
|
||||
g->entry_64 = l + (mc <= MC_1_7 ? L_HILLS_64 : L_SUNFLOWER_64);
|
||||
g->entry_256 = l + (mc <= MC_1_14 ? L_BIOME_256 : L_BAMBOO_256);
|
||||
}
|
||||
else
|
||||
{
|
||||
g->entry_16 = g->layers + L_ZOOM_16;
|
||||
g->entry_64 = g->layers + L_ZOOM_64;
|
||||
g->entry_256 = g->layers + L_BIOME_256;
|
||||
g->entry_16 = l + L_ZOOM_16;
|
||||
g->entry_64 = l + L_ZOOM_64;
|
||||
g->entry_256 = l + L_BIOME_256;
|
||||
}
|
||||
setupScale(g->entry_1, 1);
|
||||
}
|
||||
|
||||
void setupGenerator(LayerStack *g, int mc)
|
||||
{
|
||||
setupGeneratorLargeBiomes(g, mc, 0);
|
||||
}
|
||||
|
||||
/* Recursively calculates the minimum buffer size required to generate an area
|
||||
* of the specified size from the current layer onwards.
|
||||
@ -309,29 +529,14 @@ static void getMaxArea(
|
||||
getMaxArea(layer->p2, areaX, areaZ, maxX, maxZ, siz);
|
||||
}
|
||||
|
||||
size_t calcRequiredBuf(const Layer *layer, int areaX, int areaZ)
|
||||
size_t getMinLayerCacheSize(const Layer *layer, int sizeX, int sizeZ)
|
||||
{
|
||||
int maxX = areaX, maxZ = areaZ;
|
||||
int maxX = sizeX, maxZ = sizeZ;
|
||||
size_t bufsiz = 0;
|
||||
getMaxArea(layer, areaX, areaZ, &maxX, &maxZ, &bufsiz);
|
||||
getMaxArea(layer, sizeX, sizeZ, &maxX, &maxZ, &bufsiz);
|
||||
return bufsiz + maxX * (size_t)maxZ;
|
||||
}
|
||||
|
||||
int *allocCache(const Layer *layer, int sizeX, int sizeZ)
|
||||
{
|
||||
size_t bytes = calcRequiredBuf(layer, sizeX, sizeZ) * sizeof(int);
|
||||
int *ret = (int *) malloc(bytes);
|
||||
memset(ret, 0, bytes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void applySeed(LayerStack *g, uint64_t seed)
|
||||
{
|
||||
// the seed has to be applied recursively
|
||||
setLayerSeed(g->entry_1, seed);
|
||||
}
|
||||
|
||||
int genArea(const Layer *layer, int *out, int areaX, int areaZ, int areaWidth, int areaHeight)
|
||||
{
|
||||
memset(out, 0, areaWidth*areaHeight*sizeof(*out));
|
||||
@ -340,172 +545,5 @@ int genArea(const Layer *layer, int *out, int areaX, int areaZ, int areaWidth, i
|
||||
|
||||
|
||||
|
||||
int genNetherScaled(int mc, uint64_t seed, int scale, int *out,
|
||||
int x, int z, int w, int h, int y0, int y1)
|
||||
{
|
||||
if (scale != 1 && scale != 4 && scale != 16 && scale != 64)
|
||||
return 1; // unsupported scale
|
||||
|
||||
if (mc < MC_1_16)
|
||||
{
|
||||
int i, siz = w*h*(y1-y0+1);
|
||||
for (i = 0; i < siz; i++)
|
||||
out[i] = nether_wastes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetherNoise nn;
|
||||
setNetherSeed(&nn, seed);
|
||||
|
||||
if (scale == 1)
|
||||
{
|
||||
if (y0 != 0 || y1 != 0)
|
||||
{
|
||||
printf("getNetherScaled(): volume voronoi not implemented yet\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vx = x - 2;
|
||||
int vz = z - 2;
|
||||
int pX = vx >> 2;
|
||||
int pZ = vz >> 2;
|
||||
int pW = ((vx + w) >> 2) - pX + 2;
|
||||
int pH = ((vz + h) >> 2) - pZ + 2;
|
||||
|
||||
int err = mapNether2D(&nn, out, pX, pZ, pW, pH);
|
||||
if (err)
|
||||
return err;
|
||||
Layer lvoronoi;
|
||||
memset(&lvoronoi, 0, sizeof(Layer));
|
||||
lvoronoi.startSalt = getVoronoiSHA(seed);
|
||||
return mapVoronoi(&lvoronoi, out, x, z, w, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
return mapNether3D(&nn, out, x, z, w, h, y0, y1-y0+1, scale, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int genEndScaled(int mc, uint64_t seed, int scale, int *out,
|
||||
int x, int z, int w, int h)
|
||||
{
|
||||
if (scale != 1 && scale != 4 && scale != 16 && scale != 64)
|
||||
return 1; // unsupported scale
|
||||
|
||||
if (mc < MC_1_9)
|
||||
{
|
||||
int i, siz = w*h;
|
||||
for (i = 0; i < siz; i++)
|
||||
out[i] = the_end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
EndNoise en;
|
||||
setEndSeed(&en, seed);
|
||||
|
||||
if (scale == 1)
|
||||
{
|
||||
int vx = x - 2;
|
||||
int vz = z - 2;
|
||||
int pX = vx >> 2;
|
||||
int pZ = vz >> 2;
|
||||
int pW = ((vx + w) >> 2) - pX + 2;
|
||||
int pH = ((vz + h) >> 2) - pZ + 2;
|
||||
|
||||
int err = mapEnd(&en, out, pX, pZ, pW, pH);
|
||||
if (err)
|
||||
return err;
|
||||
Layer lvoronoi;
|
||||
memset(&lvoronoi, 0, sizeof(Layer));
|
||||
if (mc >= MC_1_15)
|
||||
{
|
||||
lvoronoi.startSalt = getVoronoiSHA(seed);
|
||||
return mapVoronoi(&lvoronoi, out, x, z, w, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
lvoronoi.startSalt = getLayerSalt(10);
|
||||
return mapVoronoi114(&lvoronoi, out, x, z, w, h);
|
||||
}
|
||||
}
|
||||
else if (scale == 4)
|
||||
{
|
||||
return mapEnd(&en, out, x, z, w, h);
|
||||
}
|
||||
else if (scale == 16)
|
||||
{
|
||||
return mapEndBiome(&en, out, x, z, w, h);
|
||||
}
|
||||
else if (scale == 64)
|
||||
{
|
||||
int i, j, di, dj;
|
||||
int r = 4;
|
||||
int hw = (2+w) * r + 1;
|
||||
int hh = (2+h) * r + 1;
|
||||
int16_t *hmap = (int16_t*) calloc(hw*hh, sizeof(*hmap));
|
||||
|
||||
for (j = 0; j < h; j++)
|
||||
{
|
||||
for (i = 0; i < w; i++)
|
||||
{
|
||||
int64_t hx = (i+x) * r;
|
||||
int64_t hz = (j+z) * r;
|
||||
if (hx*hx + hz*hz <= 4096L)
|
||||
{
|
||||
out[j*w+i] = the_end;
|
||||
continue;
|
||||
}
|
||||
|
||||
int64_t h = 64*16*16;
|
||||
|
||||
for (dj = -r; dj < r; dj++)
|
||||
{
|
||||
for (di = -r; di < r; di++)
|
||||
{
|
||||
int64_t rx = hx + di;
|
||||
int64_t rz = hz + dj;
|
||||
int hi = i*r + di+r;
|
||||
int hj = j*r + dj+r;
|
||||
int16_t *p = &hmap[hj*hw + hi];
|
||||
if (*p == 0)
|
||||
{
|
||||
if (sampleSimplex2D(&en, rx, rz) < -0.9f)
|
||||
{
|
||||
*p = (llabs(rx) * 3439 + llabs(rz) * 147) % 13 + 9;
|
||||
*p *= *p;
|
||||
}
|
||||
else
|
||||
{
|
||||
*p = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p > 0)
|
||||
{
|
||||
int64_t noise = 4*(di*di + dj*dj) * (*p);
|
||||
if (noise < h)
|
||||
h = noise;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (h < 3600)
|
||||
out[j*w+i] = end_highlands;
|
||||
else if (h <= 10000)
|
||||
out[j*w+i] = end_midlands;
|
||||
else if (h <= 14400)
|
||||
out[j*w+i] = end_barrens;
|
||||
else
|
||||
out[j*w+i] = small_end_islands;
|
||||
}
|
||||
}
|
||||
free(hmap);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
197
generator.h
197
generator.h
@ -3,88 +3,31 @@
|
||||
|
||||
#include "layers.h"
|
||||
|
||||
// generator flags
|
||||
#define LARGE_BIOMES 0x1
|
||||
#define FORCE_OCEAN_VARIANTS 0x4
|
||||
|
||||
/* Enumeration of the layer indices in the generator. */
|
||||
enum
|
||||
|
||||
STRUCT(Generator)
|
||||
{
|
||||
// new [[deprecated]]
|
||||
L_CONTINENT_4096 = 0, L_ISLAND_4096 = L_CONTINENT_4096,
|
||||
L_ZOOM_2048,
|
||||
L_LAND_2048, L_ADD_ISLAND_2048 = L_LAND_2048,
|
||||
L_ZOOM_1024,
|
||||
L_LAND_1024_A, L_ADD_ISLAND_1024A = L_LAND_1024_A,
|
||||
L_LAND_1024_B, L_ADD_ISLAND_1024B = L_LAND_1024_B, // 1.7+
|
||||
L_LAND_1024_C, L_ADD_ISLAND_1024C = L_LAND_1024_C, // 1.7+
|
||||
L_ISLAND_1024, L_REMOVE_OCEAN_1024 = L_ISLAND_1024, // 1.7+
|
||||
L_SNOW_1024, L_ADD_SNOW_1024 = L_SNOW_1024,
|
||||
L_LAND_1024_D, L_ADD_ISLAND_1024D = L_LAND_1024_D, // 1.7+
|
||||
L_COOL_1024, L_COOL_WARM_1024 = L_COOL_1024, // 1.7+
|
||||
L_HEAT_1024, L_HEAT_ICE_1024 = L_HEAT_1024, // 1.7+
|
||||
L_SPECIAL_1024, // 1.7+
|
||||
L_ZOOM_512,
|
||||
L_LAND_512, // 1.6-
|
||||
L_ZOOM_256,
|
||||
L_LAND_256, L_ADD_ISLAND_256 = L_LAND_256,
|
||||
L_MUSHROOM_256, L_ADD_MUSHROOM_256 = L_MUSHROOM_256,
|
||||
L_DEEP_OCEAN_256, // 1.7+
|
||||
L_BIOME_256,
|
||||
L_BAMBOO_256, L14_BAMBOO_256 = L_BAMBOO_256, // 1.14+
|
||||
L_ZOOM_128,
|
||||
L_ZOOM_64,
|
||||
L_BIOME_EDGE_64,
|
||||
L_NOISE_256, L_RIVER_INIT_256 = L_NOISE_256,
|
||||
L_ZOOM_128_HILLS,
|
||||
L_ZOOM_64_HILLS,
|
||||
L_HILLS_64,
|
||||
L_SUNFLOWER_64, L_RARE_BIOME_64 = L_SUNFLOWER_64, // 1.7+
|
||||
L_ZOOM_32,
|
||||
L_LAND_32, L_ADD_ISLAND_32 = L_LAND_32,
|
||||
L_ZOOM_16,
|
||||
L_SHORE_16, // NOTE: in 1.0 this slot is scale 1:32
|
||||
L_SWAMP_RIVER_16, // 1.6-
|
||||
L_ZOOM_8,
|
||||
L_ZOOM_4,
|
||||
L_SMOOTH_4,
|
||||
L_ZOOM_128_RIVER,
|
||||
L_ZOOM_64_RIVER,
|
||||
L_ZOOM_32_RIVER,
|
||||
L_ZOOM_16_RIVER,
|
||||
L_ZOOM_8_RIVER,
|
||||
L_ZOOM_4_RIVER,
|
||||
L_RIVER_4,
|
||||
L_SMOOTH_4_RIVER,
|
||||
L_RIVER_MIX_4,
|
||||
L_OCEAN_TEMP_256, L13_OCEAN_TEMP_256 = L_OCEAN_TEMP_256, // 1.13+
|
||||
L_ZOOM_128_OCEAN, L13_ZOOM_128 = L_ZOOM_128_OCEAN, // 1.13+
|
||||
L_ZOOM_64_OCEAN, L13_ZOOM_64 = L_ZOOM_64_OCEAN, // 1.13+
|
||||
L_ZOOM_32_OCEAN, L13_ZOOM_32 = L_ZOOM_32_OCEAN, // 1.13+
|
||||
L_ZOOM_16_OCEAN, L13_ZOOM_16 = L_ZOOM_16_OCEAN, // 1.13+
|
||||
L_ZOOM_8_OCEAN, L13_ZOOM_8 = L_ZOOM_8_OCEAN, // 1.13+
|
||||
L_ZOOM_4_OCEAN, L13_ZOOM_4 = L_ZOOM_4_OCEAN, // 1.13+
|
||||
L_OCEAN_MIX_4, L13_OCEAN_MIX_4 = L_OCEAN_MIX_4, // 1.13+
|
||||
int mc;
|
||||
int dim;
|
||||
uint32_t flags;
|
||||
uint64_t seed;
|
||||
uint64_t sha;
|
||||
|
||||
L_VORONOI_1, L_VORONOI_ZOOM_1 = L_VORONOI_1,
|
||||
|
||||
// largeBiomes layers
|
||||
L_ZOOM_LARGE_A,
|
||||
L_ZOOM_LARGE_B,
|
||||
L_ZOOM_L_RIVER_A,
|
||||
L_ZOOM_L_RIVER_B,
|
||||
|
||||
L_NUM
|
||||
};
|
||||
|
||||
|
||||
STRUCT(LayerStack)
|
||||
{
|
||||
Layer layers[L_NUM];
|
||||
Layer *entry_1; // entry scale (1:1) [L_VORONOI_1]
|
||||
Layer *entry_4; // entry scale (1:4) [L_RIVER_MIX_4|L_OCEAN_MIX_4]
|
||||
// unofficial entries for other scales (latest sensible layers):
|
||||
Layer *entry_16; // [L_SWAMP_RIVER_16|L_SHORE_16]
|
||||
Layer *entry_64; // [L_HILLS_64|L_SUNFLOWER_64]
|
||||
Layer *entry_256; // [L_BIOME_256|L_BAMBOO_256]
|
||||
PerlinNoise oceanRnd;
|
||||
union {
|
||||
struct { // MC 1.0 - 1.17
|
||||
LayerStack ls;
|
||||
Layer xlayer[5]; // buffer for custom entry layers @{1,4,16,64,256}
|
||||
Layer *entry;
|
||||
NetherNoise nn; // MC 1.16
|
||||
};
|
||||
struct { // MC 1.18
|
||||
BiomeNoise bn;
|
||||
};
|
||||
};
|
||||
EndNoise en; // MC 1.9
|
||||
};
|
||||
|
||||
|
||||
@ -93,31 +36,82 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Initialise an instance of a generator. */
|
||||
void setupGenerator(LayerStack *g, int mc);
|
||||
///=============================================================================
|
||||
/// Biome Generation
|
||||
///=============================================================================
|
||||
|
||||
/* Initialise an instance of a generator with largeBiomes configuration. */
|
||||
void setupGeneratorLargeBiomes(LayerStack *g, int mc, int largeBiomes);
|
||||
/**
|
||||
* Sets up a biome generator for a given MC version. The 'flags' can be used to
|
||||
* control LARGE_BIOMES or to FORCE_OCEAN_VARIANTS to enable ocean variants at
|
||||
* scales higher than normal.
|
||||
*/
|
||||
void setupGenerator(Generator *g, int mc, uint32_t flags);
|
||||
|
||||
/**
|
||||
* Initializes the generator dimension using a given seed.
|
||||
*
|
||||
* dim=0: Overworld
|
||||
* dim=-1: Nether
|
||||
* dim=+1: End
|
||||
*/
|
||||
void applySeed(Generator *g, int dim, uint64_t seed);
|
||||
|
||||
/**
|
||||
* Calculates the buffer size (number of ints) required to generate a cuboidal
|
||||
* volume of size (sx, sy, sz). If 'sy' is zero the buffer is calculated for a
|
||||
* 2D plane (which is equivalent to sy=1 here).
|
||||
* The function allocCache() can be used to allocate the corresponding int
|
||||
* buffer using malloc().
|
||||
*/
|
||||
size_t getMinCacheSize(const Generator *g, int scale, int sx, int sy, int sz);
|
||||
int *allocCache(const Generator *g, Range r);
|
||||
|
||||
/**
|
||||
* Generates the biomes for a cuboidal scaled range given by 'r'.
|
||||
* (See description of Range for more detail.)
|
||||
*
|
||||
* The output is generated inside the cache. Upon success the biome ids can be
|
||||
* accessed by indexing as:
|
||||
* cache[ y*r.sx*r.sz + z*r.sx + x ]
|
||||
* where (x,y,z) is an relative position inside the range cuboid.
|
||||
*
|
||||
* The required length of the cache can be determined with getMinCacheSize().
|
||||
*
|
||||
* The return value is zero upon success.
|
||||
*/
|
||||
int genBiomes(const Generator *g, int *cache, Range r);
|
||||
/**
|
||||
* Gets the biome for a specified scaled position. Note that the scale should
|
||||
* be either 1 or 4, for block or biome coordinates respectively.
|
||||
* Returns none (-1) upon failure.
|
||||
*/
|
||||
int getBiomeAt(const Generator *g, int scale, int x, int y, int z);
|
||||
|
||||
/**
|
||||
* Returns the default layer that corresponds to the given scale.
|
||||
* Supported scales are {0, 1, 4, 16, 64, 256}. A scale of zero indicates the
|
||||
* custom entry layer 'g->entry'.
|
||||
* (Overworld, MC <= 1.17)
|
||||
*/
|
||||
const Layer *getLayerForScale(const Generator *g, int scale);
|
||||
|
||||
|
||||
///=============================================================================
|
||||
/// Layered Biome Generation (interface up to 1.17)
|
||||
///=============================================================================
|
||||
|
||||
/* Initialize an instance of a layered generator. */
|
||||
void setupLayerStack(LayerStack *g, int mc, int largeBiomes);
|
||||
|
||||
/* Calculates the minimum size of the buffers required to generate an area of
|
||||
* dimensions 'sizeX' by 'sizeZ' at the specified layer.
|
||||
*/
|
||||
size_t calcRequiredBuf(const Layer *layer, int areaX, int areaZ);
|
||||
|
||||
/* Allocates an amount of memory required to generate an area of dimensions
|
||||
* 'sizeX' by 'sizeZ' for the magnification of the given layer.
|
||||
*/
|
||||
int *allocCache(const Layer *layer, int sizeX, int sizeZ);
|
||||
|
||||
size_t getMinLayerCacheSize(const Layer *layer, int sizeX, int sizeZ);
|
||||
|
||||
/* Set up custom layers. */
|
||||
Layer *setupLayer(LayerStack *g, int layerId, mapfunc_t *map, int mc,
|
||||
Layer *setupLayer(Layer *l, mapfunc_t *map, int mc,
|
||||
int8_t zoom, int8_t edge, uint64_t saltbase, Layer *p, Layer *p2);
|
||||
|
||||
/* Sets the world seed for the generator */
|
||||
void applySeed(LayerStack *g, uint64_t seed);
|
||||
|
||||
/* Generates the specified area using the current generator settings and stores
|
||||
* the biomeIDs in 'out'.
|
||||
* The biomeIDs will be indexed in the form: out[x + z*areaWidth]
|
||||
@ -127,21 +121,6 @@ void applySeed(LayerStack *g, uint64_t seed);
|
||||
int genArea(const Layer *layer, int *out, int areaX, int areaZ, int areaWidth, int areaHeight);
|
||||
|
||||
|
||||
/* Generate nether or end biomes at scales: 1:1, 1:4, 1:16, or 1:64
|
||||
* @mc minecaft version
|
||||
* @seed world seed
|
||||
* @scale mapping scale of output, has to be one of 1, 4, 16, or 64
|
||||
* @out output buffer, out[yi*w*h + zi*w + xi], size = w*h*(y1-y0+1)
|
||||
* for voronoi (scale=1) add 7 to each dimension as buffer
|
||||
* @x,z,w,h planar area
|
||||
* @y0,y1 min and max vertical dimensions (inclusive)
|
||||
* @return zero upon success
|
||||
*/
|
||||
int genNetherScaled(int mc, uint64_t seed, int scale, int *out,
|
||||
int x, int z, int w, int h, int y0, int y1);
|
||||
int genEndScaled(int mc, uint64_t seed, int scale, int *out,
|
||||
int x, int z, int w, int h);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
361
layers.h
361
layers.h
@ -1,29 +1,8 @@
|
||||
#ifndef LAYER_H_
|
||||
#define LAYER_H_
|
||||
|
||||
#include "javarnd.h"
|
||||
#include "noise.h"
|
||||
|
||||
#define __STDC_FORMAT_MACROS 1
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void*)0)
|
||||
#endif
|
||||
|
||||
#define STRUCT(S) typedef struct S S; struct S
|
||||
|
||||
#if __GNUC__
|
||||
#define PREFETCH(PTR,RW,LOC) __builtin_prefetch(PTR,RW,LOC)
|
||||
#define L(COND) (__builtin_expect(!!(COND),1)) // [[likely]]
|
||||
#define U(COND) (__builtin_expect((COND),0)) // [[unlikely]]
|
||||
#else
|
||||
#define PREFETCH(PTR,RW,LOC)
|
||||
#define L(COND) (COND)
|
||||
#define U(COND) (COND)
|
||||
#endif
|
||||
|
||||
#define LAYER_INIT_SHA (~0ULL)
|
||||
|
||||
@ -34,8 +13,8 @@ enum MCversion
|
||||
MC_1_0, // <=1.0 Experimental!
|
||||
MC_1_1, MC_1_2, MC_1_3, MC_1_4, MC_1_5, MC_1_6,
|
||||
MC_1_7, MC_1_8, MC_1_9, MC_1_10, MC_1_11, MC_1_12,
|
||||
MC_1_13, MC_1_14, MC_1_15, MC_1_16, MC_1_17,
|
||||
MC_NEWEST = MC_1_17,
|
||||
MC_1_13, MC_1_14, MC_1_15, MC_1_16, MC_1_17, MC_1_18,
|
||||
MC_NEWEST = MC_1_18,
|
||||
};
|
||||
|
||||
enum BiomeID
|
||||
@ -135,6 +114,24 @@ enum BiomeID
|
||||
// 1.17
|
||||
dripstone_caves = 174,
|
||||
lush_caves = 175,
|
||||
// 1.18
|
||||
meadow = 177,
|
||||
grove = 178,
|
||||
snowy_slopes = 179,
|
||||
jagged_peaks = 180,
|
||||
frozen_peaks = 181,
|
||||
stony_peaks = 182,
|
||||
old_growth_birch_forest = tall_birch_forest,
|
||||
old_growth_pine_taiga = giant_tree_taiga,
|
||||
old_growth_spruce_taiga = giant_spruce_taiga,
|
||||
snowy_plains = snowy_tundra,
|
||||
sparse_jungle = jungle_edge,
|
||||
stony_shore = stone_shore,
|
||||
windswept_hills = mountains,
|
||||
windswept_forest = wooded_mountains,
|
||||
windswept_gravelly_hills = gravelly_mountains,
|
||||
windswept_savanna = shattered_savanna,
|
||||
wooded_badlands = wooded_badlands_plateau,
|
||||
};
|
||||
|
||||
|
||||
@ -144,26 +141,118 @@ enum BiomeTempCategory
|
||||
};
|
||||
|
||||
|
||||
STRUCT(PerlinNoise)
|
||||
/* Enumeration of the layer indices in the layer stack. */
|
||||
enum LayerId
|
||||
{
|
||||
int d[512];
|
||||
double a, b, c;
|
||||
// new [[deprecated]]
|
||||
L_CONTINENT_4096 = 0, L_ISLAND_4096 = L_CONTINENT_4096,
|
||||
L_ZOOM_2048,
|
||||
L_LAND_2048, L_ADD_ISLAND_2048 = L_LAND_2048,
|
||||
L_ZOOM_1024,
|
||||
L_LAND_1024_A, L_ADD_ISLAND_1024A = L_LAND_1024_A,
|
||||
L_LAND_1024_B, L_ADD_ISLAND_1024B = L_LAND_1024_B, // 1.7+
|
||||
L_LAND_1024_C, L_ADD_ISLAND_1024C = L_LAND_1024_C, // 1.7+
|
||||
L_ISLAND_1024, L_REMOVE_OCEAN_1024 = L_ISLAND_1024, // 1.7+
|
||||
L_SNOW_1024, L_ADD_SNOW_1024 = L_SNOW_1024,
|
||||
L_LAND_1024_D, L_ADD_ISLAND_1024D = L_LAND_1024_D, // 1.7+
|
||||
L_COOL_1024, L_COOL_WARM_1024 = L_COOL_1024, // 1.7+
|
||||
L_HEAT_1024, L_HEAT_ICE_1024 = L_HEAT_1024, // 1.7+
|
||||
L_SPECIAL_1024, // 1.7+
|
||||
L_ZOOM_512,
|
||||
L_LAND_512, // 1.6-
|
||||
L_ZOOM_256,
|
||||
L_LAND_256, L_ADD_ISLAND_256 = L_LAND_256,
|
||||
L_MUSHROOM_256, L_ADD_MUSHROOM_256 = L_MUSHROOM_256,
|
||||
L_DEEP_OCEAN_256, // 1.7+
|
||||
L_BIOME_256,
|
||||
L_BAMBOO_256, L14_BAMBOO_256 = L_BAMBOO_256, // 1.14+
|
||||
L_ZOOM_128,
|
||||
L_ZOOM_64,
|
||||
L_BIOME_EDGE_64,
|
||||
L_NOISE_256, L_RIVER_INIT_256 = L_NOISE_256,
|
||||
L_ZOOM_128_HILLS,
|
||||
L_ZOOM_64_HILLS,
|
||||
L_HILLS_64,
|
||||
L_SUNFLOWER_64, L_RARE_BIOME_64 = L_SUNFLOWER_64, // 1.7+
|
||||
L_ZOOM_32,
|
||||
L_LAND_32, L_ADD_ISLAND_32 = L_LAND_32,
|
||||
L_ZOOM_16,
|
||||
L_SHORE_16, // NOTE: in 1.0 this slot is scale 1:32
|
||||
L_SWAMP_RIVER_16, // 1.6-
|
||||
L_ZOOM_8,
|
||||
L_ZOOM_4,
|
||||
L_SMOOTH_4,
|
||||
L_ZOOM_128_RIVER,
|
||||
L_ZOOM_64_RIVER,
|
||||
L_ZOOM_32_RIVER,
|
||||
L_ZOOM_16_RIVER,
|
||||
L_ZOOM_8_RIVER,
|
||||
L_ZOOM_4_RIVER,
|
||||
L_RIVER_4,
|
||||
L_SMOOTH_4_RIVER,
|
||||
L_RIVER_MIX_4,
|
||||
L_OCEAN_TEMP_256, L13_OCEAN_TEMP_256 = L_OCEAN_TEMP_256, // 1.13+
|
||||
L_ZOOM_128_OCEAN, L13_ZOOM_128 = L_ZOOM_128_OCEAN, // 1.13+
|
||||
L_ZOOM_64_OCEAN, L13_ZOOM_64 = L_ZOOM_64_OCEAN, // 1.13+
|
||||
L_ZOOM_32_OCEAN, L13_ZOOM_32 = L_ZOOM_32_OCEAN, // 1.13+
|
||||
L_ZOOM_16_OCEAN, L13_ZOOM_16 = L_ZOOM_16_OCEAN, // 1.13+
|
||||
L_ZOOM_8_OCEAN, L13_ZOOM_8 = L_ZOOM_8_OCEAN, // 1.13+
|
||||
L_ZOOM_4_OCEAN, L13_ZOOM_4 = L_ZOOM_4_OCEAN, // 1.13+
|
||||
L_OCEAN_MIX_4, L13_OCEAN_MIX_4 = L_OCEAN_MIX_4, // 1.13+
|
||||
|
||||
L_VORONOI_1, L_VORONOI_ZOOM_1 = L_VORONOI_1,
|
||||
|
||||
// largeBiomes layers
|
||||
L_ZOOM_LARGE_A,
|
||||
L_ZOOM_LARGE_B,
|
||||
L_ZOOM_L_RIVER_A,
|
||||
L_ZOOM_L_RIVER_B,
|
||||
|
||||
L_NUM
|
||||
};
|
||||
|
||||
STRUCT(OctaveNoise)
|
||||
|
||||
STRUCT(Range)
|
||||
{
|
||||
double lacuna;
|
||||
double persist;
|
||||
int octcnt;
|
||||
PerlinNoise *octaves;
|
||||
// Cuboidal range, given by a position, size and scaling in the horizontal
|
||||
// axes, used to define a generation range. The parameters for the vertical
|
||||
// control can be left at zero when dealing with versions without 3D volume
|
||||
// support. The vertical scaling is equal to 1:1 iff scale == 1, and 1:4
|
||||
// (default biome scale) in all other cases!
|
||||
//
|
||||
// @scale: Horizontal scale factor, should be one of 1, 4, 16, 64, or 256
|
||||
// additionally a value of zero bypasses scaling and expects a
|
||||
// manual generation entry layer.
|
||||
// @x,z: Horizontal position, i.e. coordinates of north-west corner.
|
||||
// @sx,sz: Horizontal size (width and height for 2D), should be positive.
|
||||
// @y Vertical position, 1:1 iff scale==1, 1:4 otherwise.
|
||||
// @sy Vertical size. Values <= 0 are treated equivalent to 1.
|
||||
//
|
||||
// Volumes generated with a range are generally indexed as:
|
||||
// out [ i_y*sx*sz + i_z*sx + i_x ]
|
||||
// where i_x, i_y, i_z are indecies in their respective directions.
|
||||
//
|
||||
// EXAMPLES
|
||||
// Area at normal biome scale (1:4):
|
||||
// Range r_2d = {4, x,z, sx,sz};
|
||||
// (C99 syntax allows ommission of the trailing zero-initialization.)
|
||||
//
|
||||
// Area at block scale (1:1) at sea level:
|
||||
// Range r_surf = {1, x,z, sx,sz, 63};
|
||||
// (Block level scale uses voronoi sampling with 1:1 vertical scaling.)
|
||||
//
|
||||
// Area at chunk scale (1:16) near sea level:
|
||||
// Range r_surf16 = {16, x,z, sx,sz, 15};
|
||||
// (Note that the vertical scaling is always 1:4 for non-voronoi scales.)
|
||||
//
|
||||
// Volume at scale (1:4):
|
||||
// Range r_vol = {4, x,z, sx,sz, y,sy};
|
||||
|
||||
int scale;
|
||||
int x, z, sx, sz;
|
||||
int y, sy;
|
||||
};
|
||||
|
||||
STRUCT(DoublePerlinNoise)
|
||||
{
|
||||
double amplitude;
|
||||
OctaveNoise octA;
|
||||
OctaveNoise octB;
|
||||
};
|
||||
|
||||
struct Layer;
|
||||
typedef int (mapfunc_t)(const struct Layer *, int *, int, int, int, int);
|
||||
@ -187,15 +276,29 @@ STRUCT(Layer)
|
||||
Layer *p, *p2; // parent layers
|
||||
};
|
||||
|
||||
STRUCT(NetherNoise)
|
||||
// Overworld biome generator up to 1.17
|
||||
STRUCT(LayerStack)
|
||||
{
|
||||
// altitude and wierdness don't affect nether biomes
|
||||
Layer layers[L_NUM];
|
||||
Layer *entry_1; // entry scale (1:1) [L_VORONOI_1]
|
||||
Layer *entry_4; // entry scale (1:4) [L_RIVER_MIX_4|L_OCEAN_MIX_4]
|
||||
// unofficial entries for other scales (latest sensible layers):
|
||||
Layer *entry_16; // [L_SWAMP_RIVER_16|L_SHORE_16]
|
||||
Layer *entry_64; // [L_HILLS_64|L_SUNFLOWER_64]
|
||||
Layer *entry_256; // [L_BIOME_256|L_BAMBOO_256]
|
||||
PerlinNoise oceanRnd;
|
||||
};
|
||||
|
||||
// Nether biome generator 1.16-1.17
|
||||
STRUCT(NetherNoise)
|
||||
{ // altitude and wierdness don't affect nether biomes
|
||||
// and the weight is a 5th noise parameter which is constant
|
||||
DoublePerlinNoise temperature;
|
||||
DoublePerlinNoise humidity;
|
||||
PerlinNoise oct[8]; // buffer for octaves in double perlin noise
|
||||
};
|
||||
|
||||
// End biome generator 1.9+
|
||||
typedef PerlinNoise EndNoise;
|
||||
|
||||
STRUCT(SurfaceNoise)
|
||||
@ -208,6 +311,43 @@ STRUCT(SurfaceNoise)
|
||||
PerlinNoise oct[16+16+8];
|
||||
};
|
||||
|
||||
STRUCT(Spline)
|
||||
{
|
||||
int len, typ;
|
||||
float loc[12];
|
||||
float der[12];
|
||||
Spline *val[12];
|
||||
};
|
||||
|
||||
STRUCT(FixSpline)
|
||||
{
|
||||
int len;
|
||||
float val;
|
||||
};
|
||||
|
||||
STRUCT(SplineStack)
|
||||
{ // the stack size here is just sufficient for overworld generation
|
||||
Spline stack[42];
|
||||
FixSpline fstack[151];
|
||||
int len, flen;
|
||||
};
|
||||
|
||||
/// Overworld and Nether biome generator for 1.18
|
||||
STRUCT(BiomeNoise)
|
||||
{
|
||||
DoublePerlinNoise shift;
|
||||
DoublePerlinNoise temperature;
|
||||
DoublePerlinNoise humidity;
|
||||
DoublePerlinNoise continentalness;
|
||||
DoublePerlinNoise erosion;
|
||||
DoublePerlinNoise weirdness;
|
||||
PerlinNoise oct[2*23]; // buffer for octaves in double perlin noise
|
||||
Spline *sp;
|
||||
SplineStack ss;
|
||||
int previdx;
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
@ -227,20 +367,6 @@ void setLayerSeed(Layer *layer, uint64_t worldSeed);
|
||||
// Noise
|
||||
//==============================================================================
|
||||
|
||||
void perlinInit(PerlinNoise *rnd, uint64_t *seed);
|
||||
double samplePerlin(const PerlinNoise *rnd, double x, double y, double z,
|
||||
double yamp, double ymin);
|
||||
double sampleSimplex2D(const PerlinNoise *rnd, double x, double y);
|
||||
|
||||
void octaveInit(OctaveNoise *rnd, uint64_t *seed, PerlinNoise *octaves,
|
||||
int omin, int len);
|
||||
double sampleOctave(const OctaveNoise *rnd, double x, double y, double z);
|
||||
|
||||
void doublePerlinInit(DoublePerlinNoise *rnd, uint64_t *seed,
|
||||
PerlinNoise *octavesA, PerlinNoise *octavesB, int omin, int len);
|
||||
double sampleDoublePerlin(const DoublePerlinNoise *rnd,
|
||||
double x, double y, double z);
|
||||
|
||||
void initSurfaceNoise(SurfaceNoise *rnd, uint64_t *seed,
|
||||
double xzScale, double yScale, double xzFactor, double yFactor);
|
||||
void initSurfaceNoiseEnd(SurfaceNoise *rnd, uint64_t seed);
|
||||
@ -248,10 +374,11 @@ double sampleSurfaceNoise(const SurfaceNoise *rnd, int x, int y, int z);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// Nether (1.16+) and End (1.9+) Biome Generation
|
||||
// End (1.9+), Nether (1.16+) and Overworld (1.18+) Biome Noise Generation
|
||||
//==============================================================================
|
||||
|
||||
/**
|
||||
* Nether generation (1.16-1.17)
|
||||
* Nether biomes are 3D, and generated at scale 1:4. Use voronoiAccess3D() to
|
||||
* convert coordinates at 1:1 scale to their 1:4 access. Biome checks for
|
||||
* structures are generally done at y=0.
|
||||
@ -263,101 +390,64 @@ double sampleSurfaceNoise(const SurfaceNoise *rnd, int x, int y, int z);
|
||||
* Use mapNether2D() to get a 2D area of nether biomes at y=0, scale 1:4.
|
||||
*
|
||||
* The mapNether3D() function attempts to optimize the generation of a volume
|
||||
* at scale 1:4, ranging from (x,y,z) to (x+w, y+yh, z+h) [exclusive] and the
|
||||
* output is indexed with out[y_k*(w*h) + z_j*w + x_i]. If the optimization
|
||||
* parameter 'confidence' has a value less than 1.0, the generation will
|
||||
* generally be faster, but can yield incorrect results in some circumstances.
|
||||
* at scale 1:4. The output is indexed as:
|
||||
* out[i_y*(r.sx*r.sz) + i_z*r.sx + i_x].
|
||||
* If the optimization parameter 'confidence' has a value less than 1.0, the
|
||||
* generation will generally be faster, but can yield incorrect results in some
|
||||
* circumstances.
|
||||
*
|
||||
* The output buffer for the map-functions need only be of sufficient size to
|
||||
* hold the generated area (i.e. w*h or w*h*yh).
|
||||
* hold the generated area (i.e. w*h or r.sx*r.sy*r.sz).
|
||||
*/
|
||||
void setNetherSeed(NetherNoise *nn, uint64_t seed);
|
||||
int getNetherBiome(const NetherNoise *nn, int x, int y, int z, float *ndel);
|
||||
int mapNether2D(const NetherNoise *nn, int *out, int x, int z, int w, int h);
|
||||
int mapNether3D(const NetherNoise *nn, int *out, int x, int z, int w, int h,
|
||||
int y, int yh, int scale, float confidence);
|
||||
int mapNether3D(const NetherNoise *nn, int *out, Range r, float confidence);
|
||||
/**
|
||||
* The scaled Nether generation supports scales 1, 4, 16, 64, and 256.
|
||||
* It is similar to mapNether3D(), but applies voronoi zoom if necessary, and
|
||||
* fills the output buffer with nether_wastes for versions older than 1.16.
|
||||
*/
|
||||
int genNetherScaled(const NetherNoise *nn, int *out, Range r, int mc, uint64_t sha);
|
||||
|
||||
/**
|
||||
* End biome generation is based on simplex noise and varies only at a 1:16
|
||||
* chunk scale which can be generated with mapEndBiome(). The function mapEnd()
|
||||
* is a variation which also scales this up on a regular grid to 1:4. The final
|
||||
* access at a 1:1 scale is the standard voronoi layer.
|
||||
* access at a 1:1 scale uses voronoi.
|
||||
*/
|
||||
void setEndSeed(EndNoise *en, uint64_t seed);
|
||||
int mapEndBiome(const EndNoise *en, int *out, int x, int z, int w, int h);
|
||||
int mapEnd(const EndNoise *en, int *out, int x, int z, int w, int h);
|
||||
int getSurfaceHeightEnd(int mc, uint64_t seed, int x, int z);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// Seed Helpers
|
||||
//==============================================================================
|
||||
/**
|
||||
* The scaled End generation supports scales 1, 4, 16, and 64.
|
||||
* The End biomes are usually 2D, but in 1.15+ there is 3D voronoi noise, which
|
||||
* is controlled by the 'sha' hash of the seed. For scales higher than 1:1, and
|
||||
* versions up to 1.14, 'sha' is ignored.
|
||||
*/
|
||||
int genEndScaled(const EndNoise *en, int *out, Range r, int mc, uint64_t sha);
|
||||
|
||||
/**
|
||||
* The seed pipeline:
|
||||
* In 1.18 the Overworld and Nether use a new noise map system for the biome
|
||||
* generation. The random number generation has also updated to a Xiroshiro128
|
||||
* algorithm. The scale is 1:4, and is sampled at each point individually as
|
||||
* there is currently not much benefit from generating a volume as a whole.
|
||||
*
|
||||
* getLayerSalt(n) -> layerSalt (ls)
|
||||
* layerSalt (ls), worldSeed (ws) -> startSalt (st), startSeed (ss)
|
||||
* startSeed (ss), coords (x,z) -> chunkSeed (cs)
|
||||
*
|
||||
* The chunkSeed alone is enough to generate the first PRNG integer with:
|
||||
* mcFirstInt(cs, mod)
|
||||
* subsequent PRNG integers are generated by stepping the chunkSeed forwards,
|
||||
* salted with startSalt:
|
||||
* cs_next = mcStepSeed(cs, st)
|
||||
* The 1.18 End generation remains similar to 1.17 and does NOT use the
|
||||
* biome noise.
|
||||
*/
|
||||
|
||||
static inline uint64_t mcStepSeed(uint64_t s, uint64_t salt)
|
||||
{
|
||||
return s * (s * 6364136223846793005ULL + 1442695040888963407ULL) + salt;
|
||||
}
|
||||
|
||||
static inline int mcFirstInt(uint64_t s, int mod)
|
||||
{
|
||||
int ret = (int)(((int64_t)s >> 24) % mod);
|
||||
if (ret < 0)
|
||||
ret += mod;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int mcFirstIsZero(uint64_t s, int mod)
|
||||
{
|
||||
return (int)(((int64_t)s >> 24) % mod) == 0;
|
||||
}
|
||||
|
||||
static inline uint64_t getChunkSeed(uint64_t ss, int x, int z)
|
||||
{
|
||||
uint64_t cs = ss + x;
|
||||
cs = mcStepSeed(cs, z);
|
||||
cs = mcStepSeed(cs, x);
|
||||
cs = mcStepSeed(cs, z);
|
||||
return cs;
|
||||
}
|
||||
|
||||
static inline uint64_t getLayerSalt(uint64_t salt)
|
||||
{
|
||||
uint64_t ls = mcStepSeed(salt, salt);
|
||||
ls = mcStepSeed(ls, salt);
|
||||
ls = mcStepSeed(ls, salt);
|
||||
return ls;
|
||||
}
|
||||
|
||||
static inline uint64_t getStartSalt(uint64_t ws, uint64_t ls)
|
||||
{
|
||||
uint64_t st = ws;
|
||||
st = mcStepSeed(st, ls);
|
||||
st = mcStepSeed(st, ls);
|
||||
st = mcStepSeed(st, ls);
|
||||
return st;
|
||||
}
|
||||
|
||||
static inline uint64_t getStartSeed(uint64_t ws, uint64_t ls)
|
||||
{
|
||||
uint64_t ss = ws;
|
||||
ss = getStartSalt(ss, ls);
|
||||
ss = mcStepSeed(ss, 0);
|
||||
return ss;
|
||||
}
|
||||
void initBiomeNoise(BiomeNoise *bn, int mc);
|
||||
void setBiomeSeed(BiomeNoise *bn, uint64_t seed, int dim, int large);
|
||||
int sampleBiomeNoise(const BiomeNoise *bn, int x, int y, int z, int dim,
|
||||
uint64_t *dat);
|
||||
/**
|
||||
* The scaled biome noise generation applies for the Overworld and Nether for
|
||||
* version 1.18. The 'sha' hash of the seed is only required for voronoi at
|
||||
* scale 1:1. A scale of zero is interpreted as the default 1:4 scale.
|
||||
*/
|
||||
int genBiomeNoiseScaled(const BiomeNoise *bn, int *out, Range r, int dim,
|
||||
int mc, uint64_t sha);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
@ -416,9 +506,18 @@ mapfunc_t mapVoronoi114;
|
||||
// Biome generation now stops at scale 1:4 OceanMix and voronoi is just an
|
||||
// access algorithm, mapping the 1:1 scale onto its 1:4 correspondent.
|
||||
// It is seeded by the first 8-bytes of the SHA-256 hash of the world seed.
|
||||
uint64_t getVoronoiSHA(uint64_t worldSeed) __attribute__((const));
|
||||
ATTR(const)
|
||||
uint64_t getVoronoiSHA(uint64_t worldSeed);
|
||||
void voronoiAccess3D(uint64_t sha, int x, int y, int z, int *x4, int *y4, int *z4);
|
||||
|
||||
// Gets the range in the parent/source layer which may be accessed by voronoi.
|
||||
Range getVoronoiSrcRange(Range r);
|
||||
|
||||
// Applies a 2D voronoi mapping at height 'y' to a 'src' plane, where
|
||||
// src_range [px,pz,pw,ph] -> out_range [x,z,w,h] have to match the scaling.
|
||||
void mapVoronoiPlane(uint64_t sha, int *out, int *src,
|
||||
int x, int z, int w, int h, int y, int px, int pz, int pw, int ph);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
10
makefile
10
makefile
@ -18,7 +18,7 @@ all: release
|
||||
|
||||
debug: CFLAGS += -DDEBUG -O0 -ggdb3
|
||||
debug: libcubiomes
|
||||
release: CFLAGS += -O3
|
||||
release: CFLAGS += -O3 -g3
|
||||
release: libcubiomes
|
||||
native: CFLAGS += -O3 -march=native
|
||||
native: libcubiomes
|
||||
@ -27,7 +27,7 @@ ifeq ($(OS),Windows_NT)
|
||||
else
|
||||
libcubiomes: CFLAGS += -fPIC
|
||||
endif
|
||||
libcubiomes: layers.o generator.o finders.o util.o
|
||||
libcubiomes: noise.o biome_tree.o layers.o generator.o finders.o util.o
|
||||
$(AR) $(ARFLAGS) libcubiomes.a $^
|
||||
|
||||
|
||||
@ -40,6 +40,12 @@ generator.o: generator.c generator.h
|
||||
layers.o: layers.c layers.h
|
||||
$(CC) -c $(CFLAGS) $<
|
||||
|
||||
biome_tree.o: biome_tree.c
|
||||
$(CC) -c $(CFLAGS) $<
|
||||
|
||||
noise.o: noise.c noise.h
|
||||
$(CC) -c $(CFLAGS) $<
|
||||
|
||||
util.o: util.c util.h
|
||||
$(CC) -c $(CFLAGS) $<
|
||||
|
||||
|
348
noise.c
Normal file
348
noise.c
Normal file
@ -0,0 +1,348 @@
|
||||
|
||||
#include "noise.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
double maintainPrecision(double x)
|
||||
{
|
||||
return x - floor(x / 33554432.0 + 0.5) * 33554432.0;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
#if __GNUC__
|
||||
__builtin_unreachable();
|
||||
#endif
|
||||
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;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
noise->d[i] = i;
|
||||
}
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
int j = nextInt(seed, 256 - i) + i;
|
||||
uint8_t n = noise->d[i];
|
||||
noise->d[i] = noise->d[j];
|
||||
noise->d[j] = n;
|
||||
noise->d[i + 256] = noise->d[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;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
noise->d[i] = i;
|
||||
}
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
int j = xNextInt(xr, 256 - i) + i;
|
||||
uint8_t n = noise->d[i];
|
||||
noise->d[i] = noise->d[j];
|
||||
noise->d[j] = n;
|
||||
noise->d[i + 256] = noise->d[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;
|
||||
int i1 = (int)d1 - (int)(d1 < 0);
|
||||
int i2 = (int)d2 - (int)(d2 < 0);
|
||||
int i3 = (int)d3 - (int)(d3 < 0);
|
||||
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 = noise->d[i1] + i2;
|
||||
int a2 = noise->d[a1] + i3;
|
||||
int a3 = noise->d[a1+1] + i3;
|
||||
int b1 = noise->d[i1+1] + i2;
|
||||
int b2 = noise->d[b1] + i3;
|
||||
int b3 = noise->d[b1+1] + i3;
|
||||
|
||||
double l1 = indexedLerp(noise->d[a2], d1, d2, d3);
|
||||
double l2 = indexedLerp(noise->d[b2], d1-1, d2, d3);
|
||||
double l3 = indexedLerp(noise->d[a3], d1, d2-1, d3);
|
||||
double l4 = indexedLerp(noise->d[b3], d1-1, d2-1, d3);
|
||||
double l5 = indexedLerp(noise->d[a2+1], d1, d2, d3-1);
|
||||
double l6 = indexedLerp(noise->d[b2+1], d1-1, d2, d3-1);
|
||||
double l7 = indexedLerp(noise->d[a3+1], d1, d2-1, d3-1);
|
||||
double l8 = indexedLerp(noise->d[b3+1], d1-1, d2-1, d3-1);
|
||||
|
||||
l1 = lerp(t1, l1, l2);
|
||||
l3 = lerp(t1, l3, l4);
|
||||
l5 = lerp(t1, l5, l6);
|
||||
l7 = lerp(t1, l7, l8);
|
||||
|
||||
l1 = lerp(t2, l1, l3);
|
||||
l5 = lerp(t2, l5, l7);
|
||||
|
||||
return lerp(t3, l1, l5);
|
||||
}
|
||||
|
||||
static double simplexGrad(int idx, double x, double y, double z, double d)
|
||||
{
|
||||
double con = d - x*x - y*y - z*z;
|
||||
if (con < 0)
|
||||
return 0;
|
||||
con *= con;
|
||||
return con * con * indexedLerp(idx, x, y, z);
|
||||
}
|
||||
|
||||
double sampleSimplex2D(const PerlinNoise *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;
|
||||
}
|
||||
|
||||
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"
|
||||
};
|
||||
|
||||
int i = 0, n = 0;
|
||||
double lacuna = pow(2.0, omin);
|
||||
double persist = pow(2.0, len-1) / ((1LL << len) - 1.0);
|
||||
uint64_t xlo = xNextLong(xr);
|
||||
uint64_t xhi = xNextLong(xr);
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
68
noise.h
Normal file
68
noise.h
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef NOISE_H_
|
||||
#define NOISE_H_
|
||||
|
||||
#include "rng.h"
|
||||
|
||||
STRUCT(PerlinNoise)
|
||||
{
|
||||
uint8_t d[512];
|
||||
double a, b, c;
|
||||
double amplitude;
|
||||
double lacunarity;
|
||||
};
|
||||
|
||||
STRUCT(OctaveNoise)
|
||||
{
|
||||
int octcnt;
|
||||
PerlinNoise *octaves;
|
||||
};
|
||||
|
||||
STRUCT(DoublePerlinNoise)
|
||||
{
|
||||
double amplitude;
|
||||
OctaveNoise octA;
|
||||
OctaveNoise octB;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/// Helper
|
||||
double maintainPrecision(double x);
|
||||
|
||||
/// Perlin noise
|
||||
void perlinInit(PerlinNoise *noise, uint64_t *seed);
|
||||
void xPerlinInit(PerlinNoise *noise, Xoroshiro *xr);
|
||||
|
||||
double samplePerlin(const PerlinNoise *noise, double x, double y, double z,
|
||||
double yamp, double ymin);
|
||||
double sampleSimplex2D(const PerlinNoise *noise, double x, double y);
|
||||
|
||||
/// Perlin Octaves
|
||||
void octaveInit(OctaveNoise *noise, uint64_t *seed, PerlinNoise *octaves,
|
||||
int omin, int len);
|
||||
int xOctaveInit(OctaveNoise *noise, Xoroshiro *xr, PerlinNoise *octaves,
|
||||
const double *amplitudes, int omin, int len);
|
||||
|
||||
double sampleOctave(const OctaveNoise *noise, double x, double y, double z);
|
||||
|
||||
/// Double Perlin
|
||||
void doublePerlinInit(DoublePerlinNoise *noise, uint64_t *seed,
|
||||
PerlinNoise *octavesA, PerlinNoise *octavesB, int omin, int len);
|
||||
int xDoublePerlinInit(DoublePerlinNoise *noise, Xoroshiro *xr,
|
||||
PerlinNoise *octaves, const double *amplitudes, int omin, int len);
|
||||
|
||||
double sampleDoublePerlin(const DoublePerlinNoise *noise,
|
||||
double x, double y, double z);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NOISE_H_ */
|
||||
|
||||
|
||||
|
350
rng.h
Normal file
350
rng.h
Normal file
@ -0,0 +1,350 @@
|
||||
#ifndef RNG_H_
|
||||
#define RNG_H_
|
||||
|
||||
#define __STDC_FORMAT_MACROS 1
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
///=============================================================================
|
||||
/// Compiler and Platform Features
|
||||
///=============================================================================
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void*)0)
|
||||
#endif
|
||||
|
||||
#define STRUCT(S) typedef struct S S; struct S
|
||||
|
||||
#if __GNUC__
|
||||
#define IABS(X) __builtin_abs(X)
|
||||
#define PREFETCH(PTR,RW,LOC) __builtin_prefetch(PTR,RW,LOC)
|
||||
#define L(COND) (__builtin_expect(!!(COND),1)) // [[likely]]
|
||||
#define U(COND) (__builtin_expect((COND),0)) // [[unlikely]
|
||||
#define ATTR(...) __attribute__((__VA_ARGS__))
|
||||
#define BSWAP32(X) __builtin_bswap32(X)
|
||||
#else
|
||||
#define IABS(X) ((int)abs(X))
|
||||
#define PREFETCH(PTR,RW,LOC)
|
||||
#define L(COND) (COND)
|
||||
#define U(COND) (COND)
|
||||
#define ATTR(...)
|
||||
static inline uint32_t BSWAP32(uint32_t x) {
|
||||
x = ((x & 0x000000ff) << 24) | ((x & 0x0000ff00) << 8) |
|
||||
((x & 0x00ff0000) >> 8) | ((x & 0xff000000) >> 24);
|
||||
return x;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// imitate amd64/x64 rotate instructions
|
||||
|
||||
static inline ATTR(const, always_inline, artificial)
|
||||
uint64_t rotl64(uint64_t x, uint8_t b)
|
||||
{
|
||||
return (x << b) | (x >> (64-b));
|
||||
}
|
||||
|
||||
static inline ATTR(const, always_inline, artificial)
|
||||
uint32_t rotr32(uint32_t a, uint8_t b)
|
||||
{
|
||||
return (a >> b) | (a << (32-b));
|
||||
}
|
||||
|
||||
|
||||
///=============================================================================
|
||||
/// C implementation of Java Random
|
||||
///=============================================================================
|
||||
|
||||
static inline void setSeed(uint64_t *seed, uint64_t value)
|
||||
{
|
||||
*seed = (value ^ 0x5deece66d) & ((1ULL << 48) - 1);
|
||||
}
|
||||
|
||||
static inline int next(uint64_t *seed, const int bits)
|
||||
{
|
||||
*seed = (*seed * 0x5deece66d + 0xb) & ((1ULL << 48) - 1);
|
||||
return (int) ((int64_t)*seed >> (48 - bits));
|
||||
}
|
||||
|
||||
static inline int nextInt(uint64_t *seed, const int n)
|
||||
{
|
||||
int bits, val;
|
||||
const int m = n - 1;
|
||||
|
||||
if ((m & n) == 0) {
|
||||
uint64_t x = n * (uint64_t)next(seed, 31);
|
||||
return (int) ((int64_t) x >> 31);
|
||||
}
|
||||
|
||||
do {
|
||||
bits = next(seed, 31);
|
||||
val = bits % n;
|
||||
}
|
||||
while (bits - val + m < 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline uint64_t nextLong(uint64_t *seed)
|
||||
{
|
||||
return ((uint64_t) next(seed, 32) << 32) + next(seed, 32);
|
||||
}
|
||||
|
||||
static inline float nextFloat(uint64_t *seed)
|
||||
{
|
||||
return next(seed, 24) / (float) (1 << 24);
|
||||
}
|
||||
|
||||
static inline double nextDouble(uint64_t *seed)
|
||||
{
|
||||
uint64_t x = (uint64_t)next(seed, 26);
|
||||
x <<= 27;
|
||||
x += next(seed, 27);
|
||||
return (int64_t) x / (double) (1ULL << 53);
|
||||
}
|
||||
|
||||
/* A macro to generate the ideal assembly for X = nextInt(S, 24)
|
||||
* This is a macro and not an inline function, as many compilers can make use
|
||||
* of the additional optimisation passes for the surrounding code.
|
||||
*/
|
||||
#define JAVA_NEXT_INT24(S,X) \
|
||||
do { \
|
||||
uint64_t a = (1ULL << 48) - 1; \
|
||||
uint64_t c = 0x5deece66dULL * (S); \
|
||||
c += 11; a &= c; \
|
||||
(S) = a; \
|
||||
a = (uint64_t) ((int64_t)a >> 17); \
|
||||
c = 0xaaaaaaab * a; \
|
||||
c = (uint64_t) ((int64_t)c >> 36); \
|
||||
(X) = (int)a - (int)(c << 3) * 3; \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* Jumps forwards in the random number sequence by simulating 'n' calls to next.
|
||||
*/
|
||||
static inline void skipNextN(uint64_t *seed, uint64_t n)
|
||||
{
|
||||
uint64_t m = 1;
|
||||
uint64_t a = 0;
|
||||
uint64_t im = 0x5deece66dULL;
|
||||
uint64_t ia = 0xb;
|
||||
uint64_t k;
|
||||
|
||||
for (k = n; k; k >>= 1)
|
||||
{
|
||||
if (k & 1)
|
||||
{
|
||||
m *= im;
|
||||
a = im * a + ia;
|
||||
}
|
||||
ia = (im + 1) * ia;
|
||||
im *= im;
|
||||
}
|
||||
|
||||
*seed = *seed * m + a;
|
||||
*seed &= 0xffffffffffffULL;
|
||||
}
|
||||
|
||||
|
||||
///=============================================================================
|
||||
/// Xoroshiro 128
|
||||
///=============================================================================
|
||||
|
||||
STRUCT(Xoroshiro)
|
||||
{
|
||||
uint64_t lo, hi;
|
||||
};
|
||||
|
||||
static inline void xSetSeed(Xoroshiro *xr, uint64_t value)
|
||||
{
|
||||
const uint64_t XL = 0x9e3779b97f4a7c15ULL;
|
||||
const uint64_t XH = 0x6a09e667f3bcc909ULL;
|
||||
const uint64_t A = 0xbf58476d1ce4e5b9ULL;
|
||||
const uint64_t B = 0x94d049bb133111ebULL;
|
||||
uint64_t l = value ^ XH;
|
||||
uint64_t h = l + XL;
|
||||
l = (l ^ (l >> 30)) * A;
|
||||
h = (h ^ (h >> 30)) * A;
|
||||
l = (l ^ (l >> 27)) * B;
|
||||
h = (h ^ (h >> 27)) * B;
|
||||
l = l ^ (l >> 31);
|
||||
h = h ^ (h >> 31);
|
||||
xr->lo = l;
|
||||
xr->hi = h;
|
||||
}
|
||||
|
||||
static inline uint64_t xNextLong(Xoroshiro *xr)
|
||||
{
|
||||
uint64_t l = xr->lo;
|
||||
uint64_t h = xr->hi;
|
||||
uint64_t n = rotl64(l + h, 17) + l;
|
||||
h ^= l;
|
||||
xr->lo = rotl64(l, 49) ^ h ^ (h << 21);
|
||||
xr->hi = rotl64(h, 28);
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline int xNextInt(Xoroshiro *xr, uint32_t n)
|
||||
{
|
||||
uint64_t r = (xNextLong(xr) & 0xFFFFFFFF) * n;
|
||||
if ((uint32_t)r < n)
|
||||
{
|
||||
while ((uint32_t)r < (~n + 1) % n)
|
||||
{
|
||||
r = (xNextLong(xr) & 0xFFFFFFFF) * n;
|
||||
}
|
||||
}
|
||||
return r >> 32;
|
||||
}
|
||||
|
||||
static inline double xNextDouble(Xoroshiro *xr)
|
||||
{
|
||||
return (xNextLong(xr) >> (64-53)) * 1.1102230246251565E-16;
|
||||
}
|
||||
|
||||
static inline void xSkipN(Xoroshiro *xr, int count)
|
||||
{
|
||||
while (count --> 0)
|
||||
xNextLong(xr);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// MC Seed Helpers
|
||||
//==============================================================================
|
||||
|
||||
/**
|
||||
* The seed pipeline:
|
||||
*
|
||||
* getLayerSalt(n) -> layerSalt (ls)
|
||||
* layerSalt (ls), worldSeed (ws) -> startSalt (st), startSeed (ss)
|
||||
* startSeed (ss), coords (x,z) -> chunkSeed (cs)
|
||||
*
|
||||
* The chunkSeed alone is enough to generate the first PRNG integer with:
|
||||
* mcFirstInt(cs, mod)
|
||||
* subsequent PRNG integers are generated by stepping the chunkSeed forwards,
|
||||
* salted with startSalt:
|
||||
* cs_next = mcStepSeed(cs, st)
|
||||
*/
|
||||
|
||||
static inline uint64_t mcStepSeed(uint64_t s, uint64_t salt)
|
||||
{
|
||||
return s * (s * 6364136223846793005ULL + 1442695040888963407ULL) + salt;
|
||||
}
|
||||
|
||||
static inline int mcFirstInt(uint64_t s, int mod)
|
||||
{
|
||||
int ret = (int)(((int64_t)s >> 24) % mod);
|
||||
if (ret < 0)
|
||||
ret += mod;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int mcFirstIsZero(uint64_t s, int mod)
|
||||
{
|
||||
return (int)(((int64_t)s >> 24) % mod) == 0;
|
||||
}
|
||||
|
||||
static inline uint64_t getChunkSeed(uint64_t ss, int x, int z)
|
||||
{
|
||||
uint64_t cs = ss + x;
|
||||
cs = mcStepSeed(cs, z);
|
||||
cs = mcStepSeed(cs, x);
|
||||
cs = mcStepSeed(cs, z);
|
||||
return cs;
|
||||
}
|
||||
|
||||
static inline uint64_t getLayerSalt(uint64_t salt)
|
||||
{
|
||||
uint64_t ls = mcStepSeed(salt, salt);
|
||||
ls = mcStepSeed(ls, salt);
|
||||
ls = mcStepSeed(ls, salt);
|
||||
return ls;
|
||||
}
|
||||
|
||||
static inline uint64_t getStartSalt(uint64_t ws, uint64_t ls)
|
||||
{
|
||||
uint64_t st = ws;
|
||||
st = mcStepSeed(st, ls);
|
||||
st = mcStepSeed(st, ls);
|
||||
st = mcStepSeed(st, ls);
|
||||
return st;
|
||||
}
|
||||
|
||||
static inline uint64_t getStartSeed(uint64_t ws, uint64_t ls)
|
||||
{
|
||||
uint64_t ss = ws;
|
||||
ss = getStartSalt(ss, ls);
|
||||
ss = mcStepSeed(ss, 0);
|
||||
return ss;
|
||||
}
|
||||
|
||||
|
||||
///============================================================================
|
||||
/// Arithmatic
|
||||
///============================================================================
|
||||
|
||||
|
||||
/* Linear interpolations
|
||||
*/
|
||||
static inline double lerp(double part, double from, double to)
|
||||
{
|
||||
return from + part * (to - from);
|
||||
}
|
||||
|
||||
static inline double lerp2(
|
||||
double dx, double dy, double v00, double v10, double v01, double v11)
|
||||
{
|
||||
return lerp(dy, lerp(dx, v00, v10), lerp(dx, v01, v11));
|
||||
}
|
||||
|
||||
static inline double lerp3(
|
||||
double dx, double dy, double dz,
|
||||
double v000, double v100, double v010, double v110,
|
||||
double v001, double v101, double v011, double v111)
|
||||
{
|
||||
v000 = lerp2(dx, dy, v000, v100, v010, v110);
|
||||
v001 = lerp2(dx, dy, v001, v101, v011, v111);
|
||||
return lerp(dz, v000, v001);
|
||||
}
|
||||
|
||||
static inline double clampedLerp(double part, double from, double to)
|
||||
{
|
||||
if (part <= 0) return from;
|
||||
if (part >= 1) return to;
|
||||
return lerp(part, from, to);
|
||||
}
|
||||
|
||||
/* Find the modular inverse: (1/x) | mod m.
|
||||
* Assumes x and m are positive (less than 2^63), co-prime.
|
||||
*/
|
||||
static inline ATTR(const)
|
||||
uint64_t mulInv(uint64_t x, uint64_t m)
|
||||
{
|
||||
uint64_t t, q, a, b, n;
|
||||
if ((int64_t)m <= 1)
|
||||
return 0; // no solution
|
||||
|
||||
n = m;
|
||||
a = 0; b = 1;
|
||||
|
||||
while ((int64_t)x > 1)
|
||||
{
|
||||
if (m == 0)
|
||||
return 0; // x and m are co-prime
|
||||
q = x / m;
|
||||
t = m; m = x % m; x = t;
|
||||
t = a; a = b - q * a; b = t;
|
||||
}
|
||||
|
||||
if ((int64_t)b < 0)
|
||||
b += n;
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
#endif /* RNG_H_ */
|
||||
|
||||
|
||||
|
161
tests.c
161
tests.c
@ -1,6 +1,7 @@
|
||||
#include "finders.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <float.h>
|
||||
#include <stdlib.h>
|
||||
@ -68,17 +69,13 @@ benchmark(int64_t (*f)(int64_t n, void*), void *dat, double *tmin, double *tavg)
|
||||
return cnt;
|
||||
}
|
||||
|
||||
uint32_t getRef(int mc, int bits, const char *path)
|
||||
uint32_t getRef(int mc, int dim, int bits, int spread, const char *path)
|
||||
{
|
||||
initBiomes();
|
||||
LayerStack g;
|
||||
setupGenerator(&g, mc);
|
||||
Layer *l = g.entry_4;
|
||||
int *ids = allocCache(l, 1, 1);
|
||||
Generator g;
|
||||
setupGenerator(&g, mc, 0);
|
||||
|
||||
FILE *fp = NULL;
|
||||
if (path)
|
||||
fp = fopen(path, "w");
|
||||
if (path) fp = fopen(path, "w");
|
||||
|
||||
int r = 1 << (bits-1);
|
||||
int h = 0;
|
||||
@ -88,22 +85,21 @@ uint32_t getRef(int mc, int bits, const char *path)
|
||||
for (z = -r; z < r; z++)
|
||||
{
|
||||
int64_t s = (int64_t)( (z << bits) ^ x );
|
||||
setLayerSeed(l, s);
|
||||
//applySeed(&g, s);
|
||||
genArea(l, ids, x, z, 1, 1);
|
||||
h ^= hash32( (int) s ^ (ids[0] << 2*bits) );
|
||||
|
||||
applySeed(&g, dim, s);
|
||||
int y = (int)((hash32((int)s) & 0x7fffffff) % 384 - 64) >> 2;
|
||||
int id = getBiomeAt(&g, 4, x, y, z);
|
||||
h ^= hash32( (int) s ^ (id << 2*bits) );
|
||||
if (fp)
|
||||
fprintf(fp, "%5d%6d%4d\n", x, z, ids[0]);
|
||||
//fprintf(fp, "%5d%6d%4d\n", x*spread, z*spread, id);
|
||||
fprintf(fp, "%2ld @ %2d %4d %2d - %4d %08x\n", s, x, y, z, id, h);
|
||||
}
|
||||
}
|
||||
if (fp)
|
||||
if (fp && fp != stdout)
|
||||
fclose(fp);
|
||||
free(ids);
|
||||
return h;
|
||||
}
|
||||
|
||||
int testBiomeGen1x1(const int *mc, const uint32_t *expect, int bits, int cnt)
|
||||
int testBiomeGen1x1(const int *mc, const uint32_t *expect, int dim, int bits, int spread, int cnt)
|
||||
{
|
||||
int test;
|
||||
uint32_t h;
|
||||
@ -111,12 +107,12 @@ int testBiomeGen1x1(const int *mc, const uint32_t *expect, int bits, int cnt)
|
||||
|
||||
for (test = 0; test < cnt; test++)
|
||||
{
|
||||
printf(" [%*d/%*d] MC 1.%-2d: expecting %08x ... ",
|
||||
1+(cnt>9), test+1, 1+(cnt>9),cnt, mc[test], expect[test]);
|
||||
printf(" [%*d/%*d] MC 1.%-2d dim=%-2d: expecting %08x ... ",
|
||||
1+(cnt>9), test+1, 1+(cnt>9), cnt, mc[test], dim, expect[test]);
|
||||
fflush(stdout);
|
||||
|
||||
double t = -now();
|
||||
h = getRef(mc[test], bits, NULL);
|
||||
h = getRef(mc[test], dim, bits, spread, "t16");
|
||||
t += now();
|
||||
printf("got %08x %s\e[0m (%ld msec)\n",
|
||||
h, h == expect[test] ? "\e[1;92mOK" : "\e[1;91mFAILED",
|
||||
@ -127,90 +123,81 @@ int testBiomeGen1x1(const int *mc, const uint32_t *expect, int bits, int cnt)
|
||||
return ok;
|
||||
}
|
||||
|
||||
int testOverworldBiomes(int thorough)
|
||||
|
||||
uint32_t testAreas(int mc, int dim, int scale)
|
||||
{
|
||||
Generator g;
|
||||
setupGenerator(&g, mc, 0);
|
||||
|
||||
double t = -now();
|
||||
uint32_t hash = 0;
|
||||
uint64_t s;
|
||||
for (s = 0; s < 100; s++)
|
||||
{
|
||||
int d = 10000;
|
||||
int x = hash32(s << 5) % d - d/2;
|
||||
int y = ((int)(hash32(s << 7) % 384) - 64);
|
||||
int z = hash32(s << 9) % d - d/2;
|
||||
int w = 1 + hash32(s << 11) % 128; w = 128;
|
||||
int h = 1 + hash32(s << 13) % 128; h = 128;
|
||||
|
||||
applySeed(&g, dim, s);
|
||||
Range r = {scale, x, z, w, h, y, 1};
|
||||
int *ids = allocCache(&g, r);
|
||||
genBiomes(&g, ids, r);
|
||||
int i = 0;
|
||||
hash = 0;
|
||||
for (i = 0; i < w*h; i++)
|
||||
hash = hash32(hash ^ hash32(ids[i] + (i << 17)));
|
||||
free(ids);
|
||||
}
|
||||
t += now();
|
||||
printf(" MC 1.%-2d dim %-2d @ 1:%-3d - %08x [%ld msec]\n",
|
||||
mc, dim, scale, hash, (long)(t*1e3));
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
int testGeneration()
|
||||
{
|
||||
const int mc_vers[] = {
|
||||
MC_1_18,
|
||||
MC_1_16, MC_1_15, MC_1_13, MC_1_12, MC_1_9, MC_1_7,
|
||||
MC_1_6, MC_1_2, MC_1_1, MC_1_0,
|
||||
MC_1_6, MC_1_2, MC_1_1, MC_1_0,
|
||||
};
|
||||
const uint32_t b6_hashes[] = {
|
||||
0xade7f891,
|
||||
0xde9a6574, 0x3a568a6d, 0x96c97323, 0xbc75e996, 0xe27a45a2, 0xbc75e996,
|
||||
0x15b47206, 0x2d7e0fed, 0x5cbf4709, 0xbd794adb,
|
||||
};
|
||||
const uint32_t b10_hashes[] = {
|
||||
0xfdede71d, 0xca8005d7, 0x399f7cc8, 0xb3363967, 0x17e5592f, 0xb3363967,
|
||||
0xa52e377c, 0xdb1df71d, 0x58e86947, 0xe1e89cc3,
|
||||
};
|
||||
const int testcnt = sizeof(mc_vers) / sizeof(int);
|
||||
|
||||
printf("Testing 1x1 biome generation (quick):\n");
|
||||
if (!testBiomeGen1x1(mc_vers, b6_hashes, 6, testcnt))
|
||||
return -1;
|
||||
if (!thorough)
|
||||
return 0;
|
||||
printf("Testing 1x1 biome generation (thorough):\n");
|
||||
if (!testBiomeGen1x1(mc_vers, b10_hashes, 10, testcnt))
|
||||
return -1;
|
||||
if (!testBiomeGen1x1(mc_vers, b6_hashes, 0, 6, 1, 1))// testcnt))
|
||||
;//return -1;
|
||||
|
||||
printf("Area generation tests:\n");
|
||||
testAreas(MC_1_18, 0, 1);
|
||||
testAreas(MC_1_18, 0, 4);
|
||||
testAreas(MC_1_18, 0, 16);
|
||||
testAreas(MC_1_18, 0, 64);
|
||||
|
||||
//const uint32_t b10_hashes[] = {
|
||||
// 0x00000000,
|
||||
// 0xfdede71d, 0xca8005d7, 0x399f7cc8, 0xb3363967, 0x17e5592f, 0xb3363967,
|
||||
// 0xa52e377c, 0xdb1df71d, 0x58e86947, 0xe1e89cc3,
|
||||
//};
|
||||
//printf("Testing 1x1 biome generation (thorough):\n");
|
||||
//if (!testBiomeGen1x1(mc_vers, b10_hashes, 0, 10, 1, testcnt))
|
||||
// return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int gw = 16, gh = 16;
|
||||
|
||||
int64_t testPerfEnd(int64_t n, void *data)
|
||||
{
|
||||
EndNoise *en = (EndNoise*) data;
|
||||
int ids[gw*gh];
|
||||
int64_t r = 0;
|
||||
|
||||
while (n-->0)
|
||||
{
|
||||
int x = rand() % 10000 - 5000;
|
||||
int z = rand() % 10000 - 5000;
|
||||
mapEndBiome(en, ids, x, z, gw, gh);
|
||||
r ^= ids[0];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int64_t testPerfNether(int64_t n, void *data)
|
||||
{
|
||||
NetherNoise *nn = (NetherNoise*) data;
|
||||
int ids[gw*gh];
|
||||
int64_t r = 0;
|
||||
|
||||
while (n-->0)
|
||||
{
|
||||
int x = rand() % 10000 - 5000;
|
||||
int z = rand() % 10000 - 5000;
|
||||
mapNether2D(nn, ids, x, z, gw, gh);
|
||||
r ^= ids[0];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int testPerformance()
|
||||
{
|
||||
double tmin, tavg;
|
||||
|
||||
EndNoise en;
|
||||
setEndSeed(&en, 12345);
|
||||
benchmark(testPerfEnd, &en, &tmin, &tavg);
|
||||
printf("End %dx%d -> min:%10.0lf ns | avg:%10.0lf ns | conf:%4.2lf %%\n",
|
||||
gw, gh, 1e9*tmin, 1e9*tavg, 100 * (tavg-tmin) / (tavg+tmin));
|
||||
|
||||
NetherNoise nn;
|
||||
setNetherSeed(&nn, 12345);
|
||||
benchmark(testPerfNether, &nn, &tmin, &tavg);
|
||||
printf("Nether %dx%d -> min:%10.0lf ns | avg:%10.0lf ns | conf:%4.2lf %%\n",
|
||||
gw, gh, 1e9*tmin, 1e9*tavg, 100 * (tavg-tmin) / (tavg+tmin));
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
testOverworldBiomes(0);
|
||||
//testPerformance();
|
||||
testGeneration();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
26
util.c
26
util.c
@ -27,12 +27,14 @@ const char* mc2str(int mc)
|
||||
case MC_1_15: return "1.15"; break;
|
||||
case MC_1_16: return "1.16"; break;
|
||||
case MC_1_17: return "1.17"; break;
|
||||
case MC_1_18: return "1.18"; break;
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int str2mc(const char *s)
|
||||
{
|
||||
if (!strcmp(s, "1.18")) return MC_1_18;
|
||||
if (!strcmp(s, "1.17")) return MC_1_17;
|
||||
if (!strcmp(s, "1.16")) return MC_1_16;
|
||||
if (!strcmp(s, "1.15")) return MC_1_15;
|
||||
@ -151,6 +153,13 @@ const char *biome2str(int id)
|
||||
// 1.17
|
||||
case dripstone_caves: return "dripstone_caves";
|
||||
case lush_caves: return "lush_caves";
|
||||
// 1.18
|
||||
case meadow: return "meadow";
|
||||
case grove: return "grove";
|
||||
case snowy_slopes: return "snowy_slopes";
|
||||
case stony_peaks: return "stony_peaks";
|
||||
case jagged_peaks: return "jagged_peaks";
|
||||
case frozen_peaks: return "frozen_peaks";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -265,8 +274,15 @@ void initBiomeColors(unsigned char biomeColors[256][3])
|
||||
setBiomeColor(biomeColors, warped_forest, 73, 144, 123);
|
||||
setBiomeColor(biomeColors, basalt_deltas, 100, 95, 99);
|
||||
|
||||
setBiomeColor(biomeColors, dripstone_caves, 149, 127, 104); // TBD
|
||||
setBiomeColor(biomeColors, lush_caves, 109, 143, 62); // TBD
|
||||
setBiomeColor(biomeColors, dripstone_caves, 78, 48, 18); // TBD
|
||||
setBiomeColor(biomeColors, lush_caves, 40, 60, 0); // TBD
|
||||
|
||||
setBiomeColor(biomeColors, meadow, 96, 164, 69); // TBD
|
||||
setBiomeColor(biomeColors, grove, 71, 114, 108); // TBD
|
||||
setBiomeColor(biomeColors, snowy_slopes, 196, 196, 196); // TBD
|
||||
setBiomeColor(biomeColors, stony_peaks, 123, 143, 116); // TBD
|
||||
setBiomeColor(biomeColors, jagged_peaks, 220, 220, 200); // TBD
|
||||
setBiomeColor(biomeColors, frozen_peaks, 176, 179, 206); // TBD
|
||||
}
|
||||
|
||||
void initBiomeTypeColors(unsigned char biomeColors[256][3])
|
||||
@ -315,11 +331,11 @@ int biomesToImage(unsigned char *pixels,
|
||||
for (m = 0; m < pixscale; m++) {
|
||||
for (n = 0; n < pixscale; n++) {
|
||||
int idx = pixscale * i + n;
|
||||
if (flip)
|
||||
if (flip)
|
||||
idx += (sx * pixscale) * ((pixscale * j) + m);
|
||||
else
|
||||
else
|
||||
idx += (sx * pixscale) * ((pixscale * (sy-1-j)) + m);
|
||||
|
||||
|
||||
unsigned char *pix = pixels + 3*idx;
|
||||
pix[0] = (unsigned char)r;
|
||||
pix[1] = (unsigned char)g;
|
||||
|
2
util.h
2
util.h
@ -13,7 +13,7 @@ const char *biome2str(int id);
|
||||
void initBiomeColors(unsigned char biomeColors[256][3]);
|
||||
void initBiomeTypeColors(unsigned char biomeColors[256][3]);
|
||||
|
||||
int biomesToImage(unsigned char *pixels,
|
||||
int biomesToImage(unsigned char *pixels,
|
||||
unsigned char biomeColors[256][3], const int *biomes,
|
||||
const unsigned int sx, const unsigned int sy,
|
||||
const unsigned int pixscale, const int flip);
|
||||
|
Loading…
Reference in New Issue
Block a user