Support for 1.21 + trial chambers + end surface height

* much faster surface height generation for the end dimension
* added 1.21 to versions
* added trial chambers
* renamed trail_ruin to trail_ruins to match its id string
This commit is contained in:
Cubitect 2024-06-02 22:12:34 +02:00
parent ae58fc4983
commit d68faefd65
8 changed files with 249 additions and 28 deletions

View File

@ -51,6 +51,62 @@ void initSurfaceNoiseBeta(SurfaceNoiseBeta *snb, uint64_t seed)
octaveInitBeta(&snb->octcontB, &s, snb->oct+50, 16, 200.0, 0.5, 1.0, 2.0);
}
double sampleSurfaceNoiseBetween(const SurfaceNoise *sn, int x, int y, int z,
double noiseMin, double noiseMax)
{
double persist, amp;
double dx, dy, dz, sy;
int i;
double xzScale = 684.412 * sn->xzScale;
double yScale = 684.412 * sn->yScale;
double vmin = 0;
double vmax = 0;
persist = 1.0 / 32768.0;
amp = 64.0;
for (i = 15; i >= 0; i--)
{
dx = x * xzScale * persist;
dz = z * xzScale * persist;
sy = yScale * persist;
dy = y * sy;
vmin += samplePerlin(&sn->octmin.octaves[i], dx, dy, dz, sy, dy) * amp;
if (vmin - amp > noiseMax) return noiseMax;
vmax += samplePerlin(&sn->octmax.octaves[i], dx, dy, dz, sy, dy) * amp;
if (vmax + amp < noiseMin) return noiseMin;
amp *= 0.5;
persist *= 2.0;
}
double xzStep = xzScale / sn->xzFactor;
double yStep = yScale / sn->yFactor;
double vmain = 0.5;
persist = 1.0 / 128.0;
amp = 0.05 * 128.0;
for (i = 7; i >= 0; i--)
{
dx = x * xzStep * persist;
dz = z * xzStep * persist;
sy = yStep * persist;
dy = y * sy;
vmain += samplePerlin(&sn->octmain.octaves[i], dx, dy, dz, sy, dy) * amp;
if (vmain - amp > 1) return vmax;
if (vmain + amp < 0) return vmin;
amp *= 0.5;
persist *= 2.0;
}
return clampedLerp(vmain, vmin, vmax);
}
double sampleSurfaceNoise(const SurfaceNoise *sn, int x, int y, int z)
{
double xzScale = 684.412 * sn->xzScale;
@ -62,6 +118,7 @@ double sampleSurfaceNoise(const SurfaceNoise *sn, int x, int y, int z)
double maxNoise = 0;
double mainNoise = 0;
double persist = 1.0;
double contrib = 1.0;
double dx, dy, dz, sy, ty;
int i;
@ -73,8 +130,8 @@ double sampleSurfaceNoise(const SurfaceNoise *sn, int x, int y, int z)
sy = yScale * persist;
ty = y * sy;
minNoise += samplePerlin(&sn->octmin.octaves[i], dx, dy, dz, sy, ty) / persist;
maxNoise += samplePerlin(&sn->octmax.octaves[i], dx, dy, dz, sy, ty) / persist;
minNoise += samplePerlin(&sn->octmin.octaves[i], dx, dy, dz, sy, ty) * contrib;
maxNoise += samplePerlin(&sn->octmax.octaves[i], dx, dy, dz, sy, ty) * contrib;
if (i < 8)
{
@ -83,15 +140,15 @@ double sampleSurfaceNoise(const SurfaceNoise *sn, int x, int y, int z)
dz = maintainPrecision(z * xzStep * persist);
sy = yStep * persist;
ty = y * sy;
mainNoise += samplePerlin(&sn->octmain.octaves[i], dx, dy, dz, sy, ty) / persist;
mainNoise += samplePerlin(&sn->octmain.octaves[i], dx, dy, dz, sy, ty) * contrib;
}
persist /= 2.0;
persist *= 0.5;
contrib *= 2.0;
}
return clampedLerp(0.5 + 0.05*mainNoise, minNoise/512.0, maxNoise/512.0);
}
//==============================================================================
// Nether (1.16+) and End (1.9+) Biome Generation
//==============================================================================
@ -489,6 +546,10 @@ float getEndHeightNoise(const EndNoise *en, int x, int z, int range)
return ret;
}
#define END_NOISE_COL_YMIN 2
#define END_NOISE_COL_YMAX 18
#define END_NOISE_COL_SIZE (END_NOISE_COL_YMAX - END_NOISE_COL_YMIN + 1)
void sampleNoiseColumnEnd(double column[], const SurfaceNoise *sn,
const EndNoise *en, int x, int z, int colymin, int colymax)
{
@ -505,6 +566,55 @@ void sampleNoiseColumnEnd(double column[], const SurfaceNoise *sn,
}
}
void sampleNoiseColumnEndFull(double column[END_NOISE_COL_SIZE],
const SurfaceNoise *sn, const EndNoise *en, int x, int z)
{
// clamped (32 + 46 - y) / 64.0
static const double upper_drop[] = {
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 0-7
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 63./64, // 8-15
62./64, 61./64, 60./64, 59./64, 58./64, 57./64, 56./64, 55./64, // 16-23
54./64, 53./64, 52./64, 51./64, 50./64, 49./64, 48./64, 47./64, // 24-31
46./64 // 32
};
// clamped (y - 1) / 7.0
static const double lower_drop[] = {
0.0, 0.0, 1./7, 2./7, 3./7, 4./7, 5./7, 6./7, // 0-7
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 8-15
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 16-23
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 24-31
1.0 // 32
};
// depth is between [-108, +72]
// noise is between [-128, +128]
// for a sold block we need the upper drop as:
// (72 + 128) * u - 3000 * (1-u) > 0 => upper_drop = u < 15/16
// which occurs at y = 18 for the highest relevant noise cell
// for the lower drop we need:
// (72 + 128) * l - 30 * (1-l) > 0 => lower_drop = l > 3/23
// which occurs at y = 3 for the lowest relevant noise cell
// in terms of the depth this becomes:
// l > 30 / (103 + depth)
double depth = getEndHeightNoise(en, x, z, 0) - 8.0f;
int y;
for (y = END_NOISE_COL_YMIN; y <= END_NOISE_COL_YMAX; y++)
{
if (lower_drop[y] * (103.0 + depth) < 30)
{
column[y - END_NOISE_COL_YMIN] = -30;
continue;
}
double noise = sampleSurfaceNoiseBetween(sn, x, y, z, -128, 128);
double clamped = noise + depth;
clamped = lerp(upper_drop[y], -3000, clamped);
clamped = lerp(lower_drop[y], -30, clamped);
column[y - END_NOISE_COL_YMIN] = clamped;
}
}
/* Given bordering noise columns and a fractional position between those,
* determine the surface block height (i.e. where the interpolated noise > 0).
* Note that the noise columns should be of size: ncolxz[ colymax-colymin+1 ]
@ -568,6 +678,59 @@ int getSurfaceHeightEnd(int mc, uint64_t seed, int x, int z)
return getSurfaceHeight(ncol00, ncol01, ncol10, ncol11, y0, y1, 4, dx, dz);
}
int mapSurfaceHeightEnd(const EndNoise *en, const SurfaceNoise *sn, float *y,
int x, int z, int w, int h, int scale)
{
if (scale != 1 && scale != 2 && scale != 4 && scale != 8)
return 1;
enum { YSIZ = END_NOISE_COL_SIZE };
double cellmid = scale > 1 ? scale / 16.0 : 0;
int cellsiz = 8 / scale;
int cx = floordiv(x, cellsiz);
int cz = floordiv(z, cellsiz);
int cw = floordiv(x + w - 1, cellsiz) - cx + 2;
int i, j;
double *buf = malloc(sizeof(double) * YSIZ * cw * 2);
double *ncol[2];
ncol[0] = buf;
ncol[1] = buf + YSIZ * cw;
for (i = 0; i < cw; i++)
sampleNoiseColumnEndFull(ncol[1]+i*YSIZ, sn, en, cx+i, cz+0);
for (j = 0; j < h; j++)
{
int cj = floordiv(z + j, cellsiz);
int dj = z + j - cj * cellsiz;
if (j == 0 || dj == 0)
{
double *tmp = ncol[0];
ncol[0] = ncol[1];
ncol[1] = tmp;
for (i = 0; i < cw; i++)
sampleNoiseColumnEndFull(ncol[1]+i*YSIZ, sn, en, cx+i, cj+1);
}
for (i = 0; i < w; i++)
{
int ci = floordiv(x + i, cellsiz);
int di = x + i - ci * cellsiz;
double dx = di / (double) cellsiz + cellmid;
double dz = dj / (double) cellsiz + cellmid;
double *ncol0 = ncol[0] + (ci - cx) * YSIZ;
double *ncol1 = ncol[1] + (ci - cx) * YSIZ;
y[j*w+i] = getSurfaceHeight(ncol0, ncol1, ncol0+YSIZ, ncol1+YSIZ,
END_NOISE_COL_YMIN, END_NOISE_COL_YMAX, 4, dx, dz);
}
}
free(buf);
return 0;
}
int genEndScaled(const EndNoise *en, int *out, Range r, int mc, uint64_t sha)
{
if (mc < MC_1_0)

View File

@ -223,6 +223,9 @@ void setEndSeed(EndNoise *en, int mc, 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);
int mapSurfaceHeightEnd(const EndNoise *en, const SurfaceNoise *sn, float *y,
int x, int z, int w, int h, int scale);
/**
* 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

View File

@ -32,7 +32,8 @@ enum MCVersion
MC_1_19_2,
MC_1_19, // 1.19.3 - 1.19.4
MC_1_20,
MC_NEWEST = MC_1_20,
MC_1_21,
MC_NEWEST = MC_1_21,
};
enum Dimension

View File

@ -19,5 +19,8 @@ BiomeTree g_btree[MC_NEWEST - MC_1_18 + 1] =
// MC_1_20
{ btree20_steps, &btree20_param[0][0], btree20_nodes, btree20_order,
sizeof(btree20_nodes) / sizeof(uint64_t) },
// MC_1_21
{ btree20_steps, &btree20_param[0][0], btree20_nodes, btree20_order,
sizeof(btree20_nodes) / sizeof(uint64_t) },
};

View File

@ -83,7 +83,8 @@ int getStructureConfig(int structureType, int mc, StructureConfig *sconf)
s_ruined_portal_n = { 34222645, 40, 25, Ruined_Portal, STRUCT_NETHER,0},
s_ruined_portal_n_117 = { 34222645, 25, 15, Ruined_Portal_N, STRUCT_NETHER,0},
s_ancient_city = { 20083232, 24, 16, Ancient_City, 0,0},
s_trail_ruin = { 83469867, 34, 26, Trail_Ruin, 0,0},
s_trail_ruins = { 83469867, 34, 26, Trail_Ruins, 0,0},
s_trial_chambers = { 94251327, 34, 22, Trial_Chambers, 0,0},
s_treasure = { 10387320, 1, 1, Treasure, STRUCT_CHUNK,0},
s_mineshaft = { 0, 1, 1, Mineshaft, STRUCT_CHUNK,0},
s_desert_well_115 = { 30010, 1, 1, Desert_Well, STRUCT_CHUNK, 1.f/1000},
@ -100,7 +101,9 @@ int getStructureConfig(int structureType, int mc, StructureConfig *sconf)
s_end_gateway_115 = { 30000, 1, 1, End_Gateway, STRUCT_END|STRUCT_CHUNK, 700},
s_end_gateway_116 = { 40013, 1, 1, End_Gateway, STRUCT_END|STRUCT_CHUNK, 700},
s_end_gateway_117 = { 40013, 1, 1, End_Gateway, STRUCT_END|STRUCT_CHUNK, 1.f/700},
s_end_gateway = { 40000, 1, 1, End_Gateway, STRUCT_END|STRUCT_CHUNK, 1.f/700}
s_end_gateway = { 40000, 1, 1, End_Gateway, STRUCT_END|STRUCT_CHUNK, 1.f/700},
s_end_island_115 = { 0, 1, 1, End_Island, STRUCT_END|STRUCT_CHUNK, 14},
s_end_island = { 0, 1, 1, End_Island, STRUCT_END|STRUCT_CHUNK, 1.f/14}
;
switch (structureType)
@ -170,6 +173,10 @@ int getStructureConfig(int structureType, int mc, StructureConfig *sconf)
// 1.11 and 1.12 generate gateways using a random source that passed
// the block filling, making them much more difficult to predict
return mc >= MC_1_13;
case End_Island:
if (mc <= MC_1_15) *sconf = s_end_island_115;
else *sconf = s_end_island;
return mc >= MC_1_13; // we only support decorator features for 1.13+
case Desert_Well:
if (mc <= MC_1_15) *sconf = s_desert_well_115;
else if (mc <= MC_1_17) *sconf = s_desert_well_117;
@ -180,9 +187,12 @@ int getStructureConfig(int structureType, int mc, StructureConfig *sconf)
case Geode:
*sconf = mc <= MC_1_17 ? s_geode_117 : s_geode;
return mc >= MC_1_17;
case Trail_Ruin:
*sconf = s_trail_ruin;
case Trail_Ruins:
*sconf = s_trail_ruins;
return mc >= MC_1_20;
case Trial_Chambers:
*sconf = s_trial_chambers;
return mc >= MC_1_21;
default:
memset(sconf, 0, sizeof(StructureConfig));
return 0;
@ -224,7 +234,8 @@ int getStructurePos(int structureType, int mc, uint64_t seed, int regX, int regZ
case Ruined_Portal:
case Ruined_Portal_N:
case Ancient_City:
case Trail_Ruin:
case Trail_Ruins:
case Trial_Chambers:
*pos = getFeaturePos(sconf, seed, regX, regZ);
return 1;
@ -278,6 +289,7 @@ int getStructurePos(int structureType, int mc, uint64_t seed, int regX, int regZ
}
case End_Gateway:
case End_Island:
case Desert_Well:
case Geode:
// decorator features
@ -950,7 +962,7 @@ int isViableFeatureBiome(int mc, int structureType, int biomeID)
if (mc <= MC_1_18) return 0;
return biomeID == deep_dark;
case Trail_Ruin:
case Trail_Ruins:
if (mc <= MC_1_19) return 0;
else {
switch (biomeID) {
@ -966,6 +978,10 @@ int isViableFeatureBiome(int mc, int structureType, int biomeID)
}
}
case Trial_Chambers:
if (mc <= MC_1_20) return 0;
return biomeID != deep_dark && isOverworld(mc, biomeID);
case Treasure:
if (mc <= MC_1_12) return 0;
return biomeID == beach || biomeID == snowy_beach;
@ -1257,7 +1273,7 @@ int isViableStructurePos(int structureType, Generator *g, int x, int z, uint32_t
switch (structureType)
{
case Trail_Ruin:
case Trail_Ruins:
if (g->mc <= MC_1_19) goto L_not_viable;
goto L_feature;
case Ocean_Ruin:
@ -1301,8 +1317,8 @@ L_feature:
{
if (g->mc <= MC_1_17)
g->entry = &g->ls.layers[L_RIVER_MIX_4];
sampleX = x >> 2;
sampleZ = z >> 2;
sampleX = x * 4;
sampleZ = z * 4;
}
id = getBiomeAt(g, 0, sampleX, 319>>2, sampleZ);
if (id < 0 || !isViableFeatureBiome(g->mc, structureType, id))
@ -1501,14 +1517,18 @@ L_feature:
goto L_viable;
case Ancient_City:
if (g->mc <= MC_1_18)
goto L_not_viable;
if (g->mc <= MC_1_18) goto L_not_viable;
goto L_jigsaw;
case Trial_Chambers:
if (g->mc <= MC_1_20) goto L_not_viable;
L_jigsaw:
{
StructureVariant sv;
getVariant(&sv, Ancient_City, g->mc, g->seed, x, z, -1);
sampleX = (chunkX*32 + 2*sv.x + sv.sx) / 2 >> 2;
sampleZ = (chunkZ*32 + 2*sv.z + sv.sz) / 2 >> 2;
sampleY = -27 >> 2;
getVariant(&sv, structureType, g->mc, g->seed, x, z, -1);
sampleX = (chunkX*32 + 2*sv.x + sv.sx - 1) / 2 >> 2;
sampleZ = (chunkZ*32 + 2*sv.z + sv.sz - 1) / 2 >> 2;
sampleY = sv.y >> 2;
id = getBiomeAt(g, 4, sampleX, sampleY, sampleZ);
}
if (id < 0 || !isViableFeatureBiome(g->mc, structureType, id))
@ -1686,7 +1706,7 @@ int isViableEndCityTerrain(const Generator *g, const SurfaceNoise *sn,
if (h01 < h00) h00 = h01;
if (h10 < h00) h00 = h10;
if (h11 < h00) h00 = h11;
return h00 >= 60;
return h00 >= 60 ? h00 : 0;
}
@ -1865,6 +1885,7 @@ int getVariant(StructureVariant *r, int structType, int mc, uint64_t seed,
case 2: r->x = x+sx; r->z = z+sz; break; // 2:cw180
case 3: r->x = x-sz; r->z = z+sx; break; // 3:cw270=ccw90
}
r->y = -27;
r->sy = sy;
return 1;
@ -2040,6 +2061,21 @@ int getVariant(StructureVariant *r, int structType, int mc, uint64_t seed,
}
return 1;
case Trial_Chambers:
r->y = nextInt(&rng, 1+20) + -40; // Y-level
r->rotation = nextInt(&rng, 4);
r->start = nextInt(&rng, 2); // corridor/end_[12]
r->sx = 19; r->sy = 20; r->sz = 19;
//r->y += -1; // groundLevelData
switch (r->rotation)
{ // 0:0, 1:cw90, 2:cw180, 3:cw270=ccw90
case 0: break;
case 1: r->x = 1-r->sz; r->z = 0; break;
case 2: r->x = 1-r->sx; r->z = 1-r->sz; break;
case 3: r->x = 0; r->z = 1-r->sx; break;
}
return 1;
default:
return 0;
}
@ -3996,7 +4032,10 @@ int floodFillGen(struct locate_info_t *info, int i, int j, Pos *p)
while (--qn >= 0)
{
if (info->stop && *info->stop)
{
free(queue);
return 0;
}
int d = queue[qn].d;
i = queue[qn].i;
j = queue[qn].j;
@ -4337,7 +4376,7 @@ struct _gp_args
int mc;
uint32_t flags;
};
// TODO: This function requires testing across versions
static void _genPotential(struct _gp_args *a, int layer, int id)
{
int mc = a->mc;

View File

@ -36,7 +36,9 @@ enum StructureType
Bastion,
End_City,
End_Gateway,
Trail_Ruin,
End_Island,
Trail_Ruins,
Trial_Chambers,
FEATURE_NUM
};

View File

@ -608,8 +608,15 @@ int genArea(const Layer *layer, int *out, int areaX, int areaZ, int areaWidth, i
int mapApproxHeight(float *y, int *ids, const Generator *g, const SurfaceNoise *sn,
int x, int z, int w, int h)
{
if (g->dim != DIM_OVERWORLD)
if (g->dim == DIM_NETHER)
return 127;
if (g->dim == DIM_END)
{
if (g->mc <= MC_1_8)
return 1;
return mapSurfaceHeightEnd(&g->en, sn, y, x, z, w, h, 4);
}
if (g->mc >= MC_1_18)
{

5
util.c
View File

@ -73,12 +73,14 @@ const char* mc2str(int mc)
case MC_1_19_2: return "1.19.2"; break;
case MC_1_19: return "1.19"; break;
case MC_1_20: return "1.20"; break;
case MC_1_21: return "1.21"; break;
default: return NULL;
}
}
int str2mc(const char *s)
{
if (!strcmp(s, "1.21")) return MC_1_21;
if (!strcmp(s, "1.20")) return MC_1_20;
if (!strcmp(s, "1.19")) return MC_1_19;
if (!strcmp(s, "1.19.2")) return MC_1_19_2;
@ -265,7 +267,8 @@ const char* struct2str(int stype)
case Ruined_Portal_N: return "ruined_portal_nether";
case Geode: return "amethyst_geode";
case Ancient_City: return "ancient_city";
case Trail_Ruin: return "trail_ruins";
case Trail_Ruins: return "trail_ruins";
case Trial_Chambers: return "trial_chambers";
case Fortress: return "fortress";
case Bastion: return "bastion_remnant";
case End_City: return "end_city";