Move biome calculation to BiomeGen

BiomeGen defines an interface that, given a set of BiomeParams, computes biomes
for a given area using the algorithm implemented by that specific BiomeGen.
This abstracts away the old system where each mapgen supplied the noises
required for biome generation.
This commit is contained in:
kwolekr 2016-04-28 03:43:09 -04:00
parent fa6b21a15b
commit 76f4856479
16 changed files with 421 additions and 304 deletions

View File

@ -181,8 +181,6 @@ EmergeManager::~EmergeManager()
delete oremgr;
delete decomgr;
delete schemmgr;
delete params.sparams;
}

View File

@ -76,10 +76,9 @@ Mapgen::Mapgen()
vm = NULL;
ndef = NULL;
heightmap = NULL;
biomegen = NULL;
biomemap = NULL;
heatmap = NULL;
humidmap = NULL;
heightmap = NULL;
}
@ -94,11 +93,10 @@ Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
vm = NULL;
ndef = NULL;
heightmap = NULL;
ndef = emerge->ndef;
biomegen = NULL;
biomemap = NULL;
heatmap = NULL;
humidmap = NULL;
heightmap = NULL;
}
@ -444,6 +442,14 @@ void GenerateNotifier::getEvents(
//// MapgenParams
////
MapgenParams::~MapgenParams()
{
delete bparams;
delete sparams;
}
void MapgenParams::load(const Settings &settings)
{
std::string seed_str;
@ -458,10 +464,13 @@ void MapgenParams::load(const Settings &settings)
settings.getS16NoEx("water_level", water_level);
settings.getS16NoEx("chunksize", chunksize);
settings.getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
settings.getNoiseParams("mg_biome_np_heat", np_biome_heat);
settings.getNoiseParams("mg_biome_np_heat_blend", np_biome_heat_blend);
settings.getNoiseParams("mg_biome_np_humidity", np_biome_humidity);
settings.getNoiseParams("mg_biome_np_humidity_blend", np_biome_humidity_blend);
delete bparams;
bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
if (bparams) {
bparams->readParams(&settings);
bparams->seed = seed;
}
delete sparams;
MapgenFactory *mgfactory = EmergeManager::getMapgenFactory(mg_name);
@ -479,10 +488,9 @@ void MapgenParams::save(Settings &settings) const
settings.setS16("water_level", water_level);
settings.setS16("chunksize", chunksize);
settings.setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
settings.setNoiseParams("mg_biome_np_heat", np_biome_heat);
settings.setNoiseParams("mg_biome_np_heat_blend", np_biome_heat_blend);
settings.setNoiseParams("mg_biome_np_humidity", np_biome_humidity);
settings.setNoiseParams("mg_biome_np_humidity_blend", np_biome_humidity_blend);
if (bparams)
bparams->writeParams(&settings);
if (sparams)
sparams->writeParams(&settings);

View File

@ -44,6 +44,8 @@ extern FlagDesc flagdesc_mapgen[];
extern FlagDesc flagdesc_gennotify[];
class Biome;
class BiomeGen;
struct BiomeParams;
class EmergeManager;
class MapBlock;
class VoxelManipulator;
@ -115,11 +117,7 @@ struct MapgenParams {
s16 water_level;
u32 flags;
NoiseParams np_biome_heat;
NoiseParams np_biome_heat_blend;
NoiseParams np_biome_humidity;
NoiseParams np_biome_humidity_blend;
BiomeParams *bparams;
MapgenSpecificParams *sparams;
MapgenParams() :
@ -128,12 +126,12 @@ struct MapgenParams {
seed(0),
water_level(1),
flags(MG_CAVES | MG_LIGHT | MG_DECORATIONS),
np_biome_heat(NoiseParams(50, 50, v3f(750.0, 750.0, 750.0), 5349, 3, 0.5, 2.0)),
np_biome_heat_blend(NoiseParams(0, 1.5, v3f(8.0, 8.0, 8.0), 13, 2, 1.0, 2.0)),
np_biome_humidity(NoiseParams(50, 50, v3f(750.0, 750.0, 750.0), 842, 3, 0.5, 2.0)),
np_biome_humidity_blend(NoiseParams(0, 1.5, v3f(8.0, 8.0, 8.0), 90003, 2, 1.0, 2.0)),
bparams(NULL),
sparams(NULL)
{}
{
}
virtual ~MapgenParams();
void load(const Settings &settings);
void save(Settings &settings) const;
@ -153,10 +151,9 @@ class Mapgen {
u32 blockseed;
s16 *heightmap;
u8 *biomemap;
float *heatmap;
float *humidmap;
v3s16 csize;
BiomeGen *biomegen;
GenerateNotifier gennotify;
Mapgen();

View File

@ -61,10 +61,7 @@ MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge
// 1-down overgeneration
this->zstride_1d = csize.X * (csize.Y + 1);
this->biomemap = new u8[csize.X * csize.Z];
this->heightmap = new s16[csize.X * csize.Z];
this->heatmap = NULL;
this->humidmap = NULL;
this->heightmap = new s16[csize.X * csize.Z];
MapgenFlatParams *sp = (MapgenFlatParams *)params->sparams;
@ -86,15 +83,12 @@ MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge
noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z);
noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
//// Biome noise
noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
noise_heat_blend = new Noise(&params->np_biome_heat_blend, seed, csize.X, csize.Z);
noise_humidity_blend = new Noise(&params->np_biome_humidity_blend, seed, csize.X, csize.Z);
//// Initialize biome generator
biomegen = emerge->biomemgr->createBiomeGen(
BIOMEGEN_ORIGINAL, params->bparams, csize);
biomemap = biomegen->biomemap;
//// Resolve nodes to be used
INodeDefManager *ndef = emerge->ndef;
c_stone = ndef->getId("mapgen_stone");
c_water_source = ndef->getId("mapgen_water_source");
c_lava_source = ndef->getId("mapgen_lava_source");
@ -128,13 +122,9 @@ MapgenFlat::~MapgenFlat()
delete noise_cave1;
delete noise_cave2;
delete noise_heat;
delete noise_humidity;
delete noise_heat_blend;
delete noise_humidity_blend;
delete biomegen;
delete[] heightmap;
delete[] biomemap;
}
@ -252,12 +242,10 @@ void MapgenFlat::makeChunk(BlockMakeData *data)
// Create heightmap
updateHeightmap(node_min, node_max);
// Create biomemap at heightmap surface
bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result,
noise_humidity->result, heightmap, biomemap);
// Actually place the biome-specific nodes
MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result);
// Init biome generator, place biome-specific nodes, and build biomemap
biomegen->calcBiomeNoise(node_min);
biomegen->getBiomes(heightmap);
MgStoneType stone_type = generateBiomes();
if (flags & MG_CAVES)
generateCaves(stone_surface_max_y);
@ -343,18 +331,7 @@ void MapgenFlat::calculateNoise()
// only if solid terrain is present in mapchunk
noise_filler_depth->perlinMap2D(x, z);
noise_heat->perlinMap2D(x, z);
noise_humidity->perlinMap2D(x, z);
noise_heat_blend->perlinMap2D(x, z);
noise_humidity_blend->perlinMap2D(x, z);
for (s32 i = 0; i < csize.X * csize.Z; i++) {
noise_heat->result[i] += noise_heat_blend->result[i];
noise_humidity->result[i] += noise_humidity_blend->result[i];
}
heatmap = noise_heat->result;
humidmap = noise_humidity->result;
//printf("calculateNoise: %dus\n", t.stop());
}
@ -406,7 +383,7 @@ s16 MapgenFlat::generateTerrain()
}
MgStoneType MapgenFlat::generateBiomes(float *heat_map, float *humidity_map)
MgStoneType MapgenFlat::generateBiomes()
{
v3s16 em = vm->m_area.getExtent();
u32 index = 0;
@ -443,7 +420,8 @@ MgStoneType MapgenFlat::generateBiomes(float *heat_map, float *humidity_map)
// 3. When stone or water is detected but biome has not yet been calculated.
if ((c == c_stone && (air_above || water_above || !biome)) ||
(c == c_water_source && (air_above || !biome))) {
biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
biome = biomegen->getBiomeAtIndex(index, y);
depth_top = biome->depth_top;
base_filler = MYMAX(depth_top + biome->depth_filler
+ noise_filler_depth->result[index], 0);

View File

@ -104,7 +104,7 @@ class MapgenFlat : public Mapgen {
int getSpawnLevelAtPoint(v2s16 p);
void calculateNoise();
s16 generateTerrain();
MgStoneType generateBiomes(float *heat_map, float *humidity_map);
MgStoneType generateBiomes();
void dustTopNodes();
void generateCaves(s16 max_stone_y);
};

View File

@ -59,10 +59,7 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *
// 1-down overgeneration
this->zstride_1d = csize.X * (csize.Y + 1);
this->biomemap = new u8[csize.X * csize.Z];
this->heightmap = new s16[csize.X * csize.Z];
this->heatmap = NULL;
this->humidmap = NULL;
MapgenFractalParams *sp = (MapgenFractalParams *)params->sparams;
@ -87,18 +84,15 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *
noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z);
noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
//// Biome noise
noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
noise_heat_blend = new Noise(&params->np_biome_heat_blend, seed, csize.X, csize.Z);
noise_humidity_blend = new Noise(&params->np_biome_humidity_blend, seed, csize.X, csize.Z);
//// Initialize biome generator
biomegen = emerge->biomemgr->createBiomeGen(
BIOMEGEN_ORIGINAL, params->bparams, csize);
biomemap = biomegen->biomemap;
this->formula = fractal / 2 + fractal % 2;
this->julia = fractal % 2 == 0;
//// Resolve nodes to be used
INodeDefManager *ndef = emerge->ndef;
c_stone = ndef->getId("mapgen_stone");
c_water_source = ndef->getId("mapgen_water_source");
c_lava_source = ndef->getId("mapgen_lava_source");
@ -132,13 +126,9 @@ MapgenFractal::~MapgenFractal()
delete noise_cave1;
delete noise_cave2;
delete noise_heat;
delete noise_humidity;
delete noise_heat_blend;
delete noise_humidity_blend;
delete biomegen;
delete[] heightmap;
delete[] biomemap;
}
@ -217,7 +207,7 @@ int MapgenFractal::getSpawnLevelAtPoint(v2s16 p)
s16 search_start = MYMAX(seabed_level, water_level + 1);
if (seabed_level > water_level)
solid_below = true;
for (s16 y = search_start; y <= search_start + 128; y++) {
if (getFractalAtPoint(p.X, y, p.Y)) { // Fractal node
solid_below = true;
@ -268,12 +258,10 @@ void MapgenFractal::makeChunk(BlockMakeData *data)
// Create heightmap
updateHeightmap(node_min, node_max);
// Create biomemap at heightmap surface
bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result,
noise_humidity->result, heightmap, biomemap);
// Actually place the biome-specific nodes
MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result);
// Init biome generator, place biome-specific nodes, and build biomemap
biomegen->calcBiomeNoise(node_min);
biomegen->getBiomes(heightmap);
MgStoneType stone_type = generateBiomes();
if (flags & MG_CAVES)
generateCaves(stone_surface_max_y);
@ -358,18 +346,7 @@ void MapgenFractal::calculateNoise()
// only if solid terrain is present in mapchunk
noise_filler_depth->perlinMap2D(x, z);
noise_heat->perlinMap2D(x, z);
noise_humidity->perlinMap2D(x, z);
noise_heat_blend->perlinMap2D(x, z);
noise_humidity_blend->perlinMap2D(x, z);
for (s32 i = 0; i < csize.X * csize.Z; i++) {
noise_heat->result[i] += noise_heat_blend->result[i];
noise_humidity->result[i] += noise_humidity_blend->result[i];
}
heatmap = noise_heat->result;
humidmap = noise_humidity->result;
//printf("calculateNoise: %dus\n", t.stop());
}
@ -530,7 +507,7 @@ s16 MapgenFractal::generateTerrain()
}
MgStoneType MapgenFractal::generateBiomes(float *heat_map, float *humidity_map)
MgStoneType MapgenFractal::generateBiomes()
{
v3s16 em = vm->m_area.getExtent();
u32 index = 0;
@ -567,7 +544,8 @@ MgStoneType MapgenFractal::generateBiomes(float *heat_map, float *humidity_map)
// 3. When stone or water is detected but biome has not yet been calculated.
if ((c == c_stone && (air_above || water_above || !biome)) ||
(c == c_water_source && (air_above || !biome))) {
biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
biome = biomegen->getBiomeAtIndex(index, y);
depth_top = biome->depth_top;
base_filler = MYMAX(depth_top + biome->depth_filler
+ noise_filler_depth->result[index], 0);

View File

@ -88,11 +88,6 @@ class MapgenFractal : public Mapgen {
Noise *noise_cave1;
Noise *noise_cave2;
Noise *noise_heat;
Noise *noise_humidity;
Noise *noise_heat_blend;
Noise *noise_humidity_blend;
content_t c_stone;
content_t c_water_source;
content_t c_lava_source;
@ -114,7 +109,7 @@ class MapgenFractal : public Mapgen {
void calculateNoise();
bool getFractalAtPoint(s16 x, s16 y, s16 z);
s16 generateTerrain();
MgStoneType generateBiomes(float *heat_map, float *humidity_map);
MgStoneType generateBiomes();
void dustTopNodes();
void generateCaves(s16 max_stone_y);
};

View File

@ -57,10 +57,7 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge)
// 1-down overgeneration
this->zstride_1d = csize.X * (csize.Y + 1);
this->biomemap = new u8[csize.X * csize.Z];
this->heightmap = new s16[csize.X * csize.Z];
this->heatmap = NULL;
this->humidmap = NULL;
MapgenV5Params *sp = (MapgenV5Params *)params->sparams;
@ -79,15 +76,12 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge)
noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z);
noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
// Biome noise
noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
noise_heat_blend = new Noise(&params->np_biome_heat_blend, seed, csize.X, csize.Z);
noise_humidity_blend = new Noise(&params->np_biome_humidity_blend, seed, csize.X, csize.Z);
//// Initialize biome generator
biomegen = emerge->biomemgr->createBiomeGen(
BIOMEGEN_ORIGINAL, params->bparams, csize);
biomemap = biomegen->biomemap;
//// Resolve nodes to be used
INodeDefManager *ndef = emerge->ndef;
c_stone = ndef->getId("mapgen_stone");
c_water_source = ndef->getId("mapgen_water_source");
c_lava_source = ndef->getId("mapgen_lava_source");
@ -123,13 +117,9 @@ MapgenV5::~MapgenV5()
delete noise_cave2;
delete noise_ground;
delete noise_heat;
delete noise_humidity;
delete noise_heat_blend;
delete noise_humidity_blend;
delete biomegen;
delete[] heightmap;
delete[] biomemap;
}
@ -248,12 +238,10 @@ void MapgenV5::makeChunk(BlockMakeData *data)
// Create heightmap
updateHeightmap(node_min, node_max);
// Create biomemap at heightmap surface
bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result,
noise_humidity->result, heightmap, biomemap);
// Actually place the biome-specific nodes
MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result);
// Init biome generator, place biome-specific nodes, and build biomemap
biomegen->calcBiomeNoise(node_min);
biomegen->getBiomes(heightmap);
MgStoneType stone_type = generateBiomes();
// Generate caves
if ((flags & MG_CAVES) && (stone_surface_max_y >= node_min.Y))
@ -343,18 +331,7 @@ void MapgenV5::calculateNoise()
// only if solid terrain is present in mapchunk
noise_filler_depth->perlinMap2D(x, z);
noise_heat->perlinMap2D(x, z);
noise_humidity->perlinMap2D(x, z);
noise_heat_blend->perlinMap2D(x, z);
noise_humidity_blend->perlinMap2D(x, z);
for (s32 i = 0; i < csize.X * csize.Z; i++) {
noise_heat->result[i] += noise_heat_blend->result[i];
noise_humidity->result[i] += noise_humidity_blend->result[i];
}
heatmap = noise_heat->result;
humidmap = noise_humidity->result;
//printf("calculateNoise: %dus\n", t.stop());
}
@ -416,7 +393,7 @@ int MapgenV5::generateBaseTerrain()
}
MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map)
MgStoneType MapgenV5::generateBiomes()
{
v3s16 em = vm->m_area.getExtent();
u32 index = 0;
@ -452,7 +429,8 @@ MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map)
// 3. When stone or water is detected but biome has not yet been calculated.
if ((c == c_stone && (air_above || water_above || !biome)) ||
(c == c_water_source && (air_above || !biome))) {
biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
biome = biomegen->getBiomeAtIndex(index, y);
depth_top = biome->depth_top;
base_filler = MYMAX(depth_top + biome->depth_filler
+ noise_filler_depth->result[index], 0);

View File

@ -70,11 +70,6 @@ class MapgenV5 : public Mapgen {
Noise *noise_cave2;
Noise *noise_ground;
Noise *noise_heat;
Noise *noise_humidity;
Noise *noise_heat_blend;
Noise *noise_humidity_blend;
content_t c_stone;
content_t c_water_source;
content_t c_lava_source;
@ -95,7 +90,7 @@ class MapgenV5 : public Mapgen {
int getSpawnLevelAtPoint(v2s16 p);
void calculateNoise();
int generateBaseTerrain();
MgStoneType generateBiomes(float *heat_map, float *humidity_map);
MgStoneType generateBiomes();
void generateCaves(int max_stone_y);
void dustTopNodes();
};

View File

@ -64,10 +64,7 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge)
// 1-down overgeneration
this->zstride_1d = csize.X * (csize.Y + 1);
this->biomemap = new u8[csize.X * csize.Z];
this->heightmap = new s16[csize.X * csize.Z];
this->heatmap = NULL;
this->humidmap = NULL;
this->ridge_heightmap = new s16[csize.X * csize.Z];
MapgenV7Params *sp = (MapgenV7Params *)params->sparams;
@ -92,15 +89,13 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge)
noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z);
noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
//// Biome noise
noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
noise_heat_blend = new Noise(&params->np_biome_heat_blend, seed, csize.X, csize.Z);
noise_humidity_blend = new Noise(&params->np_biome_humidity_blend, seed, csize.X, csize.Z);
// TODO(hmmmm): should we have a way to disable biomemanager biomes?
//// Initialize biome generator
biomegen = emerge->biomemgr->createBiomeGen(
BIOMEGEN_ORIGINAL, params->bparams, csize);
biomemap = biomegen->biomemap;
//// Resolve nodes to be used
INodeDefManager *ndef = emerge->ndef;
c_stone = ndef->getId("mapgen_stone");
c_water_source = ndef->getId("mapgen_water_source");
c_lava_source = ndef->getId("mapgen_lava_source");
@ -141,14 +136,10 @@ MapgenV7::~MapgenV7()
delete noise_cave1;
delete noise_cave2;
delete noise_heat;
delete noise_humidity;
delete noise_heat_blend;
delete noise_humidity_blend;
delete biomegen;
delete[] ridge_heightmap;
delete[] heightmap;
delete[] biomemap;
}
@ -279,12 +270,10 @@ void MapgenV7::makeChunk(BlockMakeData *data)
// Update heightmap to include mountain terrain
updateHeightmap(node_min, node_max);
// Create biomemap at heightmap surface
bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result,
noise_humidity->result, heightmap, biomemap);
// Actually place the biome-specific nodes
MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result);
// Init biome generator, place biome-specific nodes, and build biomemap
biomegen->calcBiomeNoise(node_min);
biomegen->getBiomes(heightmap);
MgStoneType stone_type = generateBiomes();
if (flags & MG_CAVES)
generateCaves(stone_surface_max_y);
@ -384,35 +373,10 @@ void MapgenV7::calculateNoise()
// Cave noises are calculated in generateCaves()
// only if solid terrain is present in mapchunk
noise_filler_depth->perlinMap2D(x, z);
noise_heat->perlinMap2D(x, z);
noise_humidity->perlinMap2D(x, z);
noise_heat_blend->perlinMap2D(x, z);
noise_humidity_blend->perlinMap2D(x, z);
for (s32 i = 0; i < csize.X * csize.Z; i++) {
noise_heat->result[i] += noise_heat_blend->result[i];
noise_humidity->result[i] += noise_humidity_blend->result[i];
}
heatmap = noise_heat->result;
humidmap = noise_humidity->result;
//printf("calculateNoise: %dus\n", t.stop());
}
Biome *MapgenV7::getBiomeAtPoint(v3s16 p)
{
float heat = NoisePerlin2D(&noise_heat->np, p.X, p.Z, seed) +
NoisePerlin2D(&noise_heat_blend->np, p.X, p.Z, seed);
float humidity = NoisePerlin2D(&noise_humidity->np, p.X, p.Z, seed) +
NoisePerlin2D(&noise_humidity_blend->np, p.X, p.Z, seed);
s16 groundlevel = baseTerrainLevelAtPoint(p.X, p.Z);
return bmgr->getBiome(heat, humidity, groundlevel);
}
float MapgenV7::baseTerrainLevelAtPoint(s16 x, s16 z)
{
float hselect = NoisePerlin2D(&noise_height_select->np, x, z, seed);
@ -553,7 +517,7 @@ void MapgenV7::generateRidgeTerrain()
}
MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map)
MgStoneType MapgenV7::generateBiomes()
{
v3s16 em = vm->m_area.getExtent();
u32 index = 0;
@ -589,7 +553,8 @@ MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map)
// 3. When stone or water is detected but biome has not yet been calculated.
if ((c == c_stone && (air_above || water_above || !biome)) ||
(c == c_water_source && (air_above || !biome))) {
biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
biome = biomegen->getBiomeAtIndex(index, y);
depth_top = biome->depth_top;
base_filler = MYMAX(depth_top + biome->depth_filler
+ noise_filler_depth->result[index], 0);

View File

@ -84,11 +84,6 @@ class MapgenV7 : public Mapgen {
Noise *noise_cave1;
Noise *noise_cave2;
Noise *noise_heat;
Noise *noise_humidity;
Noise *noise_heat_blend;
Noise *noise_humidity_blend;
content_t c_stone;
content_t c_water_source;
content_t c_lava_source;
@ -107,7 +102,6 @@ class MapgenV7 : public Mapgen {
virtual void makeChunk(BlockMakeData *data);
int getSpawnLevelAtPoint(v2s16 p);
Biome *getBiomeAtPoint(v3s16 p);
float baseTerrainLevelAtPoint(s16 x, s16 z);
float baseTerrainLevelFromMap(int index);
@ -119,7 +113,7 @@ class MapgenV7 : public Mapgen {
int generateTerrain();
void generateRidgeTerrain();
MgStoneType generateBiomes(float *heat_map, float *humidity_map);
MgStoneType generateBiomes();
void dustTopNodes();
void generateCaves(s16 max_stone_y);

View File

@ -68,7 +68,7 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *
: Mapgen(mapgenid, params, emerge)
{
this->m_emerge = emerge;
this->bmgr = emerge->biomemgr;
this->bmgr = emerge->biomemgr;
//// amount of elements to skip for the next index
//// for noise/height/biome maps (not vmanip)
@ -77,15 +77,13 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *
// 1-down overgeneration
this->zstride_1d = csize.X * (csize.Y + 1);
this->biomemap = new u8[csize.X * csize.Z];
this->heightmap = new s16[csize.X * csize.Z];
this->heatmap = NULL;
this->humidmap = NULL;
this->map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
g_settings->getU16("map_generation_limit"));
MapgenValleysParams *sp = (MapgenValleysParams *)params->sparams;
BiomeParamsOriginal *bp = (BiomeParamsOriginal *)params->bparams;
this->spflags = sp->spflags;
this->altitude_chill = sp->altitude_chill;
@ -113,15 +111,16 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *
noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
noise_massive_caves = new Noise(&sp->np_massive_caves, seed, csize.X, csize.Y + 1, csize.Z);
//// Biome noise
noise_heat_blend = new Noise(&params->np_biome_heat_blend, seed, csize.X, csize.Z);
noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
noise_humidity_blend = new Noise(&params->np_biome_humidity_blend, seed, csize.X, csize.Z);
noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
//// Initialize biome generator
// NOTE: valleys mapgen can only use BiomeGenOriginal
biomegen = emerge->biomemgr->createBiomeGen(
BIOMEGEN_ORIGINAL, params->bparams, csize);
biomemap = biomegen->biomemap;
m_bgen = (BiomeGenOriginal *)biomegen;
this->humid_rivers = (spflags & MGVALLEYS_HUMID_RIVERS);
this->use_altitude_chill = (spflags & MGVALLEYS_ALT_CHILL);
this->humidity_adjust = params->np_biome_humidity.offset - 50.f;
this->humidity_adjust = bp->np_humidity.offset - 50.f;
// a small chance of overflows if the settings are very high
this->cave_water_max_height = water_level + MYMAX(0, water_features_lim - 4) * 50;
@ -130,8 +129,6 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *
tcave_cache = new float[csize.Y + 2];
//// Resolve nodes to be used
INodeDefManager *ndef = emerge->ndef;
c_cobble = ndef->getId("mapgen_cobble");
c_desert_stone = ndef->getId("mapgen_desert_stone");
c_dirt = ndef->getId("mapgen_dirt");
@ -174,12 +171,8 @@ MapgenValleys::~MapgenValleys()
delete noise_valley_depth;
delete noise_valley_profile;
delete noise_heat;
delete noise_heat_blend;
delete noise_humidity;
delete noise_humidity_blend;
delete biomegen;
delete[] biomemap;
delete[] heightmap;
delete[] tcave_cache;
}
@ -293,14 +286,19 @@ void MapgenValleys::makeChunk(BlockMakeData *data)
// Generate noise maps and base terrain height.
calculateNoise();
// Generate biome noises. Note this must be executed strictly before
// generateTerrain, because generateTerrain depends on intermediate
// biome-related noises.
biomegen->calcBiomeNoise(node_min);
// Generate base terrain with initial heightmaps
s16 stone_surface_max_y = generateTerrain();
// Create biomemap at heightmap surface
bmgr->calcBiomes(csize.X, csize.Z, heatmap, humidmap, heightmap, biomemap);
// Build biomemap
biomegen->getBiomes(heightmap);
// Actually place the biome-specific nodes
MgStoneType stone_type = generateBiomes(heatmap, humidmap);
// Place biome-specific nodes
MgStoneType stone_type = generateBiomes();
// Cave creation.
if (flags & MG_CAVES)
@ -391,10 +389,6 @@ void MapgenValleys::calculateNoise()
//TimeTaker tcn("actualNoise");
noise_filler_depth->perlinMap2D(x, z);
noise_heat_blend->perlinMap2D(x, z);
noise_heat->perlinMap2D(x, z);
noise_humidity_blend->perlinMap2D(x, z);
noise_humidity->perlinMap2D(x, z);
noise_inter_valley_slope->perlinMap2D(x, z);
noise_rivers->perlinMap2D(x, z);
noise_terrain_height->perlinMap2D(x, z);
@ -418,9 +412,8 @@ void MapgenValleys::calculateNoise()
}
for (s32 index = 0; index < csize.X * csize.Z; index++) {
noise_heat->result[index] += noise_heat_blend->result[index] + heat_offset;
noise_humidity->result[index] *= humidity_scale;
noise_humidity->result[index] += noise_humidity_blend->result[index];
m_bgen->heatmap[index] += heat_offset;
m_bgen->humidmap[index] *= humidity_scale;
}
TerrainNoise tn;
@ -450,9 +443,6 @@ void MapgenValleys::calculateNoise()
float mount = terrainLevelFromNoise(&tn);
noise_terrain_height->result[index] = mount;
}
heatmap = noise_heat->result;
humidmap = noise_humidity->result;
}
@ -596,7 +586,7 @@ int MapgenValleys::generateTerrain()
float river_y = noise_rivers->result[index_2d];
float surface_y = noise_terrain_height->result[index_2d];
float slope = noise_inter_valley_slope->result[index_2d];
float t_heat = noise_heat->result[index_2d];
float t_heat = m_bgen->heatmap[index_2d];
heightmap[index_2d] = -MAX_MAP_GENERATION_LIMIT;
@ -610,7 +600,7 @@ int MapgenValleys::generateTerrain()
t_heat -= alt_to_heat * MYMAX(surface_y, river_y) / altitude_chill;
// If humidity is low or heat is high, lower the water table.
float delta = noise_humidity->result[index_2d] - 50.f;
float delta = m_bgen->humidmap[index_2d] - 50.f;
if (delta < 0.f) {
float t_evap = (t_heat - 32.f) / evaporation;
river_y += delta * MYMAX(t_evap, 0.08f);
@ -672,7 +662,7 @@ int MapgenValleys::generateTerrain()
// Use base ground (water table) in a riverbed, to
// avoid an unnatural rise in humidity.
float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]);
float humid = noise_humidity->result[index_2d];
float humid = m_bgen->humidmap[index_2d];
float water_depth = (t_alt - river_y) / humidity_dropoff;
humid *= 1.f + pow(0.5f, MYMAX(water_depth, 1.f));
@ -683,7 +673,7 @@ int MapgenValleys::generateTerrain()
if (t_alt > 0.f)
humid -= alt_to_humid * t_alt / altitude_chill;
noise_humidity->result[index_2d] = humid;
m_bgen->humidmap[index_2d] = humid;
}
// Assign the heat adjusted by any changed altitudes.
@ -693,9 +683,9 @@ int MapgenValleys::generateTerrain()
float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]);
if (humid_rivers && heightmap[index_2d] == (s16)myround(surface_y))
// The altitude hasn't changed. Use the first result.
noise_heat->result[index_2d] = t_heat;
m_bgen->heatmap[index_2d] = t_heat;
else if (t_alt > 0.f)
noise_heat->result[index_2d] -= alt_to_heat * t_alt / altitude_chill;
m_bgen->heatmap[index_2d] -= alt_to_heat * t_alt / altitude_chill;
}
}
@ -703,7 +693,7 @@ int MapgenValleys::generateTerrain()
}
MgStoneType MapgenValleys::generateBiomes(float *heat_map, float *humidity_map)
MgStoneType MapgenValleys::generateBiomes()
{
v3s16 em = vm->m_area.getExtent();
u32 index = 0;
@ -739,9 +729,9 @@ MgStoneType MapgenValleys::generateBiomes(float *heat_map, float *humidity_map)
// 3. When stone or water is detected but biome has not yet been calculated.
if ((c == c_stone && (air_above || water_above || !biome))
|| ((c == c_water_source || c == c_river_water_source)
&& (air_above || !biome))) {
&& (air_above || !biome))) {
// Both heat and humidity have already been adjusted for altitude.
biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
biome = biomegen->getBiomeAtIndex(index, y);
depth_top = biome->depth_top;
base_filler = MYMAX(depth_top

View File

@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MYCUBE(x) (x) * (x) * (x)
class BiomeManager;
class BiomeGenOriginal;
// Global profiler
//class Profiler;
@ -98,6 +99,7 @@ class MapgenValleys : public Mapgen {
private:
EmergeManager *m_emerge;
BiomeManager *bmgr;
BiomeGenOriginal *m_bgen;
int ystride;
int zstride;
@ -136,11 +138,6 @@ class MapgenValleys : public Mapgen {
Noise *noise_valley_depth;
Noise *noise_valley_profile;
Noise *noise_heat;
Noise *noise_heat_blend;
Noise *noise_humidity;
Noise *noise_humidity_blend;
content_t c_cobble;
content_t c_desert_stone;
content_t c_dirt;
@ -164,9 +161,7 @@ class MapgenValleys : public Mapgen {
float terrainLevelFromNoise(TerrainNoise *tn);
float adjustedTerrainLevelFromNoise(TerrainNoise *tn);
float humidityByTerrain(float humidity_base, float mount, float rivers, float valley);
MgStoneType generateBiomes(float *heat_map, float *humidity_map);
MgStoneType generateBiomes();
void dustTopNodes();
void generateCaves(s16 max_stone_y);

View File

@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h"
#include "util/mathconstants.h"
#include "porting.h"
#include "settings.h"
///////////////////////////////////////////////////////////////////////////////
@ -63,49 +64,11 @@ BiomeManager::BiomeManager(IGameDef *gamedef) :
}
BiomeManager::~BiomeManager()
{
//if (biomecache)
// delete[] biomecache;
}
// just a PoC, obviously needs optimization later on (precalculate this)
void BiomeManager::calcBiomes(s16 sx, s16 sy, float *heat_map,
float *humidity_map, s16 *height_map, u8 *biomeid_map)
{
for (s32 i = 0; i != sx * sy; i++) {
Biome *biome = getBiome(heat_map[i], humidity_map[i], height_map[i]);
biomeid_map[i] = biome->index;
}
}
Biome *BiomeManager::getBiome(float heat, float humidity, s16 y)
{
Biome *b, *biome_closest = NULL;
float dist_min = FLT_MAX;
for (size_t i = 1; i < m_objects.size(); i++) {
b = (Biome *)m_objects[i];
if (!b || y > b->y_max || y < b->y_min)
continue;
float d_heat = heat - b->heat_point;
float d_humidity = humidity - b->humidity_point;
float dist = (d_heat * d_heat) +
(d_humidity * d_humidity);
if (dist < dist_min) {
dist_min = dist;
biome_closest = b;
}
}
return biome_closest ? biome_closest : (Biome *)m_objects[0];
}
void BiomeManager::clear()
{
EmergeManager *emerge = m_gamedef->getEmergeManager();
@ -118,17 +81,153 @@ void BiomeManager::clear()
}
// Don't delete the first biome
for (size_t i = 1; i < m_objects.size(); i++) {
Biome *b = (Biome *)m_objects[i];
delete b;
}
for (size_t i = 1; i < m_objects.size(); i++)
delete (Biome *)m_objects[i];
m_objects.resize(1);
}
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void BiomeParamsOriginal::readParams(const Settings *settings)
{
settings->getNoiseParams("mg_biome_np_heat", np_heat);
settings->getNoiseParams("mg_biome_np_heat_blend", np_heat_blend);
settings->getNoiseParams("mg_biome_np_humidity", np_humidity);
settings->getNoiseParams("mg_biome_np_humidity_blend", np_humidity_blend);
}
void BiomeParamsOriginal::writeParams(Settings *settings) const
{
settings->setNoiseParams("mg_biome_np_heat", np_heat);
settings->setNoiseParams("mg_biome_np_heat_blend", np_heat_blend);
settings->setNoiseParams("mg_biome_np_humidity", np_humidity);
settings->setNoiseParams("mg_biome_np_humidity_blend", np_humidity_blend);
}
////////////////////////////////////////////////////////////////////////////////
BiomeGenOriginal::BiomeGenOriginal(BiomeManager *biomemgr,
BiomeParamsOriginal *params, v3s16 chunksize)
{
m_bmgr = biomemgr;
m_params = params;
m_csize = chunksize;
noise_heat = new Noise(&params->np_heat,
params->seed, m_csize.X, m_csize.Z);
noise_humidity = new Noise(&params->np_humidity,
params->seed, m_csize.X, m_csize.Z);
noise_heat_blend = new Noise(&params->np_heat_blend,
params->seed, m_csize.X, m_csize.Z);
noise_humidity_blend = new Noise(&params->np_humidity_blend,
params->seed, m_csize.X, m_csize.Z);
heatmap = noise_heat->result;
humidmap = noise_humidity->result;
biomemap = new u8[m_csize.X * m_csize.Z];
}
BiomeGenOriginal::~BiomeGenOriginal()
{
delete []biomemap;
delete noise_heat;
delete noise_humidity;
delete noise_heat_blend;
delete noise_humidity_blend;
}
Biome *BiomeGenOriginal::calcBiomeAtPoint(v3s16 pos) const
{
float heat =
NoisePerlin2D(&m_params->np_heat, pos.X, pos.Z, m_params->seed) +
NoisePerlin2D(&m_params->np_heat_blend, pos.X, pos.Z, m_params->seed);
float humidity =
NoisePerlin2D(&m_params->np_humidity, pos.X, pos.Z, m_params->seed) +
NoisePerlin2D(&m_params->np_humidity_blend, pos.X, pos.Z, m_params->seed);
return calcBiomeFromNoise(heat, humidity, pos.Y);
}
void BiomeGenOriginal::calcBiomeNoise(v3s16 pmin)
{
m_pmin = pmin;
noise_heat->perlinMap2D(pmin.X, pmin.Z);
noise_humidity->perlinMap2D(pmin.X, pmin.Z);
noise_heat_blend->perlinMap2D(pmin.X, pmin.Z);
noise_humidity_blend->perlinMap2D(pmin.X, pmin.Z);
for (s32 i = 0; i < m_csize.X * m_csize.Z; i++) {
noise_heat->result[i] += noise_heat_blend->result[i];
noise_humidity->result[i] += noise_humidity_blend->result[i];
}
}
u8 *BiomeGenOriginal::getBiomes(s16 *heightmap)
{
for (s32 i = 0; i != m_csize.X * m_csize.Z; i++) {
Biome *biome = calcBiomeFromNoise(
noise_heat->result[i],
noise_humidity->result[i],
heightmap[i]);
biomemap[i] = biome->index;
}
return biomemap;
}
Biome *BiomeGenOriginal::getBiomeAtPoint(v3s16 pos) const
{
return getBiomeAtIndex(
(pos.Z - m_pmin.Z) * m_csize.X + (pos.X - m_pmin.X),
pos.Y);
}
Biome *BiomeGenOriginal::getBiomeAtIndex(size_t index, s16 y) const
{
return calcBiomeFromNoise(
noise_heat->result[index],
noise_humidity->result[index],
y);
}
Biome *BiomeGenOriginal::calcBiomeFromNoise(float heat, float humidity, s16 y) const
{
Biome *b, *biome_closest = NULL;
float dist_min = FLT_MAX;
for (size_t i = 1; i < m_bmgr->getNumObjects(); i++) {
b = (Biome *)m_bmgr->getRaw(i);
if (!b || y > b->y_max || y < b->y_min)
continue;
float d_heat = heat - b->heat_point;
float d_humidity = humidity - b->humidity_point;
float dist = (d_heat * d_heat) +
(d_humidity * d_humidity);
if (dist < dist_min) {
dist_min = dist;
biome_closest = b;
}
}
return biome_closest ? biome_closest : (Biome *)m_bmgr->getRaw(BIOME_NONE);
}
////////////////////////////////////////////////////////////////////////////////
void Biome::resolveNodeNames()
{

View File

@ -22,6 +22,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "objdef.h"
#include "nodedef.h"
#include "noise.h"
class Settings;
class BiomeManager;
////
//// Biome
////
#define BIOME_NONE ((u8)0)
enum BiomeType
{
@ -56,10 +66,122 @@ class Biome : public ObjDef, public NodeResolver {
virtual void resolveNodeNames();
};
////
//// BiomeGen
////
enum BiomeGenType {
BIOMEGEN_ORIGINAL,
};
struct BiomeParams {
virtual void readParams(const Settings *settings) = 0;
virtual void writeParams(Settings *settings) const = 0;
virtual ~BiomeParams() {}
int seed;
};
class BiomeGen {
public:
virtual ~BiomeGen() {}
virtual BiomeGenType getType() const = 0;
// Calculates the biome at the exact position provided. This function can
// be called at any time, but may be less efficient than the latter methods,
// depending on implementation.
virtual Biome *calcBiomeAtPoint(v3s16 pos) const = 0;
// Computes any intermediate results needed for biome generation. Must be
// called before using any of: getBiomes, getBiomeAtPoint, or getBiomeAtIndex.
// Calling this invalidates the previous results stored in biomemap.
virtual void calcBiomeNoise(v3s16 pmin) = 0;
// Gets all biomes in current chunk using each corresponding element of
// heightmap as the y position, then stores the results by biome index in
// biomemap (also returned)
virtual u8 *getBiomes(s16 *heightmap) = 0;
// Gets a single biome at the specified position, which must be contained
// in the region formed by m_pmin and (m_pmin + m_csize - 1).
virtual Biome *getBiomeAtPoint(v3s16 pos) const = 0;
// Same as above, but uses a raw numeric index correlating to the (x,z) position.
virtual Biome *getBiomeAtIndex(size_t index, s16 y) const = 0;
// Result of calcBiomes bulk computation.
u8 *biomemap;
protected:
BiomeManager *m_bmgr;
v3s16 m_pmin;
v3s16 m_csize;
};
////
//// BiomeGen implementations
////
//
// Original biome algorithm (Whittaker's classification + surface height)
//
struct BiomeParamsOriginal : public BiomeParams {
BiomeParamsOriginal() :
np_heat(50, 50, v3f(750.0, 750.0, 750.0), 5349, 3, 0.5, 2.0),
np_humidity(50, 50, v3f(750.0, 750.0, 750.0), 842, 3, 0.5, 2.0),
np_heat_blend(0, 1.5, v3f(8.0, 8.0, 8.0), 13, 2, 1.0, 2.0),
np_humidity_blend(0, 1.5, v3f(8.0, 8.0, 8.0), 90003, 2, 1.0, 2.0)
{
}
virtual void readParams(const Settings *settings);
virtual void writeParams(Settings *settings) const;
NoiseParams np_heat;
NoiseParams np_humidity;
NoiseParams np_heat_blend;
NoiseParams np_humidity_blend;
};
class BiomeGenOriginal : public BiomeGen {
public:
BiomeGenOriginal(BiomeManager *biomemgr,
BiomeParamsOriginal *params, v3s16 chunksize);
virtual ~BiomeGenOriginal();
BiomeGenType getType() const { return BIOMEGEN_ORIGINAL; }
Biome *calcBiomeAtPoint(v3s16 pos) const;
void calcBiomeNoise(v3s16 pmin);
u8 *getBiomes(s16 *heightmap);
Biome *getBiomeAtPoint(v3s16 pos) const;
Biome *getBiomeAtIndex(size_t index, s16 y) const;
Biome *calcBiomeFromNoise(float heat, float humidity, s16 y) const;
float *heatmap;
float *humidmap;
private:
BiomeParamsOriginal *m_params;
Noise *noise_heat;
Noise *noise_humidity;
Noise *noise_heat_blend;
Noise *noise_humidity_blend;
};
////
//// BiomeManager
////
class BiomeManager : public ObjDefManager {
public:
static const char *OBJECT_TITLE;
BiomeManager(IGameDef *gamedef);
virtual ~BiomeManager();
@ -73,15 +195,36 @@ class BiomeManager : public ObjDefManager {
return new Biome;
}
BiomeGen *createBiomeGen(BiomeGenType type, BiomeParams *params, v3s16 chunksize)
{
switch (type) {
case BIOMEGEN_ORIGINAL:
return new BiomeGenOriginal(this,
(BiomeParamsOriginal *)params, chunksize);
default:
return NULL;
}
}
static BiomeParams *createBiomeParams(BiomeGenType type)
{
switch (type) {
case BIOMEGEN_ORIGINAL:
return new BiomeParamsOriginal;
default:
return NULL;
}
}
virtual void clear();
void calcBiomes(s16 sx, s16 sy, float *heat_map, float *humidity_map,
s16 *height_map, u8 *biomeid_map);
Biome *getBiome(float heat, float humidity, s16 y);
// Looks for pos in the biome cache, and if non-existent, looks up by noise
u8 getBiomeAtPoint(v3s16 pos);
private:
IGameDef *m_gamedef;
};
#endif
#endif

View File

@ -528,24 +528,26 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L)
return 1;
}
case MGOBJ_BIOMEMAP: {
if (!mg->biomemap)
if (!mg->biomegen)
return 0;
lua_newtable(L);
for (size_t i = 0; i != maplen; i++) {
lua_pushinteger(L, mg->biomemap[i]);
lua_pushinteger(L, mg->biomegen->biomemap[i]);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
case MGOBJ_HEATMAP: {
if (!mg->heatmap)
if (!mg->biomegen || mg->biomegen->getType() != BIOMEGEN_ORIGINAL)
return 0;
BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen;
lua_newtable(L);
for (size_t i = 0; i != maplen; i++) {
lua_pushnumber(L, mg->heatmap[i]);
lua_pushnumber(L, bg->heatmap[i]);
lua_rawseti(L, -2, i + 1);
}
@ -553,12 +555,14 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L)
}
case MGOBJ_HUMIDMAP: {
if (!mg->humidmap)
if (!mg->biomegen || mg->biomegen->getType() != BIOMEGEN_ORIGINAL)
return 0;
BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen;
lua_newtable(L);
for (size_t i = 0; i != maplen; i++) {
lua_pushnumber(L, mg->humidmap[i]);
lua_pushnumber(L, bg->humidmap[i]);
lua_rawseti(L, -2, i + 1);
}