mirror of
https://github.com/minetest/minetest.git
synced 2024-10-05 09:23:17 +02:00
b1b40fef16
This allows games to specify biome cave liquids and avoid the old hardcoded behaviour, but preserves the ability to have multiple cave liquids in one biome, such as lava and water. When multiple cave liquids are defined by the biome definition, make each entire cave use a randomly chosen liquid, instead of every small cave segment using a randomly chosen liquid. Plus an optimisation: Don't place nodes if cave liquid is defined as 'air'
339 lines
11 KiB
C++
339 lines
11 KiB
C++
/*
|
|
Minetest
|
|
Copyright (C) 2014-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
|
Copyright (C) 2014-2018 paramat
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "mg_biome.h"
|
|
#include "mg_decoration.h"
|
|
#include "emerge.h"
|
|
#include "server.h"
|
|
#include "nodedef.h"
|
|
#include "map.h" //for MMVManip
|
|
#include "util/numeric.h"
|
|
#include "porting.h"
|
|
#include "settings.h"
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
BiomeManager::BiomeManager(Server *server) :
|
|
ObjDefManager(server, OBJDEF_BIOME)
|
|
{
|
|
m_server = server;
|
|
|
|
// Create default biome to be used in case none exist
|
|
Biome *b = new Biome;
|
|
|
|
b->name = "default";
|
|
b->flags = 0;
|
|
b->depth_top = 0;
|
|
b->depth_filler = -MAX_MAP_GENERATION_LIMIT;
|
|
b->depth_water_top = 0;
|
|
b->depth_riverbed = 0;
|
|
b->min_pos = v3s16(-MAX_MAP_GENERATION_LIMIT,
|
|
-MAX_MAP_GENERATION_LIMIT, -MAX_MAP_GENERATION_LIMIT);
|
|
b->max_pos = v3s16(MAX_MAP_GENERATION_LIMIT,
|
|
MAX_MAP_GENERATION_LIMIT, MAX_MAP_GENERATION_LIMIT);
|
|
b->heat_point = 0.0;
|
|
b->humidity_point = 0.0;
|
|
b->vertical_blend = 0;
|
|
|
|
b->m_nodenames.emplace_back("mapgen_stone");
|
|
b->m_nodenames.emplace_back("mapgen_stone");
|
|
b->m_nodenames.emplace_back("mapgen_stone");
|
|
b->m_nodenames.emplace_back("mapgen_water_source");
|
|
b->m_nodenames.emplace_back("mapgen_water_source");
|
|
b->m_nodenames.emplace_back("mapgen_river_water_source");
|
|
b->m_nodenames.emplace_back("mapgen_stone");
|
|
b->m_nodenames.emplace_back("ignore");
|
|
b->m_nodenames.emplace_back("ignore");
|
|
b->m_nnlistsizes.push_back(1);
|
|
b->m_nodenames.emplace_back("ignore");
|
|
b->m_nodenames.emplace_back("ignore");
|
|
b->m_nodenames.emplace_back("ignore");
|
|
m_ndef->pendNodeResolve(b);
|
|
|
|
add(b);
|
|
}
|
|
|
|
|
|
void BiomeManager::clear()
|
|
{
|
|
EmergeManager *emerge = m_server->getEmergeManager();
|
|
|
|
// Remove all dangling references in Decorations
|
|
DecorationManager *decomgr = emerge->decomgr;
|
|
for (size_t i = 0; i != decomgr->getNumObjects(); i++) {
|
|
Decoration *deco = (Decoration *)decomgr->getRaw(i);
|
|
deco->biomes.clear();
|
|
}
|
|
|
|
// Don't delete the first biome
|
|
for (size_t i = 1; i < m_objects.size(); i++)
|
|
delete (Biome *)m_objects[i];
|
|
|
|
m_objects.resize(1);
|
|
}
|
|
|
|
|
|
// For BiomeGen type 'BiomeGenOriginal'
|
|
float BiomeManager::getHeatAtPosOriginal(v3s16 pos, NoiseParams &np_heat,
|
|
NoiseParams &np_heat_blend, u64 seed)
|
|
{
|
|
return
|
|
NoisePerlin2D(&np_heat, pos.X, pos.Z, seed) +
|
|
NoisePerlin2D(&np_heat_blend, pos.X, pos.Z, seed);
|
|
}
|
|
|
|
|
|
// For BiomeGen type 'BiomeGenOriginal'
|
|
float BiomeManager::getHumidityAtPosOriginal(v3s16 pos, NoiseParams &np_humidity,
|
|
NoiseParams &np_humidity_blend, u64 seed)
|
|
{
|
|
return
|
|
NoisePerlin2D(&np_humidity, pos.X, pos.Z, seed) +
|
|
NoisePerlin2D(&np_humidity_blend, pos.X, pos.Z, seed);
|
|
}
|
|
|
|
|
|
// For BiomeGen type 'BiomeGenOriginal'
|
|
Biome *BiomeManager::getBiomeFromNoiseOriginal(float heat, float humidity, v3s16 pos)
|
|
{
|
|
Biome *biome_closest = nullptr;
|
|
Biome *biome_closest_blend = nullptr;
|
|
float dist_min = FLT_MAX;
|
|
float dist_min_blend = FLT_MAX;
|
|
|
|
for (size_t i = 1; i < getNumObjects(); i++) {
|
|
Biome *b = (Biome *)getRaw(i);
|
|
if (!b ||
|
|
pos.Y < b->min_pos.Y || pos.Y > b->max_pos.Y + b->vertical_blend ||
|
|
pos.X < b->min_pos.X || pos.X > b->max_pos.X ||
|
|
pos.Z < b->min_pos.Z || pos.Z > b->max_pos.Z)
|
|
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 (pos.Y <= b->max_pos.Y) { // Within y limits of biome b
|
|
if (dist < dist_min) {
|
|
dist_min = dist;
|
|
biome_closest = b;
|
|
}
|
|
} else if (dist < dist_min_blend) { // Blend area above biome b
|
|
dist_min_blend = dist;
|
|
biome_closest_blend = b;
|
|
}
|
|
}
|
|
|
|
mysrand(pos.Y + (heat + humidity) * 0.9f);
|
|
if (biome_closest_blend && dist_min_blend <= dist_min &&
|
|
myrand_range(0, biome_closest_blend->vertical_blend) >=
|
|
pos.Y - biome_closest_blend->max_pos.Y)
|
|
return biome_closest_blend;
|
|
|
|
return (biome_closest) ? biome_closest : (Biome *)getRaw(BIOME_NONE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
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(¶ms->np_heat,
|
|
params->seed, m_csize.X, m_csize.Z);
|
|
noise_humidity = new Noise(¶ms->np_humidity,
|
|
params->seed, m_csize.X, m_csize.Z);
|
|
noise_heat_blend = new Noise(¶ms->np_heat_blend,
|
|
params->seed, m_csize.X, m_csize.Z);
|
|
noise_humidity_blend = new Noise(¶ms->np_humidity_blend,
|
|
params->seed, m_csize.X, m_csize.Z);
|
|
|
|
heatmap = noise_heat->result;
|
|
humidmap = noise_humidity->result;
|
|
|
|
biomemap = new biome_t[m_csize.X * m_csize.Z];
|
|
// Initialise with the ID of 'BIOME_NONE' so that cavegen can get the
|
|
// fallback biome when biome generation (which calculates the biomemap IDs)
|
|
// is disabled.
|
|
memset(biomemap, 0, sizeof(biome_t) * m_csize.X * m_csize.Z);
|
|
}
|
|
|
|
BiomeGenOriginal::~BiomeGenOriginal()
|
|
{
|
|
delete []biomemap;
|
|
|
|
delete noise_heat;
|
|
delete noise_humidity;
|
|
delete noise_heat_blend;
|
|
delete noise_humidity_blend;
|
|
}
|
|
|
|
// Only usable in a mapgen thread
|
|
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);
|
|
}
|
|
|
|
|
|
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];
|
|
}
|
|
}
|
|
|
|
|
|
biome_t *BiomeGenOriginal::getBiomes(s16 *heightmap, v3s16 pmin)
|
|
{
|
|
for (s16 zr = 0; zr < m_csize.Z; zr++)
|
|
for (s16 xr = 0; xr < m_csize.X; xr++) {
|
|
s32 i = zr * m_csize.X + xr;
|
|
Biome *biome = calcBiomeFromNoise(
|
|
noise_heat->result[i],
|
|
noise_humidity->result[i],
|
|
v3s16(pmin.X + xr, heightmap[i], pmin.Z + zr));
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
Biome *BiomeGenOriginal::getBiomeAtIndex(size_t index, v3s16 pos) const
|
|
{
|
|
return calcBiomeFromNoise(
|
|
noise_heat->result[index],
|
|
noise_humidity->result[index],
|
|
pos);
|
|
}
|
|
|
|
|
|
Biome *BiomeGenOriginal::calcBiomeFromNoise(float heat, float humidity, v3s16 pos) const
|
|
{
|
|
Biome *biome_closest = nullptr;
|
|
Biome *biome_closest_blend = nullptr;
|
|
float dist_min = FLT_MAX;
|
|
float dist_min_blend = FLT_MAX;
|
|
|
|
for (size_t i = 1; i < m_bmgr->getNumObjects(); i++) {
|
|
Biome *b = (Biome *)m_bmgr->getRaw(i);
|
|
if (!b ||
|
|
pos.Y < b->min_pos.Y || pos.Y > b->max_pos.Y + b->vertical_blend ||
|
|
pos.X < b->min_pos.X || pos.X > b->max_pos.X ||
|
|
pos.Z < b->min_pos.Z || pos.Z > b->max_pos.Z)
|
|
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 (pos.Y <= b->max_pos.Y) { // Within y limits of biome b
|
|
if (dist < dist_min) {
|
|
dist_min = dist;
|
|
biome_closest = b;
|
|
}
|
|
} else if (dist < dist_min_blend) { // Blend area above biome b
|
|
dist_min_blend = dist;
|
|
biome_closest_blend = b;
|
|
}
|
|
}
|
|
|
|
// Carefully tune pseudorandom seed variation to avoid single node dither
|
|
// and create larger scale blending patterns similar to horizontal biome
|
|
// blend.
|
|
mysrand(pos.Y + (heat + humidity) * 0.9f);
|
|
|
|
if (biome_closest_blend && dist_min_blend <= dist_min &&
|
|
myrand_range(0, biome_closest_blend->vertical_blend) >=
|
|
pos.Y - biome_closest_blend->max_pos.Y)
|
|
return biome_closest_blend;
|
|
|
|
return (biome_closest) ? biome_closest : (Biome *)m_bmgr->getRaw(BIOME_NONE);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Biome::resolveNodeNames()
|
|
{
|
|
getIdFromNrBacklog(&c_top, "mapgen_stone", CONTENT_AIR, false);
|
|
getIdFromNrBacklog(&c_filler, "mapgen_stone", CONTENT_AIR, false);
|
|
getIdFromNrBacklog(&c_stone, "mapgen_stone", CONTENT_AIR, false);
|
|
getIdFromNrBacklog(&c_water_top, "mapgen_water_source", CONTENT_AIR, false);
|
|
getIdFromNrBacklog(&c_water, "mapgen_water_source", CONTENT_AIR, false);
|
|
getIdFromNrBacklog(&c_river_water, "mapgen_river_water_source", CONTENT_AIR, false);
|
|
getIdFromNrBacklog(&c_riverbed, "mapgen_stone", CONTENT_AIR, false);
|
|
getIdFromNrBacklog(&c_dust, "ignore", CONTENT_IGNORE, false);
|
|
getIdsFromNrBacklog(&c_cave_liquid);
|
|
getIdFromNrBacklog(&c_dungeon, "ignore", CONTENT_IGNORE, false);
|
|
getIdFromNrBacklog(&c_dungeon_alt, "ignore", CONTENT_IGNORE, false);
|
|
getIdFromNrBacklog(&c_dungeon_stair, "ignore", CONTENT_IGNORE, false);
|
|
}
|