mirror of
https://github.com/luanti-org/luanti.git
synced 2025-08-01 17:38:41 +00:00
parent
fc9747eb4b
commit
20a85d76d9
118 changed files with 236 additions and 221 deletions
19
src/mapgen/CMakeLists.txt
Normal file
19
src/mapgen/CMakeLists.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
set(mapgen_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cavegen.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dungeongen.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mapgen_carpathian.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mapgen.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mapgen_flat.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mapgen_fractal.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mapgen_singlenode.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mapgen_v5.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mapgen_v6.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mapgen_v7.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mapgen_valleys.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mg_biome.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mg_decoration.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mg_ore.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mg_schematic.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/treegen.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
882
src/mapgen/cavegen.cpp
Normal file
882
src/mapgen/cavegen.cpp
Normal file
|
@ -0,0 +1,882 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
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 "util/numeric.h"
|
||||
#include "map.h"
|
||||
#include "mapgen.h"
|
||||
#include "mapgen_v5.h"
|
||||
#include "mapgen_v6.h"
|
||||
#include "mapgen_v7.h"
|
||||
#include "mg_biome.h"
|
||||
#include "cavegen.h"
|
||||
|
||||
static NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0);
|
||||
|
||||
|
||||
////
|
||||
//// CavesNoiseIntersection
|
||||
////
|
||||
|
||||
CavesNoiseIntersection::CavesNoiseIntersection(
|
||||
INodeDefManager *nodedef, BiomeManager *biomemgr, v3s16 chunksize,
|
||||
NoiseParams *np_cave1, NoiseParams *np_cave2, s32 seed, float cave_width)
|
||||
{
|
||||
assert(nodedef);
|
||||
assert(biomemgr);
|
||||
|
||||
m_ndef = nodedef;
|
||||
m_bmgr = biomemgr;
|
||||
|
||||
m_csize = chunksize;
|
||||
m_cave_width = cave_width;
|
||||
|
||||
m_ystride = m_csize.X;
|
||||
m_zstride_1d = m_csize.X * (m_csize.Y + 1);
|
||||
|
||||
// Noises are created using 1-down overgeneration
|
||||
// A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
|
||||
// re-carving the solid overtop placed for blocking sunlight
|
||||
noise_cave1 = new Noise(np_cave1, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
|
||||
noise_cave2 = new Noise(np_cave2, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
|
||||
}
|
||||
|
||||
|
||||
CavesNoiseIntersection::~CavesNoiseIntersection()
|
||||
{
|
||||
delete noise_cave1;
|
||||
delete noise_cave2;
|
||||
}
|
||||
|
||||
|
||||
void CavesNoiseIntersection::generateCaves(MMVManip *vm,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap)
|
||||
{
|
||||
assert(vm);
|
||||
assert(biomemap);
|
||||
|
||||
noise_cave1->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
|
||||
noise_cave2->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
|
||||
|
||||
const v3s16 &em = vm->m_area.getExtent();
|
||||
u32 index2d = 0; // Biomemap index
|
||||
|
||||
for (s16 z = nmin.Z; z <= nmax.Z; z++)
|
||||
for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
|
||||
bool column_is_open = false; // Is column open to overground
|
||||
bool is_under_river = false; // Is column under river water
|
||||
bool is_under_tunnel = false; // Is tunnel or is under tunnel
|
||||
bool is_top_filler_above = false; // Is top or filler above node
|
||||
// Indexes at column top
|
||||
u32 vi = vm->m_area.index(x, nmax.Y, z);
|
||||
u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
|
||||
(x - nmin.X); // 3D noise index
|
||||
// Biome of column
|
||||
Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]);
|
||||
u16 depth_top = biome->depth_top;
|
||||
u16 base_filler = depth_top + biome->depth_filler;
|
||||
u16 depth_riverbed = biome->depth_riverbed;
|
||||
u16 nplaced = 0;
|
||||
// Don't excavate the overgenerated stone at nmax.Y + 1,
|
||||
// this creates a 'roof' over the tunnel, preventing light in
|
||||
// tunnels at mapchunk borders when generating mapchunks upwards.
|
||||
// This 'roof' is removed when the mapchunk above is generated.
|
||||
for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
|
||||
index3d -= m_ystride,
|
||||
vm->m_area.add_y(em, vi, -1)) {
|
||||
content_t c = vm->m_data[vi].getContent();
|
||||
|
||||
if (c == CONTENT_AIR || c == biome->c_water_top ||
|
||||
c == biome->c_water) {
|
||||
column_is_open = true;
|
||||
is_top_filler_above = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == biome->c_river_water) {
|
||||
column_is_open = true;
|
||||
is_under_river = true;
|
||||
is_top_filler_above = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ground
|
||||
float d1 = contour(noise_cave1->result[index3d]);
|
||||
float d2 = contour(noise_cave2->result[index3d]);
|
||||
|
||||
if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) {
|
||||
// In tunnel and ground content, excavate
|
||||
vm->m_data[vi] = MapNode(CONTENT_AIR);
|
||||
is_under_tunnel = true;
|
||||
// If tunnel roof is top or filler, replace with stone
|
||||
if (is_top_filler_above)
|
||||
vm->m_data[vi + em.X] = MapNode(biome->c_stone);
|
||||
is_top_filler_above = false;
|
||||
} else if (column_is_open && is_under_tunnel &&
|
||||
(c == biome->c_stone || c == biome->c_filler)) {
|
||||
// Tunnel entrance floor, place biome surface nodes
|
||||
if (is_under_river) {
|
||||
if (nplaced < depth_riverbed) {
|
||||
vm->m_data[vi] = MapNode(biome->c_riverbed);
|
||||
is_top_filler_above = true;
|
||||
nplaced++;
|
||||
} else {
|
||||
// Disable top/filler placement
|
||||
column_is_open = false;
|
||||
is_under_river = false;
|
||||
is_under_tunnel = false;
|
||||
}
|
||||
} else if (nplaced < depth_top) {
|
||||
vm->m_data[vi] = MapNode(biome->c_top);
|
||||
is_top_filler_above = true;
|
||||
nplaced++;
|
||||
} else if (nplaced < base_filler) {
|
||||
vm->m_data[vi] = MapNode(biome->c_filler);
|
||||
is_top_filler_above = true;
|
||||
nplaced++;
|
||||
} else {
|
||||
// Disable top/filler placement
|
||||
column_is_open = false;
|
||||
is_under_tunnel = false;
|
||||
}
|
||||
} else {
|
||||
// Not tunnel or tunnel entrance floor
|
||||
// Check node for possible replacing with stone for tunnel roof
|
||||
if (c == biome->c_top || c == biome->c_filler)
|
||||
is_top_filler_above = true;
|
||||
|
||||
column_is_open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// CavernsNoise
|
||||
////
|
||||
|
||||
CavernsNoise::CavernsNoise(
|
||||
INodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern,
|
||||
s32 seed, float cavern_limit, float cavern_taper, float cavern_threshold)
|
||||
{
|
||||
assert(nodedef);
|
||||
|
||||
m_ndef = nodedef;
|
||||
|
||||
m_csize = chunksize;
|
||||
m_cavern_limit = cavern_limit;
|
||||
m_cavern_taper = cavern_taper;
|
||||
m_cavern_threshold = cavern_threshold;
|
||||
|
||||
m_ystride = m_csize.X;
|
||||
m_zstride_1d = m_csize.X * (m_csize.Y + 1);
|
||||
|
||||
// Noise is created using 1-down overgeneration
|
||||
// A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
|
||||
// re-carving the solid overtop placed for blocking sunlight
|
||||
noise_cavern = new Noise(np_cavern, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
|
||||
|
||||
c_water_source = m_ndef->getId("mapgen_water_source");
|
||||
if (c_water_source == CONTENT_IGNORE)
|
||||
c_water_source = CONTENT_AIR;
|
||||
|
||||
c_lava_source = m_ndef->getId("mapgen_lava_source");
|
||||
if (c_lava_source == CONTENT_IGNORE)
|
||||
c_lava_source = CONTENT_AIR;
|
||||
}
|
||||
|
||||
|
||||
CavernsNoise::~CavernsNoise()
|
||||
{
|
||||
delete noise_cavern;
|
||||
}
|
||||
|
||||
|
||||
bool CavernsNoise::generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax)
|
||||
{
|
||||
assert(vm);
|
||||
|
||||
// Calculate noise
|
||||
noise_cavern->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
|
||||
|
||||
// Cache cavern_amp values
|
||||
float *cavern_amp = new float[m_csize.Y + 1];
|
||||
u8 cavern_amp_index = 0; // Index zero at column top
|
||||
for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, cavern_amp_index++) {
|
||||
cavern_amp[cavern_amp_index] =
|
||||
MYMIN((m_cavern_limit - y) / (float)m_cavern_taper, 1.0f);
|
||||
}
|
||||
|
||||
//// Place nodes
|
||||
bool near_cavern = false;
|
||||
const v3s16 &em = vm->m_area.getExtent();
|
||||
u32 index2d = 0;
|
||||
|
||||
for (s16 z = nmin.Z; z <= nmax.Z; z++)
|
||||
for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
|
||||
// Reset cave_amp index to column top
|
||||
cavern_amp_index = 0;
|
||||
// Initial voxelmanip index at column top
|
||||
u32 vi = vm->m_area.index(x, nmax.Y, z);
|
||||
// Initial 3D noise index at column top
|
||||
u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
|
||||
(x - nmin.X);
|
||||
// Don't excavate the overgenerated stone at node_max.Y + 1,
|
||||
// this creates a 'roof' over the cavern, preventing light in
|
||||
// caverns at mapchunk borders when generating mapchunks upwards.
|
||||
// This 'roof' is excavated when the mapchunk above is generated.
|
||||
for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
|
||||
index3d -= m_ystride,
|
||||
vm->m_area.add_y(em, vi, -1),
|
||||
cavern_amp_index++) {
|
||||
content_t c = vm->m_data[vi].getContent();
|
||||
float n_absamp_cavern = fabs(noise_cavern->result[index3d]) *
|
||||
cavern_amp[cavern_amp_index];
|
||||
// Disable CavesRandomWalk at a safe distance from caverns
|
||||
// to avoid excessively spreading liquids in caverns.
|
||||
if (n_absamp_cavern > m_cavern_threshold - 0.1f) {
|
||||
near_cavern = true;
|
||||
if (n_absamp_cavern > m_cavern_threshold &&
|
||||
m_ndef->get(c).is_ground_content)
|
||||
vm->m_data[vi] = MapNode(CONTENT_AIR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] cavern_amp;
|
||||
|
||||
return near_cavern;
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// CavesRandomWalk
|
||||
////
|
||||
|
||||
CavesRandomWalk::CavesRandomWalk(
|
||||
INodeDefManager *ndef,
|
||||
GenerateNotifier *gennotify,
|
||||
s32 seed,
|
||||
int water_level,
|
||||
content_t water_source,
|
||||
content_t lava_source,
|
||||
int lava_depth)
|
||||
{
|
||||
assert(ndef);
|
||||
|
||||
this->ndef = ndef;
|
||||
this->gennotify = gennotify;
|
||||
this->seed = seed;
|
||||
this->water_level = water_level;
|
||||
this->np_caveliquids = &nparams_caveliquids;
|
||||
this->lava_depth = lava_depth;
|
||||
|
||||
c_water_source = water_source;
|
||||
if (c_water_source == CONTENT_IGNORE)
|
||||
c_water_source = ndef->getId("mapgen_water_source");
|
||||
if (c_water_source == CONTENT_IGNORE)
|
||||
c_water_source = CONTENT_AIR;
|
||||
|
||||
c_lava_source = lava_source;
|
||||
if (c_lava_source == CONTENT_IGNORE)
|
||||
c_lava_source = ndef->getId("mapgen_lava_source");
|
||||
if (c_lava_source == CONTENT_IGNORE)
|
||||
c_lava_source = CONTENT_AIR;
|
||||
}
|
||||
|
||||
|
||||
void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
|
||||
PseudoRandom *ps, bool is_large_cave, int max_stone_height, s16 *heightmap)
|
||||
{
|
||||
assert(vm);
|
||||
assert(ps);
|
||||
|
||||
this->vm = vm;
|
||||
this->ps = ps;
|
||||
this->node_min = nmin;
|
||||
this->node_max = nmax;
|
||||
this->heightmap = heightmap;
|
||||
this->large_cave = is_large_cave;
|
||||
|
||||
this->ystride = nmax.X - nmin.X + 1;
|
||||
|
||||
// Set initial parameters from randomness
|
||||
int dswitchint = ps->range(1, 14);
|
||||
flooded = ps->range(1, 2) == 2;
|
||||
|
||||
if (large_cave) {
|
||||
part_max_length_rs = ps->range(2, 4);
|
||||
tunnel_routepoints = ps->range(5, ps->range(15, 30));
|
||||
min_tunnel_diameter = 5;
|
||||
max_tunnel_diameter = ps->range(7, ps->range(8, 24));
|
||||
} else {
|
||||
part_max_length_rs = ps->range(2, 9);
|
||||
tunnel_routepoints = ps->range(10, ps->range(15, 30));
|
||||
min_tunnel_diameter = 2;
|
||||
max_tunnel_diameter = ps->range(2, 6);
|
||||
}
|
||||
|
||||
large_cave_is_flat = (ps->range(0, 1) == 0);
|
||||
|
||||
main_direction = v3f(0, 0, 0);
|
||||
|
||||
// Allowed route area size in nodes
|
||||
ar = node_max - node_min + v3s16(1, 1, 1);
|
||||
// Area starting point in nodes
|
||||
of = node_min;
|
||||
|
||||
// Allow a bit more
|
||||
//(this should be more than the maximum radius of the tunnel)
|
||||
const s16 insure = 10;
|
||||
s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
|
||||
ar += v3s16(1, 0, 1) * more * 2;
|
||||
of -= v3s16(1, 0, 1) * more;
|
||||
|
||||
route_y_min = 0;
|
||||
// Allow half a diameter + 7 over stone surface
|
||||
route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
|
||||
|
||||
// Limit maximum to area
|
||||
route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
|
||||
|
||||
if (large_cave) {
|
||||
s16 minpos = 0;
|
||||
if (node_min.Y < water_level && node_max.Y > water_level) {
|
||||
minpos = water_level - max_tunnel_diameter / 3 - of.Y;
|
||||
route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
|
||||
}
|
||||
route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
|
||||
route_y_min = rangelim(route_y_min, 0, route_y_max);
|
||||
}
|
||||
|
||||
s16 route_start_y_min = route_y_min;
|
||||
s16 route_start_y_max = route_y_max;
|
||||
|
||||
route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
|
||||
route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
|
||||
|
||||
// Randomize starting position
|
||||
orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
|
||||
orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
|
||||
orp.X = (float)(ps->next() % ar.X) + 0.5f;
|
||||
|
||||
// Add generation notify begin event
|
||||
if (gennotify) {
|
||||
v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
|
||||
GenNotifyType notifytype = large_cave ?
|
||||
GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
|
||||
gennotify->addEvent(notifytype, abs_pos);
|
||||
}
|
||||
|
||||
// Generate some tunnel starting from orp
|
||||
for (u16 j = 0; j < tunnel_routepoints; j++)
|
||||
makeTunnel(j % dswitchint == 0);
|
||||
|
||||
// Add generation notify end event
|
||||
if (gennotify) {
|
||||
v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
|
||||
GenNotifyType notifytype = large_cave ?
|
||||
GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
|
||||
gennotify->addEvent(notifytype, abs_pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CavesRandomWalk::makeTunnel(bool dirswitch)
|
||||
{
|
||||
if (dirswitch && !large_cave) {
|
||||
main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
|
||||
main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
|
||||
main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
|
||||
|
||||
main_direction *= (float)ps->range(0, 10) / 10;
|
||||
}
|
||||
|
||||
// Randomize size
|
||||
s16 min_d = min_tunnel_diameter;
|
||||
s16 max_d = max_tunnel_diameter;
|
||||
rs = ps->range(min_d, max_d);
|
||||
s16 rs_part_max_length_rs = rs * part_max_length_rs;
|
||||
|
||||
v3s16 maxlen;
|
||||
if (large_cave) {
|
||||
maxlen = v3s16(
|
||||
rs_part_max_length_rs,
|
||||
rs_part_max_length_rs / 2,
|
||||
rs_part_max_length_rs
|
||||
);
|
||||
} else {
|
||||
maxlen = v3s16(
|
||||
rs_part_max_length_rs,
|
||||
ps->range(1, rs_part_max_length_rs),
|
||||
rs_part_max_length_rs
|
||||
);
|
||||
}
|
||||
|
||||
v3f vec;
|
||||
// Jump downward sometimes
|
||||
if (!large_cave && ps->range(0, 12) == 0) {
|
||||
vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
|
||||
vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
|
||||
vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
|
||||
} else {
|
||||
vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
|
||||
vec.Y = (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2;
|
||||
vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
|
||||
}
|
||||
|
||||
// Do not make caves that are above ground.
|
||||
// It is only necessary to check the startpoint and endpoint.
|
||||
v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
|
||||
v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
|
||||
if (isPosAboveSurface(p1) || isPosAboveSurface(p2))
|
||||
return;
|
||||
|
||||
vec += main_direction;
|
||||
|
||||
v3f rp = orp + vec;
|
||||
if (rp.X < 0)
|
||||
rp.X = 0;
|
||||
else if (rp.X >= ar.X)
|
||||
rp.X = ar.X - 1;
|
||||
|
||||
if (rp.Y < route_y_min)
|
||||
rp.Y = route_y_min;
|
||||
else if (rp.Y >= route_y_max)
|
||||
rp.Y = route_y_max - 1;
|
||||
|
||||
if (rp.Z < 0)
|
||||
rp.Z = 0;
|
||||
else if (rp.Z >= ar.Z)
|
||||
rp.Z = ar.Z - 1;
|
||||
|
||||
vec = rp - orp;
|
||||
|
||||
float veclen = vec.getLength();
|
||||
if (veclen < 0.05f)
|
||||
veclen = 1.0f;
|
||||
|
||||
// Every second section is rough
|
||||
bool randomize_xz = (ps->range(1, 2) == 1);
|
||||
|
||||
// Carve routes
|
||||
for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
|
||||
carveRoute(vec, f, randomize_xz);
|
||||
|
||||
orp = rp;
|
||||
}
|
||||
|
||||
|
||||
void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz)
|
||||
{
|
||||
MapNode airnode(CONTENT_AIR);
|
||||
MapNode waternode(c_water_source);
|
||||
MapNode lavanode(c_lava_source);
|
||||
|
||||
v3s16 startp(orp.X, orp.Y, orp.Z);
|
||||
startp += of;
|
||||
|
||||
float nval = NoisePerlin3D(np_caveliquids, startp.X,
|
||||
startp.Y, startp.Z, seed);
|
||||
MapNode liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ?
|
||||
lavanode : waternode;
|
||||
|
||||
v3f fp = orp + vec * f;
|
||||
fp.X += 0.1f * ps->range(-10, 10);
|
||||
fp.Z += 0.1f * ps->range(-10, 10);
|
||||
v3s16 cp(fp.X, fp.Y, fp.Z);
|
||||
|
||||
s16 d0 = -rs / 2;
|
||||
s16 d1 = d0 + rs;
|
||||
if (randomize_xz) {
|
||||
d0 += ps->range(-1, 1);
|
||||
d1 += ps->range(-1, 1);
|
||||
}
|
||||
|
||||
bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2;
|
||||
|
||||
for (s16 z0 = d0; z0 <= d1; z0++) {
|
||||
s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
|
||||
for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
|
||||
s16 maxabsxz = MYMAX(abs(x0), abs(z0));
|
||||
|
||||
s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
|
||||
|
||||
for (s16 y0 = -si2; y0 <= si2; y0++) {
|
||||
// Make better floors in small caves
|
||||
if (flat_cave_floor && y0 <= -rs / 2 && rs <= 7)
|
||||
continue;
|
||||
|
||||
if (large_cave_is_flat) {
|
||||
// Make large caves not so tall
|
||||
if (rs > 7 && abs(y0) >= rs / 3)
|
||||
continue;
|
||||
}
|
||||
|
||||
v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
|
||||
p += of;
|
||||
|
||||
if (!vm->m_area.contains(p))
|
||||
continue;
|
||||
|
||||
u32 i = vm->m_area.index(p);
|
||||
content_t c = vm->m_data[i].getContent();
|
||||
if (!ndef->get(c).is_ground_content)
|
||||
continue;
|
||||
|
||||
if (large_cave) {
|
||||
int full_ymin = node_min.Y - MAP_BLOCKSIZE;
|
||||
int full_ymax = node_max.Y + MAP_BLOCKSIZE;
|
||||
|
||||
if (flooded && full_ymin < water_level && full_ymax > water_level)
|
||||
vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
|
||||
else if (flooded && full_ymax < water_level)
|
||||
vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode;
|
||||
else
|
||||
vm->m_data[i] = airnode;
|
||||
} else {
|
||||
if (c == CONTENT_IGNORE)
|
||||
continue;
|
||||
|
||||
vm->m_data[i] = airnode;
|
||||
vm->m_flags[i] |= VMANIP_FLAG_CAVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline bool CavesRandomWalk::isPosAboveSurface(v3s16 p)
|
||||
{
|
||||
if (heightmap != NULL &&
|
||||
p.Z >= node_min.Z && p.Z <= node_max.Z &&
|
||||
p.X >= node_min.X && p.X <= node_max.X) {
|
||||
u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
|
||||
if (heightmap[index] < p.Y)
|
||||
return true;
|
||||
} else if (p.Y > water_level) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// CavesV6
|
||||
////
|
||||
|
||||
CavesV6::CavesV6(INodeDefManager *ndef, GenerateNotifier *gennotify,
|
||||
int water_level, content_t water_source, content_t lava_source)
|
||||
{
|
||||
assert(ndef);
|
||||
|
||||
this->ndef = ndef;
|
||||
this->gennotify = gennotify;
|
||||
this->water_level = water_level;
|
||||
|
||||
c_water_source = water_source;
|
||||
if (c_water_source == CONTENT_IGNORE)
|
||||
c_water_source = ndef->getId("mapgen_water_source");
|
||||
if (c_water_source == CONTENT_IGNORE)
|
||||
c_water_source = CONTENT_AIR;
|
||||
|
||||
c_lava_source = lava_source;
|
||||
if (c_lava_source == CONTENT_IGNORE)
|
||||
c_lava_source = ndef->getId("mapgen_lava_source");
|
||||
if (c_lava_source == CONTENT_IGNORE)
|
||||
c_lava_source = CONTENT_AIR;
|
||||
}
|
||||
|
||||
|
||||
void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
|
||||
PseudoRandom *ps, PseudoRandom *ps2,
|
||||
bool is_large_cave, int max_stone_height, s16 *heightmap)
|
||||
{
|
||||
assert(vm);
|
||||
assert(ps);
|
||||
assert(ps2);
|
||||
|
||||
this->vm = vm;
|
||||
this->ps = ps;
|
||||
this->ps2 = ps2;
|
||||
this->node_min = nmin;
|
||||
this->node_max = nmax;
|
||||
this->heightmap = heightmap;
|
||||
this->large_cave = is_large_cave;
|
||||
|
||||
this->ystride = nmax.X - nmin.X + 1;
|
||||
|
||||
// Set initial parameters from randomness
|
||||
min_tunnel_diameter = 2;
|
||||
max_tunnel_diameter = ps->range(2, 6);
|
||||
int dswitchint = ps->range(1, 14);
|
||||
if (large_cave) {
|
||||
part_max_length_rs = ps->range(2, 4);
|
||||
tunnel_routepoints = ps->range(5, ps->range(15, 30));
|
||||
min_tunnel_diameter = 5;
|
||||
max_tunnel_diameter = ps->range(7, ps->range(8, 24));
|
||||
} else {
|
||||
part_max_length_rs = ps->range(2, 9);
|
||||
tunnel_routepoints = ps->range(10, ps->range(15, 30));
|
||||
}
|
||||
large_cave_is_flat = (ps->range(0, 1) == 0);
|
||||
|
||||
main_direction = v3f(0, 0, 0);
|
||||
|
||||
// Allowed route area size in nodes
|
||||
ar = node_max - node_min + v3s16(1, 1, 1);
|
||||
// Area starting point in nodes
|
||||
of = node_min;
|
||||
|
||||
// Allow a bit more
|
||||
//(this should be more than the maximum radius of the tunnel)
|
||||
const s16 max_spread_amount = MAP_BLOCKSIZE;
|
||||
const s16 insure = 10;
|
||||
s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1);
|
||||
ar += v3s16(1, 0, 1) * more * 2;
|
||||
of -= v3s16(1, 0, 1) * more;
|
||||
|
||||
route_y_min = 0;
|
||||
// Allow half a diameter + 7 over stone surface
|
||||
route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7;
|
||||
|
||||
// Limit maximum to area
|
||||
route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
|
||||
|
||||
if (large_cave) {
|
||||
s16 minpos = 0;
|
||||
if (node_min.Y < water_level && node_max.Y > water_level) {
|
||||
minpos = water_level - max_tunnel_diameter / 3 - of.Y;
|
||||
route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
|
||||
}
|
||||
route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
|
||||
route_y_min = rangelim(route_y_min, 0, route_y_max);
|
||||
}
|
||||
|
||||
s16 route_start_y_min = route_y_min;
|
||||
s16 route_start_y_max = route_y_max;
|
||||
|
||||
route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
|
||||
route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
|
||||
|
||||
// Randomize starting position
|
||||
orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
|
||||
orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
|
||||
orp.X = (float)(ps->next() % ar.X) + 0.5f;
|
||||
|
||||
// Add generation notify begin event
|
||||
if (gennotify != NULL) {
|
||||
v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
|
||||
GenNotifyType notifytype = large_cave ?
|
||||
GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
|
||||
gennotify->addEvent(notifytype, abs_pos);
|
||||
}
|
||||
|
||||
// Generate some tunnel starting from orp
|
||||
for (u16 j = 0; j < tunnel_routepoints; j++)
|
||||
makeTunnel(j % dswitchint == 0);
|
||||
|
||||
// Add generation notify end event
|
||||
if (gennotify != NULL) {
|
||||
v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
|
||||
GenNotifyType notifytype = large_cave ?
|
||||
GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
|
||||
gennotify->addEvent(notifytype, abs_pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CavesV6::makeTunnel(bool dirswitch)
|
||||
{
|
||||
if (dirswitch && !large_cave) {
|
||||
main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
|
||||
main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
|
||||
main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
|
||||
|
||||
main_direction *= (float)ps->range(0, 10) / 10;
|
||||
}
|
||||
|
||||
// Randomize size
|
||||
s16 min_d = min_tunnel_diameter;
|
||||
s16 max_d = max_tunnel_diameter;
|
||||
rs = ps->range(min_d, max_d);
|
||||
s16 rs_part_max_length_rs = rs * part_max_length_rs;
|
||||
|
||||
v3s16 maxlen;
|
||||
if (large_cave) {
|
||||
maxlen = v3s16(
|
||||
rs_part_max_length_rs,
|
||||
rs_part_max_length_rs / 2,
|
||||
rs_part_max_length_rs
|
||||
);
|
||||
} else {
|
||||
maxlen = v3s16(
|
||||
rs_part_max_length_rs,
|
||||
ps->range(1, rs_part_max_length_rs),
|
||||
rs_part_max_length_rs
|
||||
);
|
||||
}
|
||||
|
||||
v3f vec;
|
||||
vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
|
||||
vec.Y = (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2;
|
||||
vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
|
||||
|
||||
// Jump downward sometimes
|
||||
if (!large_cave && ps->range(0, 12) == 0) {
|
||||
vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
|
||||
vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
|
||||
vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
|
||||
}
|
||||
|
||||
// Do not make caves that are entirely above ground, to fix shadow bugs
|
||||
// caused by overgenerated large caves.
|
||||
// It is only necessary to check the startpoint and endpoint.
|
||||
v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
|
||||
v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
|
||||
|
||||
// If startpoint and endpoint are above ground, disable placement of nodes
|
||||
// in carveRoute while still running all PseudoRandom calls to ensure caves
|
||||
// are consistent with existing worlds.
|
||||
bool tunnel_above_ground =
|
||||
p1.Y > getSurfaceFromHeightmap(p1) &&
|
||||
p2.Y > getSurfaceFromHeightmap(p2);
|
||||
|
||||
vec += main_direction;
|
||||
|
||||
v3f rp = orp + vec;
|
||||
if (rp.X < 0)
|
||||
rp.X = 0;
|
||||
else if (rp.X >= ar.X)
|
||||
rp.X = ar.X - 1;
|
||||
|
||||
if (rp.Y < route_y_min)
|
||||
rp.Y = route_y_min;
|
||||
else if (rp.Y >= route_y_max)
|
||||
rp.Y = route_y_max - 1;
|
||||
|
||||
if (rp.Z < 0)
|
||||
rp.Z = 0;
|
||||
else if (rp.Z >= ar.Z)
|
||||
rp.Z = ar.Z - 1;
|
||||
|
||||
vec = rp - orp;
|
||||
|
||||
float veclen = vec.getLength();
|
||||
// As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE
|
||||
if (veclen < 0.05f)
|
||||
veclen = 1.0f;
|
||||
|
||||
// Every second section is rough
|
||||
bool randomize_xz = (ps2->range(1, 2) == 1);
|
||||
|
||||
// Carve routes
|
||||
for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
|
||||
carveRoute(vec, f, randomize_xz, tunnel_above_ground);
|
||||
|
||||
orp = rp;
|
||||
}
|
||||
|
||||
|
||||
void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz,
|
||||
bool tunnel_above_ground)
|
||||
{
|
||||
MapNode airnode(CONTENT_AIR);
|
||||
MapNode waternode(c_water_source);
|
||||
MapNode lavanode(c_lava_source);
|
||||
|
||||
v3s16 startp(orp.X, orp.Y, orp.Z);
|
||||
startp += of;
|
||||
|
||||
v3f fp = orp + vec * f;
|
||||
fp.X += 0.1f * ps->range(-10, 10);
|
||||
fp.Z += 0.1f * ps->range(-10, 10);
|
||||
v3s16 cp(fp.X, fp.Y, fp.Z);
|
||||
|
||||
s16 d0 = -rs / 2;
|
||||
s16 d1 = d0 + rs;
|
||||
if (randomize_xz) {
|
||||
d0 += ps->range(-1, 1);
|
||||
d1 += ps->range(-1, 1);
|
||||
}
|
||||
|
||||
for (s16 z0 = d0; z0 <= d1; z0++) {
|
||||
s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
|
||||
for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
|
||||
if (tunnel_above_ground)
|
||||
continue;
|
||||
|
||||
s16 maxabsxz = MYMAX(abs(x0), abs(z0));
|
||||
s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
|
||||
for (s16 y0 = -si2; y0 <= si2; y0++) {
|
||||
if (large_cave_is_flat) {
|
||||
// Make large caves not so tall
|
||||
if (rs > 7 && abs(y0) >= rs / 3)
|
||||
continue;
|
||||
}
|
||||
|
||||
v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
|
||||
p += of;
|
||||
|
||||
if (!vm->m_area.contains(p))
|
||||
continue;
|
||||
|
||||
u32 i = vm->m_area.index(p);
|
||||
content_t c = vm->m_data[i].getContent();
|
||||
if (!ndef->get(c).is_ground_content)
|
||||
continue;
|
||||
|
||||
if (large_cave) {
|
||||
int full_ymin = node_min.Y - MAP_BLOCKSIZE;
|
||||
int full_ymax = node_max.Y + MAP_BLOCKSIZE;
|
||||
|
||||
if (full_ymin < water_level && full_ymax > water_level) {
|
||||
vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
|
||||
} else if (full_ymax < water_level) {
|
||||
vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode;
|
||||
} else {
|
||||
vm->m_data[i] = airnode;
|
||||
}
|
||||
} else {
|
||||
if (c == CONTENT_IGNORE || c == CONTENT_AIR)
|
||||
continue;
|
||||
|
||||
vm->m_data[i] = airnode;
|
||||
vm->m_flags[i] |= VMANIP_FLAG_CAVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline s16 CavesV6::getSurfaceFromHeightmap(v3s16 p)
|
||||
{
|
||||
if (heightmap != NULL &&
|
||||
p.Z >= node_min.Z && p.Z <= node_max.Z &&
|
||||
p.X >= node_min.X && p.X <= node_max.X) {
|
||||
u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
|
||||
return heightmap[index];
|
||||
}
|
||||
|
||||
return water_level;
|
||||
|
||||
}
|
242
src/mapgen/cavegen.h
Normal file
242
src/mapgen/cavegen.h
Normal file
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1
|
||||
|
||||
class GenerateNotifier;
|
||||
|
||||
/*
|
||||
CavesNoiseIntersection is a cave digging algorithm that carves smooth,
|
||||
web-like, continuous tunnels at points where the density of the intersection
|
||||
between two separate 3d noises is above a certain value. This value,
|
||||
cave_width, can be modified to set the effective width of these tunnels.
|
||||
|
||||
This algorithm is relatively heavyweight, taking ~80ms to generate an
|
||||
80x80x80 chunk of map on a modern processor. Use sparingly!
|
||||
|
||||
TODO(hmmmm): Remove dependency on biomes
|
||||
TODO(hmmmm): Find alternative to overgeneration as solution for sunlight issue
|
||||
*/
|
||||
class CavesNoiseIntersection
|
||||
{
|
||||
public:
|
||||
CavesNoiseIntersection(INodeDefManager *nodedef, BiomeManager *biomemgr,
|
||||
v3s16 chunksize, NoiseParams *np_cave1, NoiseParams *np_cave2,
|
||||
s32 seed, float cave_width);
|
||||
~CavesNoiseIntersection();
|
||||
|
||||
void generateCaves(MMVManip *vm, v3s16 nmin, v3s16 nmax, u8 *biomemap);
|
||||
|
||||
private:
|
||||
INodeDefManager *m_ndef;
|
||||
BiomeManager *m_bmgr;
|
||||
|
||||
// configurable parameters
|
||||
v3s16 m_csize;
|
||||
float m_cave_width;
|
||||
|
||||
// intermediate state variables
|
||||
u16 m_ystride;
|
||||
u16 m_zstride_1d;
|
||||
|
||||
Noise *noise_cave1;
|
||||
Noise *noise_cave2;
|
||||
};
|
||||
|
||||
/*
|
||||
CavernsNoise is a cave digging algorithm
|
||||
*/
|
||||
class CavernsNoise
|
||||
{
|
||||
public:
|
||||
CavernsNoise(INodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern,
|
||||
s32 seed, float cavern_limit, float cavern_taper,
|
||||
float cavern_threshold);
|
||||
~CavernsNoise();
|
||||
|
||||
bool generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax);
|
||||
|
||||
private:
|
||||
INodeDefManager *m_ndef;
|
||||
|
||||
// configurable parameters
|
||||
v3s16 m_csize;
|
||||
float m_cavern_limit;
|
||||
float m_cavern_taper;
|
||||
float m_cavern_threshold;
|
||||
|
||||
// intermediate state variables
|
||||
u16 m_ystride;
|
||||
u16 m_zstride_1d;
|
||||
|
||||
Noise *noise_cavern;
|
||||
|
||||
content_t c_water_source;
|
||||
content_t c_lava_source;
|
||||
};
|
||||
|
||||
/*
|
||||
CavesRandomWalk is an implementation of a cave-digging algorithm that
|
||||
operates on the principle of a "random walk" to approximate the stochiastic
|
||||
activity of cavern development.
|
||||
|
||||
In summary, this algorithm works by carving a randomly sized tunnel in a
|
||||
random direction a random amount of times, randomly varying in width.
|
||||
All randomness here is uniformly distributed; alternative distributions have
|
||||
not yet been implemented.
|
||||
|
||||
This algorithm is very fast, executing in less than 1ms on average for an
|
||||
80x80x80 chunk of map on a modern processor.
|
||||
*/
|
||||
class CavesRandomWalk
|
||||
{
|
||||
public:
|
||||
MMVManip *vm;
|
||||
INodeDefManager *ndef;
|
||||
GenerateNotifier *gennotify;
|
||||
s16 *heightmap;
|
||||
|
||||
// configurable parameters
|
||||
s32 seed;
|
||||
int water_level;
|
||||
int lava_depth;
|
||||
NoiseParams *np_caveliquids;
|
||||
|
||||
// intermediate state variables
|
||||
u16 ystride;
|
||||
|
||||
s16 min_tunnel_diameter;
|
||||
s16 max_tunnel_diameter;
|
||||
u16 tunnel_routepoints;
|
||||
int part_max_length_rs;
|
||||
|
||||
bool large_cave;
|
||||
bool large_cave_is_flat;
|
||||
bool flooded;
|
||||
|
||||
s16 max_stone_y;
|
||||
v3s16 node_min;
|
||||
v3s16 node_max;
|
||||
|
||||
v3f orp; // starting point, relative to caved space
|
||||
v3s16 of; // absolute coordinates of caved space
|
||||
v3s16 ar; // allowed route area
|
||||
s16 rs; // tunnel radius size
|
||||
v3f main_direction;
|
||||
|
||||
s16 route_y_min;
|
||||
s16 route_y_max;
|
||||
|
||||
PseudoRandom *ps;
|
||||
|
||||
content_t c_water_source;
|
||||
content_t c_lava_source;
|
||||
|
||||
// ndef is a mandatory parameter.
|
||||
// If gennotify is NULL, generation events are not logged.
|
||||
CavesRandomWalk(INodeDefManager *ndef, GenerateNotifier *gennotify = NULL,
|
||||
s32 seed = 0, int water_level = 1,
|
||||
content_t water_source = CONTENT_IGNORE,
|
||||
content_t lava_source = CONTENT_IGNORE, int lava_depth = -256);
|
||||
|
||||
// vm and ps are mandatory parameters.
|
||||
// If heightmap is NULL, the surface level at all points is assumed to
|
||||
// be water_level.
|
||||
void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps,
|
||||
bool is_large_cave, int max_stone_height, s16 *heightmap);
|
||||
|
||||
private:
|
||||
void makeTunnel(bool dirswitch);
|
||||
void carveRoute(v3f vec, float f, bool randomize_xz);
|
||||
|
||||
inline bool isPosAboveSurface(v3s16 p);
|
||||
};
|
||||
|
||||
/*
|
||||
CavesV6 is the original version of caves used with Mapgen V6.
|
||||
|
||||
Though it uses the same fundamental algorithm as CavesRandomWalk, it is made
|
||||
separate to preserve the exact sequence of PseudoRandom calls - any change
|
||||
to this ordering results in the output being radically different.
|
||||
Because caves in Mapgen V6 are responsible for a large portion of the basic
|
||||
terrain shape, modifying this will break our contract of reverse
|
||||
compatibility for a 'stable' mapgen such as V6.
|
||||
|
||||
tl;dr,
|
||||
*** DO NOT TOUCH THIS CLASS UNLESS YOU KNOW WHAT YOU ARE DOING ***
|
||||
*/
|
||||
class CavesV6
|
||||
{
|
||||
public:
|
||||
MMVManip *vm;
|
||||
INodeDefManager *ndef;
|
||||
GenerateNotifier *gennotify;
|
||||
PseudoRandom *ps;
|
||||
PseudoRandom *ps2;
|
||||
|
||||
// configurable parameters
|
||||
s16 *heightmap;
|
||||
content_t c_water_source;
|
||||
content_t c_lava_source;
|
||||
int water_level;
|
||||
|
||||
// intermediate state variables
|
||||
u16 ystride;
|
||||
|
||||
s16 min_tunnel_diameter;
|
||||
s16 max_tunnel_diameter;
|
||||
u16 tunnel_routepoints;
|
||||
int part_max_length_rs;
|
||||
|
||||
bool large_cave;
|
||||
bool large_cave_is_flat;
|
||||
|
||||
v3s16 node_min;
|
||||
v3s16 node_max;
|
||||
|
||||
v3f orp; // starting point, relative to caved space
|
||||
v3s16 of; // absolute coordinates of caved space
|
||||
v3s16 ar; // allowed route area
|
||||
s16 rs; // tunnel radius size
|
||||
v3f main_direction;
|
||||
|
||||
s16 route_y_min;
|
||||
s16 route_y_max;
|
||||
|
||||
// ndef is a mandatory parameter.
|
||||
// If gennotify is NULL, generation events are not logged.
|
||||
CavesV6(INodeDefManager *ndef, GenerateNotifier *gennotify = NULL,
|
||||
int water_level = 1, content_t water_source = CONTENT_IGNORE,
|
||||
content_t lava_source = CONTENT_IGNORE);
|
||||
|
||||
// vm, ps, and ps2 are mandatory parameters.
|
||||
// If heightmap is NULL, the surface level at all points is assumed to
|
||||
// be water_level.
|
||||
void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps,
|
||||
PseudoRandom *ps2, bool is_large_cave, int max_stone_height,
|
||||
s16 *heightmap = NULL);
|
||||
|
||||
private:
|
||||
void makeTunnel(bool dirswitch);
|
||||
void carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground);
|
||||
|
||||
inline s16 getSurfaceFromHeightmap(v3s16 p);
|
||||
};
|
677
src/mapgen/dungeongen.cpp
Normal file
677
src/mapgen/dungeongen.cpp
Normal file
|
@ -0,0 +1,677 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
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 "dungeongen.h"
|
||||
#include "mapgen.h"
|
||||
#include "voxel.h"
|
||||
#include "noise.h"
|
||||
#include "mapblock.h"
|
||||
#include "mapnode.h"
|
||||
#include "map.h"
|
||||
#include "nodedef.h"
|
||||
#include "settings.h"
|
||||
|
||||
//#define DGEN_USE_TORCHES
|
||||
|
||||
NoiseParams nparams_dungeon_density(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
|
||||
NoiseParams nparams_dungeon_alt_wall(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
DungeonGen::DungeonGen(INodeDefManager *ndef,
|
||||
GenerateNotifier *gennotify, DungeonParams *dparams)
|
||||
{
|
||||
assert(ndef);
|
||||
|
||||
this->ndef = ndef;
|
||||
this->gennotify = gennotify;
|
||||
|
||||
#ifdef DGEN_USE_TORCHES
|
||||
c_torch = ndef->getId("default:torch");
|
||||
#endif
|
||||
|
||||
if (dparams) {
|
||||
memcpy(&dp, dparams, sizeof(dp));
|
||||
} else {
|
||||
// Default dungeon parameters
|
||||
dp.seed = 0;
|
||||
|
||||
dp.c_water = ndef->getId("mapgen_water_source");
|
||||
dp.c_river_water = ndef->getId("mapgen_river_water_source");
|
||||
dp.c_wall = ndef->getId("mapgen_cobble");
|
||||
dp.c_alt_wall = ndef->getId("mapgen_mossycobble");
|
||||
dp.c_stair = ndef->getId("mapgen_stair_cobble");
|
||||
|
||||
if (dp.c_river_water == CONTENT_IGNORE)
|
||||
dp.c_river_water = ndef->getId("mapgen_water_source");
|
||||
|
||||
dp.diagonal_dirs = false;
|
||||
dp.only_in_ground = true;
|
||||
dp.holesize = v3s16(1, 2, 1);
|
||||
dp.corridor_len_min = 1;
|
||||
dp.corridor_len_max = 13;
|
||||
dp.room_size_min = v3s16(4, 4, 4);
|
||||
dp.room_size_max = v3s16(8, 6, 8);
|
||||
dp.room_size_large_min = v3s16(8, 8, 8);
|
||||
dp.room_size_large_max = v3s16(16, 16, 16);
|
||||
dp.rooms_min = 2;
|
||||
dp.rooms_max = 16;
|
||||
dp.y_min = -MAX_MAP_GENERATION_LIMIT;
|
||||
dp.y_max = MAX_MAP_GENERATION_LIMIT;
|
||||
dp.notifytype = GENNOTIFY_DUNGEON;
|
||||
|
||||
dp.np_density = nparams_dungeon_density;
|
||||
dp.np_alt_wall = nparams_dungeon_alt_wall;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DungeonGen::generate(MMVManip *vm, u32 bseed, v3s16 nmin, v3s16 nmax)
|
||||
{
|
||||
assert(vm);
|
||||
|
||||
//TimeTaker t("gen dungeons");
|
||||
if (nmin.Y < dp.y_min || nmax.Y > dp.y_max)
|
||||
return;
|
||||
|
||||
float nval_density = NoisePerlin3D(&dp.np_density, nmin.X, nmin.Y, nmin.Z, dp.seed);
|
||||
if (nval_density < 1.0f)
|
||||
return;
|
||||
|
||||
static const bool preserve_ignore = !g_settings->getBool("projecting_dungeons");
|
||||
|
||||
this->vm = vm;
|
||||
this->blockseed = bseed;
|
||||
random.seed(bseed + 2);
|
||||
|
||||
// Dungeon generator doesn't modify places which have this set
|
||||
vm->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE);
|
||||
|
||||
if (dp.only_in_ground) {
|
||||
// Set all air and water to be untouchable to make dungeons open to
|
||||
// caves and open air. Optionally set ignore to be untouchable to
|
||||
// prevent protruding dungeons.
|
||||
for (s16 z = nmin.Z; z <= nmax.Z; z++) {
|
||||
for (s16 y = nmin.Y; y <= nmax.Y; y++) {
|
||||
u32 i = vm->m_area.index(nmin.X, y, z);
|
||||
for (s16 x = nmin.X; x <= nmax.X; x++) {
|
||||
content_t c = vm->m_data[i].getContent();
|
||||
if (c == CONTENT_AIR || c == dp.c_water ||
|
||||
(preserve_ignore && c == CONTENT_IGNORE) ||
|
||||
c == dp.c_river_water)
|
||||
vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add them
|
||||
for (u32 i = 0; i < floor(nval_density); i++)
|
||||
makeDungeon(v3s16(1, 1, 1) * MAP_BLOCKSIZE);
|
||||
|
||||
// Optionally convert some structure to alternative structure
|
||||
if (dp.c_alt_wall == CONTENT_IGNORE)
|
||||
return;
|
||||
|
||||
for (s16 z = nmin.Z; z <= nmax.Z; z++)
|
||||
for (s16 y = nmin.Y; y <= nmax.Y; y++) {
|
||||
u32 i = vm->m_area.index(nmin.X, y, z);
|
||||
for (s16 x = nmin.X; x <= nmax.X; x++) {
|
||||
if (vm->m_data[i].getContent() == dp.c_wall) {
|
||||
if (NoisePerlin3D(&dp.np_alt_wall, x, y, z, blockseed) > 0.0f)
|
||||
vm->m_data[i].setContent(dp.c_alt_wall);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("== gen dungeons: %dms\n", t.stop());
|
||||
}
|
||||
|
||||
|
||||
void DungeonGen::makeDungeon(v3s16 start_padding)
|
||||
{
|
||||
const v3s16 &areasize = vm->m_area.getExtent();
|
||||
v3s16 roomsize;
|
||||
v3s16 roomplace;
|
||||
|
||||
/*
|
||||
Find place for first room.
|
||||
There is a 1 in 4 chance of the first room being 'large',
|
||||
all other rooms are not 'large'.
|
||||
*/
|
||||
bool fits = false;
|
||||
for (u32 i = 0; i < 100 && !fits; i++) {
|
||||
bool is_large_room = ((random.next() & 3) == 1);
|
||||
if (is_large_room) {
|
||||
roomsize.Z = random.range(
|
||||
dp.room_size_large_min.Z, dp.room_size_large_max.Z);
|
||||
roomsize.Y = random.range(
|
||||
dp.room_size_large_min.Y, dp.room_size_large_max.Y);
|
||||
roomsize.X = random.range(
|
||||
dp.room_size_large_min.X, dp.room_size_large_max.X);
|
||||
} else {
|
||||
roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z);
|
||||
roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y);
|
||||
roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X);
|
||||
}
|
||||
|
||||
// start_padding is used to disallow starting the generation of
|
||||
// a dungeon in a neighboring generation chunk
|
||||
roomplace = vm->m_area.MinEdge + start_padding;
|
||||
roomplace.Z += random.range(0, areasize.Z - roomsize.Z - start_padding.Z);
|
||||
roomplace.Y += random.range(0, areasize.Y - roomsize.Y - start_padding.Y);
|
||||
roomplace.X += random.range(0, areasize.X - roomsize.X - start_padding.X);
|
||||
|
||||
/*
|
||||
Check that we're not putting the room to an unknown place,
|
||||
otherwise it might end up floating in the air
|
||||
*/
|
||||
fits = true;
|
||||
for (s16 z = 0; z < roomsize.Z; z++)
|
||||
for (s16 y = 0; y < roomsize.Y; y++)
|
||||
for (s16 x = 0; x < roomsize.X; x++) {
|
||||
v3s16 p = roomplace + v3s16(x, y, z);
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) ||
|
||||
vm->m_data[vi].getContent() == CONTENT_IGNORE) {
|
||||
fits = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// No place found
|
||||
if (!fits)
|
||||
return;
|
||||
|
||||
/*
|
||||
Stores the center position of the last room made, so that
|
||||
a new corridor can be started from the last room instead of
|
||||
the new room, if chosen so.
|
||||
*/
|
||||
v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
|
||||
|
||||
u32 room_count = random.range(dp.rooms_min, dp.rooms_max);
|
||||
for (u32 i = 0; i < room_count; i++) {
|
||||
// Make a room to the determined place
|
||||
makeRoom(roomsize, roomplace);
|
||||
|
||||
v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
|
||||
if (gennotify)
|
||||
gennotify->addEvent(dp.notifytype, room_center);
|
||||
|
||||
#ifdef DGEN_USE_TORCHES
|
||||
// Place torch at room center (for testing)
|
||||
vm->m_data[vm->m_area.index(room_center)] = MapNode(c_torch);
|
||||
#endif
|
||||
|
||||
// Quit if last room
|
||||
if (i == room_count - 1)
|
||||
break;
|
||||
|
||||
// Determine walker start position
|
||||
|
||||
bool start_in_last_room = (random.range(0, 2) != 0);
|
||||
|
||||
v3s16 walker_start_place;
|
||||
|
||||
if (start_in_last_room) {
|
||||
walker_start_place = last_room_center;
|
||||
} else {
|
||||
walker_start_place = room_center;
|
||||
// Store center of current room as the last one
|
||||
last_room_center = room_center;
|
||||
}
|
||||
|
||||
// Create walker and find a place for a door
|
||||
v3s16 doorplace;
|
||||
v3s16 doordir;
|
||||
|
||||
m_pos = walker_start_place;
|
||||
if (!findPlaceForDoor(doorplace, doordir))
|
||||
return;
|
||||
|
||||
if (random.range(0, 1) == 0)
|
||||
// Make the door
|
||||
makeDoor(doorplace, doordir);
|
||||
else
|
||||
// Don't actually make a door
|
||||
doorplace -= doordir;
|
||||
|
||||
// Make a random corridor starting from the door
|
||||
v3s16 corridor_end;
|
||||
v3s16 corridor_end_dir;
|
||||
makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir);
|
||||
|
||||
// Find a place for a random sized room
|
||||
roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z);
|
||||
roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y);
|
||||
roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X);
|
||||
|
||||
m_pos = corridor_end;
|
||||
m_dir = corridor_end_dir;
|
||||
if (!findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace))
|
||||
return;
|
||||
|
||||
if (random.range(0, 1) == 0)
|
||||
// Make the door
|
||||
makeDoor(doorplace, doordir);
|
||||
else
|
||||
// Don't actually make a door
|
||||
roomplace -= doordir;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
|
||||
{
|
||||
MapNode n_wall(dp.c_wall);
|
||||
MapNode n_air(CONTENT_AIR);
|
||||
|
||||
// Make +-X walls
|
||||
for (s16 z = 0; z < roomsize.Z; z++)
|
||||
for (s16 y = 0; y < roomsize.Y; y++) {
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(0, y, z);
|
||||
if (!vm->m_area.contains(p))
|
||||
continue;
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vm->m_data[vi] = n_wall;
|
||||
}
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z);
|
||||
if (!vm->m_area.contains(p))
|
||||
continue;
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vm->m_data[vi] = n_wall;
|
||||
}
|
||||
}
|
||||
|
||||
// Make +-Z walls
|
||||
for (s16 x = 0; x < roomsize.X; x++)
|
||||
for (s16 y = 0; y < roomsize.Y; y++) {
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(x, y, 0);
|
||||
if (!vm->m_area.contains(p))
|
||||
continue;
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vm->m_data[vi] = n_wall;
|
||||
}
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1);
|
||||
if (!vm->m_area.contains(p))
|
||||
continue;
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vm->m_data[vi] = n_wall;
|
||||
}
|
||||
}
|
||||
|
||||
// Make +-Y walls (floor and ceiling)
|
||||
for (s16 z = 0; z < roomsize.Z; z++)
|
||||
for (s16 x = 0; x < roomsize.X; x++) {
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(x, 0, z);
|
||||
if (!vm->m_area.contains(p))
|
||||
continue;
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vm->m_data[vi] = n_wall;
|
||||
}
|
||||
{
|
||||
v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z);
|
||||
if (!vm->m_area.contains(p))
|
||||
continue;
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
|
||||
continue;
|
||||
vm->m_data[vi] = n_wall;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill with air
|
||||
for (s16 z = 1; z < roomsize.Z - 1; z++)
|
||||
for (s16 y = 1; y < roomsize.Y - 1; y++)
|
||||
for (s16 x = 1; x < roomsize.X - 1; x++) {
|
||||
v3s16 p = roomplace + v3s16(x, y, z);
|
||||
if (!vm->m_area.contains(p))
|
||||
continue;
|
||||
u32 vi = vm->m_area.index(p);
|
||||
vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
|
||||
vm->m_data[vi] = n_air;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DungeonGen::makeFill(v3s16 place, v3s16 size,
|
||||
u8 avoid_flags, MapNode n, u8 or_flags)
|
||||
{
|
||||
for (s16 z = 0; z < size.Z; z++)
|
||||
for (s16 y = 0; y < size.Y; y++)
|
||||
for (s16 x = 0; x < size.X; x++) {
|
||||
v3s16 p = place + v3s16(x, y, z);
|
||||
if (!vm->m_area.contains(p))
|
||||
continue;
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (vm->m_flags[vi] & avoid_flags)
|
||||
continue;
|
||||
vm->m_flags[vi] |= or_flags;
|
||||
vm->m_data[vi] = n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DungeonGen::makeHole(v3s16 place)
|
||||
{
|
||||
makeFill(place, dp.holesize, 0, MapNode(CONTENT_AIR),
|
||||
VMANIP_FLAG_DUNGEON_INSIDE);
|
||||
}
|
||||
|
||||
|
||||
void DungeonGen::makeDoor(v3s16 doorplace, v3s16 doordir)
|
||||
{
|
||||
makeHole(doorplace);
|
||||
|
||||
#ifdef DGEN_USE_TORCHES
|
||||
// Place torch (for testing)
|
||||
vm->m_data[vm->m_area.index(doorplace)] = MapNode(c_torch);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
|
||||
v3s16 &result_place, v3s16 &result_dir)
|
||||
{
|
||||
makeHole(doorplace);
|
||||
v3s16 p0 = doorplace;
|
||||
v3s16 dir = doordir;
|
||||
u32 length = random.range(dp.corridor_len_min, dp.corridor_len_max);
|
||||
u32 partlength = random.range(dp.corridor_len_min, dp.corridor_len_max);
|
||||
u32 partcount = 0;
|
||||
s16 make_stairs = 0;
|
||||
|
||||
if (random.next() % 2 == 0 && partlength >= 3)
|
||||
make_stairs = random.next() % 2 ? 1 : -1;
|
||||
|
||||
for (u32 i = 0; i < length; i++) {
|
||||
v3s16 p = p0 + dir;
|
||||
if (partcount != 0)
|
||||
p.Y += make_stairs;
|
||||
|
||||
// Check segment of minimum size corridor is in voxelmanip
|
||||
if (vm->m_area.contains(p) && vm->m_area.contains(p + v3s16(0, 1, 0))) {
|
||||
if (make_stairs) {
|
||||
makeFill(p + v3s16(-1, -1, -1),
|
||||
dp.holesize + v3s16(2, 3, 2),
|
||||
VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
|
||||
MapNode(dp.c_wall),
|
||||
0);
|
||||
makeHole(p);
|
||||
makeHole(p - dir);
|
||||
|
||||
// TODO: fix stairs code so it works 100%
|
||||
// (quite difficult)
|
||||
|
||||
// exclude stairs from the bottom step
|
||||
// exclude stairs from diagonal steps
|
||||
if (((dir.X ^ dir.Z) & 1) &&
|
||||
(((make_stairs == 1) && i != 0) ||
|
||||
((make_stairs == -1) && i != length - 1))) {
|
||||
// rotate face 180 deg if
|
||||
// making stairs backwards
|
||||
int facedir = dir_to_facedir(dir * make_stairs);
|
||||
v3s16 ps = p;
|
||||
u16 stair_width = (dir.Z != 0) ? dp.holesize.X : dp.holesize.Z;
|
||||
// Stair width direction vector
|
||||
v3s16 swv = (dir.Z != 0) ? v3s16(1, 0, 0) : v3s16(0, 0, 1);
|
||||
|
||||
for (u16 st = 0; st < stair_width; st++) {
|
||||
u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z);
|
||||
if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) &&
|
||||
vm->m_data[vi].getContent() == dp.c_wall)
|
||||
vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
|
||||
|
||||
vi = vm->m_area.index(ps.X, ps.Y, ps.Z);
|
||||
if (vm->m_area.contains(ps) &&
|
||||
vm->m_data[vi].getContent() == dp.c_wall)
|
||||
vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
|
||||
|
||||
ps += swv;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
makeFill(p + v3s16(-1, -1, -1),
|
||||
dp.holesize + v3s16(2, 2, 2),
|
||||
VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
|
||||
MapNode(dp.c_wall),
|
||||
0);
|
||||
makeHole(p);
|
||||
}
|
||||
|
||||
p0 = p;
|
||||
} else {
|
||||
// Can't go here, turn away
|
||||
dir = turn_xz(dir, random.range(0, 1));
|
||||
make_stairs = -make_stairs;
|
||||
partcount = 0;
|
||||
partlength = random.range(1, length);
|
||||
continue;
|
||||
}
|
||||
|
||||
partcount++;
|
||||
if (partcount >= partlength) {
|
||||
partcount = 0;
|
||||
|
||||
dir = random_turn(random, dir);
|
||||
|
||||
partlength = random.range(1, length);
|
||||
|
||||
make_stairs = 0;
|
||||
if (random.next() % 2 == 0 && partlength >= 3)
|
||||
make_stairs = random.next() % 2 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
result_place = p0;
|
||||
result_dir = dir;
|
||||
}
|
||||
|
||||
|
||||
bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
|
||||
{
|
||||
for (u32 i = 0; i < 100; i++) {
|
||||
v3s16 p = m_pos + m_dir;
|
||||
v3s16 p1 = p + v3s16(0, 1, 0);
|
||||
if (!vm->m_area.contains(p) || !vm->m_area.contains(p1) || i % 4 == 0) {
|
||||
randomizeDir();
|
||||
continue;
|
||||
}
|
||||
if (vm->getNodeNoExNoEmerge(p).getContent() == dp.c_wall &&
|
||||
vm->getNodeNoExNoEmerge(p1).getContent() == dp.c_wall) {
|
||||
// Found wall, this is a good place!
|
||||
result_place = p;
|
||||
result_dir = m_dir;
|
||||
// Randomize next direction
|
||||
randomizeDir();
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
Determine where to move next
|
||||
*/
|
||||
// Jump one up if the actual space is there
|
||||
if (vm->getNodeNoExNoEmerge(p +
|
||||
v3s16(0, 0, 0)).getContent() == dp.c_wall &&
|
||||
vm->getNodeNoExNoEmerge(p +
|
||||
v3s16(0, 1, 0)).getContent() == CONTENT_AIR &&
|
||||
vm->getNodeNoExNoEmerge(p +
|
||||
v3s16(0, 2, 0)).getContent() == CONTENT_AIR)
|
||||
p += v3s16(0,1,0);
|
||||
// Jump one down if the actual space is there
|
||||
if (vm->getNodeNoExNoEmerge(p +
|
||||
v3s16(0, 1, 0)).getContent() == dp.c_wall &&
|
||||
vm->getNodeNoExNoEmerge(p +
|
||||
v3s16(0, 0, 0)).getContent() == CONTENT_AIR &&
|
||||
vm->getNodeNoExNoEmerge(p +
|
||||
v3s16(0, -1, 0)).getContent() == CONTENT_AIR)
|
||||
p += v3s16(0, -1, 0);
|
||||
// Check if walking is now possible
|
||||
if (vm->getNodeNoExNoEmerge(p).getContent() != CONTENT_AIR ||
|
||||
vm->getNodeNoExNoEmerge(p +
|
||||
v3s16(0, 1, 0)).getContent() != CONTENT_AIR) {
|
||||
// Cannot continue walking here
|
||||
randomizeDir();
|
||||
continue;
|
||||
}
|
||||
// Move there
|
||||
m_pos = p;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
|
||||
v3s16 &result_doordir, v3s16 &result_roomplace)
|
||||
{
|
||||
for (s16 trycount = 0; trycount < 30; trycount++) {
|
||||
v3s16 doorplace;
|
||||
v3s16 doordir;
|
||||
bool r = findPlaceForDoor(doorplace, doordir);
|
||||
if (!r)
|
||||
continue;
|
||||
v3s16 roomplace;
|
||||
// X east, Z north, Y up
|
||||
if (doordir == v3s16(1, 0, 0)) // X+
|
||||
roomplace = doorplace +
|
||||
v3s16(0, -1, random.range(-roomsize.Z + 2, -2));
|
||||
if (doordir == v3s16(-1, 0, 0)) // X-
|
||||
roomplace = doorplace +
|
||||
v3s16(-roomsize.X + 1, -1, random.range(-roomsize.Z + 2, -2));
|
||||
if (doordir == v3s16(0, 0, 1)) // Z+
|
||||
roomplace = doorplace +
|
||||
v3s16(random.range(-roomsize.X + 2, -2), -1, 0);
|
||||
if (doordir == v3s16(0, 0, -1)) // Z-
|
||||
roomplace = doorplace +
|
||||
v3s16(random.range(-roomsize.X + 2, -2), -1, -roomsize.Z + 1);
|
||||
|
||||
// Check fit
|
||||
bool fits = true;
|
||||
for (s16 z = 1; z < roomsize.Z - 1; z++)
|
||||
for (s16 y = 1; y < roomsize.Y - 1; y++)
|
||||
for (s16 x = 1; x < roomsize.X - 1; x++) {
|
||||
v3s16 p = roomplace + v3s16(x, y, z);
|
||||
if (!vm->m_area.contains(p)) {
|
||||
fits = false;
|
||||
break;
|
||||
}
|
||||
if (vm->m_flags[vm->m_area.index(p)] & VMANIP_FLAG_DUNGEON_INSIDE) {
|
||||
fits = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!fits) {
|
||||
// Find new place
|
||||
continue;
|
||||
}
|
||||
result_doorplace = doorplace;
|
||||
result_doordir = doordir;
|
||||
result_roomplace = roomplace;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs)
|
||||
{
|
||||
// Make diagonal directions somewhat rare
|
||||
if (diagonal_dirs && (random.next() % 4 == 0)) {
|
||||
v3s16 dir;
|
||||
int trycount = 0;
|
||||
|
||||
do {
|
||||
trycount++;
|
||||
|
||||
dir.Z = random.next() % 3 - 1;
|
||||
dir.Y = 0;
|
||||
dir.X = random.next() % 3 - 1;
|
||||
} while ((dir.X == 0 || dir.Z == 0) && trycount < 10);
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
if (random.next() % 2 == 0)
|
||||
return random.next() % 2 ? v3s16(-1, 0, 0) : v3s16(1, 0, 0);
|
||||
|
||||
return random.next() % 2 ? v3s16(0, 0, -1) : v3s16(0, 0, 1);
|
||||
}
|
||||
|
||||
|
||||
v3s16 turn_xz(v3s16 olddir, int t)
|
||||
{
|
||||
v3s16 dir;
|
||||
if (t == 0) {
|
||||
// Turn right
|
||||
dir.X = olddir.Z;
|
||||
dir.Z = -olddir.X;
|
||||
dir.Y = olddir.Y;
|
||||
} else {
|
||||
// Turn left
|
||||
dir.X = -olddir.Z;
|
||||
dir.Z = olddir.X;
|
||||
dir.Y = olddir.Y;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
|
||||
v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
|
||||
{
|
||||
int turn = random.range(0, 2);
|
||||
v3s16 dir;
|
||||
if (turn == 0)
|
||||
// Go straight
|
||||
dir = olddir;
|
||||
else if (turn == 1)
|
||||
// Turn right
|
||||
dir = turn_xz(olddir, 0);
|
||||
else
|
||||
// Turn left
|
||||
dir = turn_xz(olddir, 1);
|
||||
return dir;
|
||||
}
|
||||
|
||||
|
||||
int dir_to_facedir(v3s16 d)
|
||||
{
|
||||
if (abs(d.X) > abs(d.Z))
|
||||
return d.X < 0 ? 3 : 1;
|
||||
|
||||
return d.Z < 0 ? 2 : 0;
|
||||
}
|
110
src/mapgen/dungeongen.h
Normal file
110
src/mapgen/dungeongen.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "voxel.h"
|
||||
#include "noise.h"
|
||||
#include "mapgen.h"
|
||||
|
||||
#define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
|
||||
#define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
|
||||
#define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
|
||||
VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)
|
||||
|
||||
class MMVManip;
|
||||
class INodeDefManager;
|
||||
|
||||
v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs);
|
||||
v3s16 turn_xz(v3s16 olddir, int t);
|
||||
v3s16 random_turn(PseudoRandom &random, v3s16 olddir);
|
||||
int dir_to_facedir(v3s16 d);
|
||||
|
||||
|
||||
struct DungeonParams {
|
||||
s32 seed;
|
||||
|
||||
content_t c_water;
|
||||
content_t c_river_water;
|
||||
content_t c_wall;
|
||||
content_t c_alt_wall;
|
||||
content_t c_stair;
|
||||
|
||||
bool diagonal_dirs;
|
||||
bool only_in_ground;
|
||||
v3s16 holesize;
|
||||
u16 corridor_len_min;
|
||||
u16 corridor_len_max;
|
||||
v3s16 room_size_min;
|
||||
v3s16 room_size_max;
|
||||
v3s16 room_size_large_min;
|
||||
v3s16 room_size_large_max;
|
||||
u16 rooms_min;
|
||||
u16 rooms_max;
|
||||
s16 y_min;
|
||||
s16 y_max;
|
||||
GenNotifyType notifytype;
|
||||
|
||||
NoiseParams np_density;
|
||||
NoiseParams np_alt_wall;
|
||||
};
|
||||
|
||||
class DungeonGen {
|
||||
public:
|
||||
MMVManip *vm;
|
||||
INodeDefManager *ndef;
|
||||
GenerateNotifier *gennotify;
|
||||
|
||||
u32 blockseed;
|
||||
PseudoRandom random;
|
||||
v3s16 csize;
|
||||
|
||||
content_t c_torch;
|
||||
DungeonParams dp;
|
||||
|
||||
// RoomWalker
|
||||
v3s16 m_pos;
|
||||
v3s16 m_dir;
|
||||
|
||||
DungeonGen(INodeDefManager *ndef,
|
||||
GenerateNotifier *gennotify, DungeonParams *dparams);
|
||||
|
||||
void generate(MMVManip *vm, u32 bseed,
|
||||
v3s16 full_node_min, v3s16 full_node_max);
|
||||
|
||||
void makeDungeon(v3s16 start_padding);
|
||||
void makeRoom(v3s16 roomsize, v3s16 roomplace);
|
||||
void makeCorridor(v3s16 doorplace, v3s16 doordir,
|
||||
v3s16 &result_place, v3s16 &result_dir);
|
||||
void makeDoor(v3s16 doorplace, v3s16 doordir);
|
||||
void makeFill(v3s16 place, v3s16 size, u8 avoid_flags, MapNode n, u8 or_flags);
|
||||
void makeHole(v3s16 place);
|
||||
|
||||
bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir);
|
||||
bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
|
||||
v3s16 &result_doordir, v3s16 &result_roomplace);
|
||||
|
||||
inline void randomizeDir()
|
||||
{
|
||||
m_dir = rand_ortho_dir(random, dp.diagonal_dirs);
|
||||
}
|
||||
};
|
||||
|
||||
extern NoiseParams nparams_dungeon_density;
|
||||
extern NoiseParams nparams_dungeon_alt_wall;
|
1140
src/mapgen/mapgen.cpp
Normal file
1140
src/mapgen/mapgen.cpp
Normal file
File diff suppressed because it is too large
Load diff
302
src/mapgen/mapgen.h
Normal file
302
src/mapgen/mapgen.h
Normal file
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2015-2017 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "noise.h"
|
||||
#include "nodedef.h"
|
||||
#include "util/string.h"
|
||||
#include "util/container.h"
|
||||
|
||||
#define MAPGEN_DEFAULT MAPGEN_V7
|
||||
#define MAPGEN_DEFAULT_NAME "v7"
|
||||
|
||||
/////////////////// Mapgen flags
|
||||
#define MG_TREES 0x01 // Deprecated. Moved into mgv6 flags
|
||||
#define MG_CAVES 0x02
|
||||
#define MG_DUNGEONS 0x04
|
||||
#define MG_FLAT 0x08 // Deprecated. Moved into mgv6 flags
|
||||
#define MG_LIGHT 0x10
|
||||
#define MG_DECORATIONS 0x20
|
||||
|
||||
typedef u8 biome_t; // copy from mg_biome.h to avoid an unnecessary include
|
||||
|
||||
class Settings;
|
||||
class MMVManip;
|
||||
class INodeDefManager;
|
||||
|
||||
extern FlagDesc flagdesc_mapgen[];
|
||||
extern FlagDesc flagdesc_gennotify[];
|
||||
|
||||
class Biome;
|
||||
class BiomeGen;
|
||||
struct BiomeParams;
|
||||
class BiomeManager;
|
||||
class EmergeManager;
|
||||
class MapBlock;
|
||||
class VoxelManipulator;
|
||||
struct BlockMakeData;
|
||||
class VoxelArea;
|
||||
class Map;
|
||||
|
||||
enum MapgenObject {
|
||||
MGOBJ_VMANIP,
|
||||
MGOBJ_HEIGHTMAP,
|
||||
MGOBJ_BIOMEMAP,
|
||||
MGOBJ_HEATMAP,
|
||||
MGOBJ_HUMIDMAP,
|
||||
MGOBJ_GENNOTIFY
|
||||
};
|
||||
|
||||
enum GenNotifyType {
|
||||
GENNOTIFY_DUNGEON,
|
||||
GENNOTIFY_TEMPLE,
|
||||
GENNOTIFY_CAVE_BEGIN,
|
||||
GENNOTIFY_CAVE_END,
|
||||
GENNOTIFY_LARGECAVE_BEGIN,
|
||||
GENNOTIFY_LARGECAVE_END,
|
||||
GENNOTIFY_DECORATION,
|
||||
NUM_GENNOTIFY_TYPES
|
||||
};
|
||||
|
||||
enum MgStoneType {
|
||||
MGSTONE_STONE,
|
||||
MGSTONE_DESERT_STONE,
|
||||
MGSTONE_SANDSTONE,
|
||||
MGSTONE_OTHER,
|
||||
};
|
||||
|
||||
struct GenNotifyEvent {
|
||||
GenNotifyType type;
|
||||
v3s16 pos;
|
||||
u32 id;
|
||||
};
|
||||
|
||||
class GenerateNotifier {
|
||||
public:
|
||||
GenerateNotifier() = default;
|
||||
GenerateNotifier(u32 notify_on, std::set<u32> *notify_on_deco_ids);
|
||||
|
||||
void setNotifyOn(u32 notify_on);
|
||||
void setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids);
|
||||
|
||||
bool addEvent(GenNotifyType type, v3s16 pos, u32 id=0);
|
||||
void getEvents(std::map<std::string, std::vector<v3s16> > &event_map,
|
||||
bool peek_events=false);
|
||||
|
||||
private:
|
||||
u32 m_notify_on = 0;
|
||||
std::set<u32> *m_notify_on_deco_ids;
|
||||
std::list<GenNotifyEvent> m_notify_events;
|
||||
};
|
||||
|
||||
enum MapgenType {
|
||||
MAPGEN_V5,
|
||||
MAPGEN_V6,
|
||||
MAPGEN_V7,
|
||||
MAPGEN_FLAT,
|
||||
MAPGEN_FRACTAL,
|
||||
MAPGEN_VALLEYS,
|
||||
MAPGEN_SINGLENODE,
|
||||
MAPGEN_CARPATHIAN,
|
||||
MAPGEN_INVALID,
|
||||
};
|
||||
|
||||
struct MapgenParams {
|
||||
MapgenParams() = default;
|
||||
virtual ~MapgenParams();
|
||||
|
||||
MapgenType mgtype = MAPGEN_DEFAULT;
|
||||
s16 chunksize = 5;
|
||||
u64 seed = 0;
|
||||
s16 water_level = 1;
|
||||
s16 mapgen_limit = MAX_MAP_GENERATION_LIMIT;
|
||||
u32 flags = MG_CAVES | MG_LIGHT | MG_DECORATIONS;
|
||||
|
||||
BiomeParams *bparams = nullptr;
|
||||
|
||||
s16 mapgen_edge_min = -MAX_MAP_GENERATION_LIMIT;
|
||||
s16 mapgen_edge_max = MAX_MAP_GENERATION_LIMIT;
|
||||
|
||||
virtual void readParams(const Settings *settings);
|
||||
virtual void writeParams(Settings *settings) const;
|
||||
|
||||
bool saoPosOverLimit(const v3f &p);
|
||||
s32 getSpawnRangeMax();
|
||||
|
||||
private:
|
||||
void calcMapgenEdges();
|
||||
|
||||
float m_sao_limit_min = -MAX_MAP_GENERATION_LIMIT * BS;
|
||||
float m_sao_limit_max = MAX_MAP_GENERATION_LIMIT * BS;
|
||||
bool m_mapgen_edges_calculated = false;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Generic interface for map generators. All mapgens must inherit this class.
|
||||
If a feature exposed by a public member pointer is not supported by a
|
||||
certain mapgen, it must be set to NULL.
|
||||
|
||||
Apart from makeChunk, getGroundLevelAtPoint, and getSpawnLevelAtPoint, all
|
||||
methods can be used by constructing a Mapgen base class and setting the
|
||||
appropriate public members (e.g. vm, ndef, and so on).
|
||||
*/
|
||||
class Mapgen {
|
||||
public:
|
||||
s32 seed = 0;
|
||||
int water_level = 0;
|
||||
int mapgen_limit = 0;
|
||||
u32 flags = 0;
|
||||
bool generating = false;
|
||||
int id = -1;
|
||||
|
||||
MMVManip *vm = nullptr;
|
||||
INodeDefManager *ndef = nullptr;
|
||||
|
||||
u32 blockseed;
|
||||
s16 *heightmap = nullptr;
|
||||
biome_t *biomemap = nullptr;
|
||||
v3s16 csize;
|
||||
|
||||
BiomeGen *biomegen = nullptr;
|
||||
GenerateNotifier gennotify;
|
||||
|
||||
Mapgen() = default;
|
||||
Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge);
|
||||
virtual ~Mapgen() = default;
|
||||
DISABLE_CLASS_COPY(Mapgen);
|
||||
|
||||
virtual MapgenType getType() const { return MAPGEN_INVALID; }
|
||||
|
||||
static u32 getBlockSeed(v3s16 p, s32 seed);
|
||||
static u32 getBlockSeed2(v3s16 p, s32 seed);
|
||||
s16 findGroundLevelFull(v2s16 p2d);
|
||||
s16 findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax);
|
||||
s16 findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax);
|
||||
void updateHeightmap(v3s16 nmin, v3s16 nmax);
|
||||
void getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
|
||||
s16 *floors, s16 *ceilings, u16 *num_floors, u16 *num_ceilings);
|
||||
|
||||
void updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax);
|
||||
|
||||
void setLighting(u8 light, v3s16 nmin, v3s16 nmax);
|
||||
void lightSpread(VoxelArea &a, v3s16 p, u8 light);
|
||||
void calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
|
||||
bool propagate_shadow = true);
|
||||
void propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow);
|
||||
void spreadLight(v3s16 nmin, v3s16 nmax);
|
||||
|
||||
virtual void makeChunk(BlockMakeData *data) {}
|
||||
virtual int getGroundLevelAtPoint(v2s16 p) { return 0; }
|
||||
|
||||
// getSpawnLevelAtPoint() is a function within each mapgen that returns a
|
||||
// suitable y co-ordinate for player spawn ('suitable' usually meaning
|
||||
// within 16 nodes of water_level). If a suitable spawn level cannot be
|
||||
// found at the specified (X, Z) 'MAX_MAP_GENERATION_LIMIT' is returned to
|
||||
// signify this and to cause Server::findSpawnPos() to try another (X, Z).
|
||||
virtual int getSpawnLevelAtPoint(v2s16 p) { return 0; }
|
||||
|
||||
// Mapgen management functions
|
||||
static MapgenType getMapgenType(const std::string &mgname);
|
||||
static const char *getMapgenName(MapgenType mgtype);
|
||||
static Mapgen *createMapgen(MapgenType mgtype, int mgid,
|
||||
MapgenParams *params, EmergeManager *emerge);
|
||||
static MapgenParams *createMapgenParams(MapgenType mgtype);
|
||||
static void getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden);
|
||||
|
||||
private:
|
||||
// isLiquidHorizontallyFlowable() is a helper function for updateLiquid()
|
||||
// that checks whether there are floodable nodes without liquid beneath
|
||||
// the node at index vi.
|
||||
inline bool isLiquidHorizontallyFlowable(u32 vi, v3s16 em);
|
||||
};
|
||||
|
||||
/*
|
||||
MapgenBasic is a Mapgen implementation that handles basic functionality
|
||||
the majority of conventional mapgens will probably want to use, but isn't
|
||||
generic enough to be included as part of the base Mapgen class (such as
|
||||
generating biome terrain over terrain node skeletons, generating caves,
|
||||
dungeons, etc.)
|
||||
|
||||
Inherit MapgenBasic instead of Mapgen to add this basic functionality to
|
||||
your mapgen without having to reimplement it. Feel free to override any of
|
||||
these methods if you desire different or more advanced behavior.
|
||||
|
||||
Note that you must still create your own generateTerrain implementation when
|
||||
inheriting MapgenBasic.
|
||||
*/
|
||||
class MapgenBasic : public Mapgen {
|
||||
public:
|
||||
MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge);
|
||||
virtual ~MapgenBasic();
|
||||
|
||||
virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth);
|
||||
virtual bool generateCaverns(s16 max_stone_y);
|
||||
virtual void generateDungeons(s16 max_stone_y,
|
||||
MgStoneType stone_type, content_t biome_stone);
|
||||
virtual void generateBiomes(MgStoneType *mgstone_type,
|
||||
content_t *biome_stone);
|
||||
virtual void dustTopNodes();
|
||||
|
||||
protected:
|
||||
EmergeManager *m_emerge;
|
||||
BiomeManager *m_bmgr;
|
||||
|
||||
Noise *noise_filler_depth;
|
||||
|
||||
v3s16 node_min;
|
||||
v3s16 node_max;
|
||||
v3s16 full_node_min;
|
||||
v3s16 full_node_max;
|
||||
|
||||
// Content required for generateBiomes
|
||||
content_t c_stone;
|
||||
content_t c_desert_stone;
|
||||
content_t c_sandstone;
|
||||
content_t c_water_source;
|
||||
content_t c_river_water_source;
|
||||
content_t c_lava_source;
|
||||
|
||||
// Content required for generateDungeons
|
||||
content_t c_cobble;
|
||||
content_t c_stair_cobble;
|
||||
content_t c_mossycobble;
|
||||
content_t c_stair_desert_stone;
|
||||
content_t c_sandstonebrick;
|
||||
content_t c_stair_sandstone_block;
|
||||
|
||||
int ystride;
|
||||
int zstride;
|
||||
int zstride_1d;
|
||||
int zstride_1u1d;
|
||||
|
||||
u32 spflags;
|
||||
|
||||
NoiseParams np_cave1;
|
||||
NoiseParams np_cave2;
|
||||
NoiseParams np_cavern;
|
||||
float cave_width;
|
||||
float cavern_limit;
|
||||
float cavern_taper;
|
||||
float cavern_threshold;
|
||||
int lava_depth;
|
||||
};
|
454
src/mapgen/mapgen_carpathian.cpp
Normal file
454
src/mapgen/mapgen_carpathian.cpp
Normal file
|
@ -0,0 +1,454 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2010-2016 paramat, Matt Gregory
|
||||
Copyright (C) 2010-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2017 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
|
||||
|
||||
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 <cmath>
|
||||
#include "mapgen.h"
|
||||
#include "voxel.h"
|
||||
#include "noise.h"
|
||||
#include "mapblock.h"
|
||||
#include "mapnode.h"
|
||||
#include "map.h"
|
||||
#include "content_sao.h"
|
||||
#include "nodedef.h"
|
||||
#include "voxelalgorithms.h"
|
||||
//#include "profiler.h" // For TimeTaker
|
||||
#include "settings.h" // For g_settings
|
||||
#include "emerge.h"
|
||||
#include "dungeongen.h"
|
||||
#include "cavegen.h"
|
||||
#include "mg_biome.h"
|
||||
#include "mg_ore.h"
|
||||
#include "mg_decoration.h"
|
||||
#include "mapgen_carpathian.h"
|
||||
|
||||
|
||||
FlagDesc flagdesc_mapgen_carpathian[] = {
|
||||
{"caverns", MGCARPATHIAN_CAVERNS},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
MapgenCarpathian::MapgenCarpathian(
|
||||
int mapgenid, MapgenCarpathianParams *params, EmergeManager *emerge)
|
||||
: MapgenBasic(mapgenid, params, emerge)
|
||||
{
|
||||
spflags = params->spflags;
|
||||
cave_width = params->cave_width;
|
||||
large_cave_depth = params->large_cave_depth;
|
||||
lava_depth = params->lava_depth;
|
||||
cavern_limit = params->cavern_limit;
|
||||
cavern_taper = params->cavern_taper;
|
||||
cavern_threshold = params->cavern_threshold;
|
||||
grad_wl = 1 - water_level;
|
||||
|
||||
//// 2D Terrain noise
|
||||
noise_base = new Noise(¶ms->np_base, seed, csize.X, csize.Z);
|
||||
noise_filler_depth = new Noise(¶ms->np_filler_depth, seed, csize.X, csize.Z);
|
||||
noise_height1 = new Noise(¶ms->np_height1, seed, csize.X, csize.Z);
|
||||
noise_height2 = new Noise(¶ms->np_height2, seed, csize.X, csize.Z);
|
||||
noise_height3 = new Noise(¶ms->np_height3, seed, csize.X, csize.Z);
|
||||
noise_height4 = new Noise(¶ms->np_height4, seed, csize.X, csize.Z);
|
||||
noise_hills_terrain = new Noise(¶ms->np_hills_terrain, seed, csize.X, csize.Z);
|
||||
noise_ridge_terrain = new Noise(¶ms->np_ridge_terrain, seed, csize.X, csize.Z);
|
||||
noise_step_terrain = new Noise(¶ms->np_step_terrain, seed, csize.X, csize.Z);
|
||||
noise_hills = new Noise(¶ms->np_hills, seed, csize.X, csize.Z);
|
||||
noise_ridge_mnt = new Noise(¶ms->np_ridge_mnt, seed, csize.X, csize.Z);
|
||||
noise_step_mnt = new Noise(¶ms->np_step_mnt, seed, csize.X, csize.Z);
|
||||
|
||||
//// 3D terrain noise
|
||||
// 1 up 1 down overgeneration
|
||||
noise_mnt_var = new Noise(¶ms->np_mnt_var, seed, csize.X, csize.Y + 2, csize.Z);
|
||||
|
||||
//// Cave noise
|
||||
MapgenBasic::np_cave1 = params->np_cave1;
|
||||
MapgenBasic::np_cave2 = params->np_cave2;
|
||||
MapgenBasic::np_cavern = params->np_cavern;
|
||||
}
|
||||
|
||||
|
||||
MapgenCarpathian::~MapgenCarpathian()
|
||||
{
|
||||
delete noise_base;
|
||||
delete noise_filler_depth;
|
||||
delete noise_height1;
|
||||
delete noise_height2;
|
||||
delete noise_height3;
|
||||
delete noise_height4;
|
||||
delete noise_hills_terrain;
|
||||
delete noise_ridge_terrain;
|
||||
delete noise_step_terrain;
|
||||
delete noise_hills;
|
||||
delete noise_ridge_mnt;
|
||||
delete noise_step_mnt;
|
||||
delete noise_mnt_var;
|
||||
}
|
||||
|
||||
|
||||
MapgenCarpathianParams::MapgenCarpathianParams():
|
||||
np_base (12, 1, v3f(2557, 2557, 2557), 6538, 4, 0.8, 0.5),
|
||||
np_filler_depth (0, 1, v3f(128, 128, 128), 261, 3, 0.7, 2.0),
|
||||
np_height1 (0, 5, v3f(251, 251, 251), 9613, 5, 0.5, 2.0),
|
||||
np_height2 (0, 5, v3f(383, 383, 383), 1949, 5, 0.5, 2.0),
|
||||
np_height3 (0, 5, v3f(509, 509, 509), 3211, 5, 0.5, 2.0),
|
||||
np_height4 (0, 5, v3f(631, 631, 631), 1583, 5, 0.5, 2.0),
|
||||
np_hills_terrain (1, 1, v3f(1301, 1301, 1301), 1692, 5, 0.5, 2.0),
|
||||
np_ridge_terrain (1, 1, v3f(1889, 1889, 1889), 3568, 5, 0.5, 2.0),
|
||||
np_step_terrain (1, 1, v3f(1889, 1889, 1889), 4157, 5, 0.5, 2.0),
|
||||
np_hills (0, 3, v3f(257, 257, 257), 6604, 6, 0.5, 2.0),
|
||||
np_ridge_mnt (0, 12, v3f(743, 743, 743), 5520, 6, 0.7, 2.0),
|
||||
np_step_mnt (0, 8, v3f(509, 509, 509), 2590, 6, 0.6, 2.0),
|
||||
np_mnt_var (0, 1, v3f(499, 499, 499), 2490, 5, 0.55, 2.0),
|
||||
np_cave1 (0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0),
|
||||
np_cave2 (0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0),
|
||||
np_cavern (0, 1, v3f(384, 128, 384), 723, 5, 0.63, 2.0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void MapgenCarpathianParams::readParams(const Settings *settings)
|
||||
{
|
||||
settings->getFlagStrNoEx("mgcarpathian_spflags", spflags, flagdesc_mapgen_carpathian);
|
||||
settings->getFloatNoEx("mgcarpathian_cave_width", cave_width);
|
||||
settings->getS16NoEx("mgcarpathian_large_cave_depth", large_cave_depth);
|
||||
settings->getS16NoEx("mgcarpathian_lava_depth", lava_depth);
|
||||
settings->getS16NoEx("mgcarpathian_cavern_limit", cavern_limit);
|
||||
settings->getS16NoEx("mgcarpathian_cavern_taper", cavern_taper);
|
||||
settings->getFloatNoEx("mgcarpathian_cavern_threshold", cavern_threshold);
|
||||
|
||||
settings->getNoiseParams("mgcarpathian_np_base", np_base);
|
||||
settings->getNoiseParams("mgcarpathian_np_filler_depth", np_filler_depth);
|
||||
settings->getNoiseParams("mgcarpathian_np_height1", np_height1);
|
||||
settings->getNoiseParams("mgcarpathian_np_height2", np_height2);
|
||||
settings->getNoiseParams("mgcarpathian_np_height3", np_height3);
|
||||
settings->getNoiseParams("mgcarpathian_np_height4", np_height4);
|
||||
settings->getNoiseParams("mgcarpathian_np_hills_terrain", np_hills_terrain);
|
||||
settings->getNoiseParams("mgcarpathian_np_ridge_terrain", np_ridge_terrain);
|
||||
settings->getNoiseParams("mgcarpathian_np_step_terrain", np_step_terrain);
|
||||
settings->getNoiseParams("mgcarpathian_np_hills", np_hills);
|
||||
settings->getNoiseParams("mgcarpathian_np_ridge_mnt", np_ridge_mnt);
|
||||
settings->getNoiseParams("mgcarpathian_np_step_mnt", np_step_mnt);
|
||||
settings->getNoiseParams("mgcarpathian_np_mnt_var", np_mnt_var);
|
||||
settings->getNoiseParams("mgcarpathian_np_cave1", np_cave1);
|
||||
settings->getNoiseParams("mgcarpathian_np_cave2", np_cave2);
|
||||
settings->getNoiseParams("mgcarpathian_np_cavern", np_cavern);
|
||||
}
|
||||
|
||||
|
||||
void MapgenCarpathianParams::writeParams(Settings *settings) const
|
||||
{
|
||||
settings->setFlagStr("mgcarpathian_spflags", spflags, flagdesc_mapgen_carpathian, U32_MAX);
|
||||
settings->setFloat("mgcarpathian_cave_width", cave_width);
|
||||
settings->setS16("mgcarpathian_large_cave_depth", large_cave_depth);
|
||||
settings->setS16("mgcarpathian_lava_depth", lava_depth);
|
||||
settings->setS16("mgcarpathian_cavern_limit", cavern_limit);
|
||||
settings->setS16("mgcarpathian_cavern_taper", cavern_taper);
|
||||
settings->setFloat("mgcarpathian_cavern_threshold", cavern_threshold);
|
||||
|
||||
settings->setNoiseParams("mgcarpathian_np_base", np_base);
|
||||
settings->setNoiseParams("mgcarpathian_np_filler_depth", np_filler_depth);
|
||||
settings->setNoiseParams("mgcarpathian_np_height1", np_height1);
|
||||
settings->setNoiseParams("mgcarpathian_np_height2", np_height2);
|
||||
settings->setNoiseParams("mgcarpathian_np_height3", np_height3);
|
||||
settings->setNoiseParams("mgcarpathian_np_height4", np_height4);
|
||||
settings->setNoiseParams("mgcarpathian_np_hills_terrain", np_hills_terrain);
|
||||
settings->setNoiseParams("mgcarpathian_np_ridge_terrain", np_ridge_terrain);
|
||||
settings->setNoiseParams("mgcarpathian_np_step_terrain", np_step_terrain);
|
||||
settings->setNoiseParams("mgcarpathian_np_hills", np_hills);
|
||||
settings->setNoiseParams("mgcarpathian_np_ridge_mnt", np_ridge_mnt);
|
||||
settings->setNoiseParams("mgcarpathian_np_step_mnt", np_step_mnt);
|
||||
settings->setNoiseParams("mgcarpathian_np_mnt_var", np_mnt_var);
|
||||
settings->setNoiseParams("mgcarpathian_np_cave1", np_cave1);
|
||||
settings->setNoiseParams("mgcarpathian_np_cave2", np_cave2);
|
||||
settings->setNoiseParams("mgcarpathian_np_cavern", np_cavern);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Lerp function
|
||||
inline float MapgenCarpathian::getLerp(float noise1, float noise2, float mod)
|
||||
{
|
||||
return noise1 + mod * (noise2 - noise1);
|
||||
}
|
||||
|
||||
// Steps function
|
||||
float MapgenCarpathian::getSteps(float noise)
|
||||
{
|
||||
float w = 0.5f;
|
||||
float k = floor(noise / w);
|
||||
float f = (noise - k * w) / w;
|
||||
float s = std::fmin(2.f * f, 1.f);
|
||||
return (k + s) * w;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void MapgenCarpathian::makeChunk(BlockMakeData *data)
|
||||
{
|
||||
// Pre-conditions
|
||||
assert(data->vmanip);
|
||||
assert(data->nodedef);
|
||||
assert(data->blockpos_requested.X >= data->blockpos_min.X &&
|
||||
data->blockpos_requested.Y >= data->blockpos_min.Y &&
|
||||
data->blockpos_requested.Z >= data->blockpos_min.Z);
|
||||
assert(data->blockpos_requested.X <= data->blockpos_max.X &&
|
||||
data->blockpos_requested.Y <= data->blockpos_max.Y &&
|
||||
data->blockpos_requested.Z <= data->blockpos_max.Z);
|
||||
|
||||
this->generating = true;
|
||||
this->vm = data->vmanip;
|
||||
this->ndef = data->nodedef;
|
||||
|
||||
v3s16 blockpos_min = data->blockpos_min;
|
||||
v3s16 blockpos_max = data->blockpos_max;
|
||||
node_min = blockpos_min * MAP_BLOCKSIZE;
|
||||
node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
|
||||
full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
|
||||
// Create a block-specific seed
|
||||
blockseed = getBlockSeed2(full_node_min, seed);
|
||||
|
||||
// Generate terrain
|
||||
s16 stone_surface_max_y = generateTerrain();
|
||||
|
||||
// Create heightmap
|
||||
updateHeightmap(node_min, node_max);
|
||||
|
||||
// Init biome generator, place biome-specific nodes, and build biomemap
|
||||
biomegen->calcBiomeNoise(node_min);
|
||||
|
||||
MgStoneType mgstone_type;
|
||||
content_t biome_stone;
|
||||
generateBiomes(&mgstone_type, &biome_stone);
|
||||
|
||||
// Generate caverns, tunnels and classic caves
|
||||
if (flags & MG_CAVES) {
|
||||
bool has_cavern = false;
|
||||
// Generate caverns
|
||||
if (spflags & MGCARPATHIAN_CAVERNS)
|
||||
has_cavern = generateCaverns(stone_surface_max_y);
|
||||
// Generate tunnels and classic caves
|
||||
if (has_cavern)
|
||||
// Disable classic caves in this mapchunk by setting
|
||||
// 'large cave depth' to world base. Avoids excessive liquid in
|
||||
// large caverns and floating blobs of overgenerated liquid.
|
||||
generateCaves(stone_surface_max_y, -MAX_MAP_GENERATION_LIMIT);
|
||||
else
|
||||
generateCaves(stone_surface_max_y, large_cave_depth);
|
||||
}
|
||||
|
||||
// Generate dungeons
|
||||
if (flags & MG_DUNGEONS)
|
||||
generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
|
||||
|
||||
// Generate the registered decorations
|
||||
if (flags & MG_DECORATIONS)
|
||||
m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
|
||||
|
||||
// Generate the registered ores
|
||||
m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
|
||||
|
||||
// Sprinkle some dust on top after everything else was generated
|
||||
dustTopNodes();
|
||||
|
||||
// Update liquids
|
||||
updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
|
||||
|
||||
// Calculate lighting
|
||||
if (flags & MG_LIGHT) {
|
||||
calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
|
||||
full_node_min, full_node_max);
|
||||
}
|
||||
|
||||
this->generating = false;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int MapgenCarpathian::getSpawnLevelAtPoint(v2s16 p)
|
||||
{
|
||||
s16 level_at_point = terrainLevelAtPoint(p.X, p.Y);
|
||||
if (level_at_point <= water_level || level_at_point > water_level + 32)
|
||||
return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
|
||||
|
||||
return level_at_point;
|
||||
}
|
||||
|
||||
|
||||
float MapgenCarpathian::terrainLevelAtPoint(s16 x, s16 z)
|
||||
{
|
||||
float ground = NoisePerlin2D(&noise_base->np, x, z, seed);
|
||||
float height1 = NoisePerlin2D(&noise_height1->np, x, z, seed);
|
||||
float height2 = NoisePerlin2D(&noise_height2->np, x, z, seed);
|
||||
float height3 = NoisePerlin2D(&noise_height3->np, x, z, seed);
|
||||
float height4 = NoisePerlin2D(&noise_height4->np, x, z, seed);
|
||||
float hter = NoisePerlin2D(&noise_hills_terrain->np, x, z, seed);
|
||||
float rter = NoisePerlin2D(&noise_ridge_terrain->np, x, z, seed);
|
||||
float ster = NoisePerlin2D(&noise_step_terrain->np, x, z, seed);
|
||||
float n_hills = NoisePerlin2D(&noise_hills->np, x, z, seed);
|
||||
float n_ridge_mnt = NoisePerlin2D(&noise_ridge_mnt->np, x, z, seed);
|
||||
float n_step_mnt = NoisePerlin2D(&noise_step_mnt->np, x, z, seed);
|
||||
|
||||
int height = -MAX_MAP_GENERATION_LIMIT;
|
||||
|
||||
for (s16 y = 1; y <= 30; y++) {
|
||||
float mnt_var = NoisePerlin3D(&noise_mnt_var->np, x, y, z, seed);
|
||||
|
||||
// Gradient & shallow seabed
|
||||
s32 grad = (y < water_level) ? grad_wl + (water_level - y) * 3 : 1 - y;
|
||||
|
||||
// Hill/Mountain height (hilliness)
|
||||
float hill1 = getLerp(height1, height2, mnt_var);
|
||||
float hill2 = getLerp(height3, height4, mnt_var);
|
||||
float hill3 = getLerp(height3, height2, mnt_var);
|
||||
float hill4 = getLerp(height1, height4, mnt_var);
|
||||
float hilliness = std::fmax(std::fmin(hill1, hill2), std::fmin(hill3, hill4));
|
||||
|
||||
// Rolling hills
|
||||
float hill_mnt = hilliness * pow(n_hills, 2.f);
|
||||
float hills = pow(hter, 3.f) * hill_mnt;
|
||||
|
||||
// Ridged mountains
|
||||
float ridge_mnt = hilliness * (1.f - fabs(n_ridge_mnt));
|
||||
float ridged_mountains = pow(rter, 3.f) * ridge_mnt;
|
||||
|
||||
// Step (terraced) mountains
|
||||
float step_mnt = hilliness * getSteps(n_step_mnt);
|
||||
float step_mountains = pow(ster, 3.f) * step_mnt;
|
||||
|
||||
// Final terrain level
|
||||
float mountains = hills + ridged_mountains + step_mountains;
|
||||
float surface_level = ground + mountains + grad;
|
||||
|
||||
if (y > surface_level && height < 0)
|
||||
height = y;
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int MapgenCarpathian::generateTerrain()
|
||||
{
|
||||
MapNode mn_air(CONTENT_AIR);
|
||||
MapNode mn_stone(c_stone);
|
||||
MapNode mn_water(c_water_source);
|
||||
|
||||
s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
|
||||
u32 index2d = 0;
|
||||
u32 index3d = 0;
|
||||
|
||||
// Calculate noise for terrain generation
|
||||
noise_base->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_height1->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_height2->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_height3->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_height4->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_hills_terrain->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_ridge_terrain->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_step_terrain->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_hills->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_ridge_mnt->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_step_mnt->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_mnt_var->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
|
||||
|
||||
//// Place nodes
|
||||
for (s16 z = node_min.Z; z <= node_max.Z; z++) {
|
||||
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
|
||||
u32 vi = vm->m_area.index(node_min.X, y, z);
|
||||
for (s16 x = node_min.X; x <= node_max.X;
|
||||
x++, vi++, index2d++, index3d++) {
|
||||
if (vm->m_data[vi].getContent() != CONTENT_IGNORE)
|
||||
continue;
|
||||
|
||||
// Base terrain
|
||||
float ground = noise_base->result[index2d];
|
||||
|
||||
// Gradient & shallow seabed
|
||||
s32 grad = (y < water_level) ? grad_wl + (water_level - y) * 3 : 1 - y;
|
||||
|
||||
// Hill/Mountain height (hilliness)
|
||||
float height1 = noise_height1->result[index2d];
|
||||
float height2 = noise_height2->result[index2d];
|
||||
float height3 = noise_height3->result[index2d];
|
||||
float height4 = noise_height4->result[index2d];
|
||||
float mnt_var = noise_mnt_var->result[index3d];
|
||||
// Combine height noises and apply 3D variation
|
||||
float hill1 = getLerp(height1, height2, mnt_var);
|
||||
float hill2 = getLerp(height3, height4, mnt_var);
|
||||
float hill3 = getLerp(height3, height2, mnt_var);
|
||||
float hill4 = getLerp(height1, height4, mnt_var);
|
||||
// 'hilliness' determines whether hills/mountains are
|
||||
// small or large
|
||||
float hilliness = std::fmax(std::fmin(hill1, hill2), std::fmin(hill3, hill4));
|
||||
|
||||
// Rolling hills
|
||||
float hter = noise_hills_terrain->result[index2d];
|
||||
float n_hills = noise_hills->result[index2d];
|
||||
float hill_mnt = hilliness * pow(n_hills, 2.f);
|
||||
float hills = pow(fabs(hter), 3.f) * hill_mnt;
|
||||
|
||||
// Ridged mountains
|
||||
float rter = noise_ridge_terrain->result[index2d];
|
||||
float n_ridge_mnt = noise_ridge_mnt->result[index2d];
|
||||
float ridge_mnt = hilliness * (1.f - fabs(n_ridge_mnt));
|
||||
float ridged_mountains = pow(fabs(rter), 3.f) * ridge_mnt;
|
||||
|
||||
// Step (terraced) mountains
|
||||
float ster = noise_step_terrain->result[index2d];
|
||||
float n_step_mnt = noise_step_mnt->result[index2d];
|
||||
float step_mnt = hilliness * getSteps(n_step_mnt);
|
||||
float step_mountains = pow(fabs(ster), 3.f) * step_mnt;
|
||||
|
||||
// Final terrain level
|
||||
float mountains = hills + ridged_mountains + step_mountains;
|
||||
float surface_level = ground + mountains + grad;
|
||||
|
||||
if (y < surface_level) {
|
||||
vm->m_data[vi] = mn_stone; // Stone
|
||||
if (y > stone_surface_max_y)
|
||||
stone_surface_max_y = y;
|
||||
} else if (y <= water_level) {
|
||||
vm->m_data[vi] = mn_water; // Sea water
|
||||
} else {
|
||||
vm->m_data[vi] = mn_air; // Air
|
||||
}
|
||||
}
|
||||
index2d -= ystride;
|
||||
}
|
||||
index2d += ystride;
|
||||
}
|
||||
|
||||
return stone_surface_max_y;
|
||||
}
|
102
src/mapgen/mapgen_carpathian.h
Normal file
102
src/mapgen/mapgen_carpathian.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2010-2016 paramat, Matt Gregory
|
||||
Copyright (C) 2010-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2017 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mapgen.h"
|
||||
|
||||
///////// Mapgen Carpathian flags
|
||||
#define MGCARPATHIAN_CAVERNS 0x01
|
||||
|
||||
class BiomeManager;
|
||||
|
||||
extern FlagDesc flagdesc_mapgen_carpathian[];
|
||||
|
||||
|
||||
struct MapgenCarpathianParams : public MapgenParams
|
||||
{
|
||||
u32 spflags = MGCARPATHIAN_CAVERNS;
|
||||
float cave_width = 0.09f;
|
||||
s16 large_cave_depth = -33;
|
||||
s16 lava_depth = -256;
|
||||
s16 cavern_limit = -256;
|
||||
s16 cavern_taper = 256;
|
||||
float cavern_threshold = 0.7f;
|
||||
|
||||
NoiseParams np_base;
|
||||
NoiseParams np_filler_depth;
|
||||
NoiseParams np_height1;
|
||||
NoiseParams np_height2;
|
||||
NoiseParams np_height3;
|
||||
NoiseParams np_height4;
|
||||
NoiseParams np_hills_terrain;
|
||||
NoiseParams np_ridge_terrain;
|
||||
NoiseParams np_step_terrain;
|
||||
NoiseParams np_hills;
|
||||
NoiseParams np_ridge_mnt;
|
||||
NoiseParams np_step_mnt;
|
||||
NoiseParams np_mnt_var;
|
||||
NoiseParams np_cave1;
|
||||
NoiseParams np_cave2;
|
||||
NoiseParams np_cavern;
|
||||
|
||||
MapgenCarpathianParams();
|
||||
~MapgenCarpathianParams() = default;
|
||||
|
||||
void readParams(const Settings *settings);
|
||||
void writeParams(Settings *settings) const;
|
||||
};
|
||||
|
||||
class MapgenCarpathian : public MapgenBasic
|
||||
{
|
||||
public:
|
||||
MapgenCarpathian(int mapgenid, MapgenCarpathianParams *params,
|
||||
EmergeManager *emerge);
|
||||
~MapgenCarpathian();
|
||||
|
||||
virtual MapgenType getType() const { return MAPGEN_CARPATHIAN; }
|
||||
|
||||
float getSteps(float noise);
|
||||
inline float getLerp(float noise1, float noise2, float mod);
|
||||
|
||||
virtual void makeChunk(BlockMakeData *data);
|
||||
int getSpawnLevelAtPoint(v2s16 p);
|
||||
|
||||
private:
|
||||
s16 large_cave_depth;
|
||||
s32 grad_wl;
|
||||
|
||||
Noise *noise_base;
|
||||
Noise *noise_height1;
|
||||
Noise *noise_height2;
|
||||
Noise *noise_height3;
|
||||
Noise *noise_height4;
|
||||
Noise *noise_hills_terrain;
|
||||
Noise *noise_ridge_terrain;
|
||||
Noise *noise_step_terrain;
|
||||
Noise *noise_hills;
|
||||
Noise *noise_ridge_mnt;
|
||||
Noise *noise_step_mnt;
|
||||
Noise *noise_mnt_var;
|
||||
|
||||
float terrainLevelAtPoint(s16 x, s16 z);
|
||||
int generateTerrain();
|
||||
};
|
274
src/mapgen/mapgen_flat.cpp
Normal file
274
src/mapgen/mapgen_flat.cpp
Normal file
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2015-2017 paramat
|
||||
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
|
||||
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 "mapgen.h"
|
||||
#include "voxel.h"
|
||||
#include "noise.h"
|
||||
#include "mapblock.h"
|
||||
#include "mapnode.h"
|
||||
#include "map.h"
|
||||
#include "content_sao.h"
|
||||
#include "nodedef.h"
|
||||
#include "voxelalgorithms.h"
|
||||
//#include "profiler.h" // For TimeTaker
|
||||
#include "settings.h" // For g_settings
|
||||
#include "emerge.h"
|
||||
#include "dungeongen.h"
|
||||
#include "cavegen.h"
|
||||
#include "mg_biome.h"
|
||||
#include "mg_ore.h"
|
||||
#include "mg_decoration.h"
|
||||
#include "mapgen_flat.h"
|
||||
|
||||
|
||||
FlagDesc flagdesc_mapgen_flat[] = {
|
||||
{"lakes", MGFLAT_LAKES},
|
||||
{"hills", MGFLAT_HILLS},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
MapgenFlat::MapgenFlat(int mapgenid, MapgenFlatParams *params, EmergeManager *emerge)
|
||||
: MapgenBasic(mapgenid, params, emerge)
|
||||
{
|
||||
spflags = params->spflags;
|
||||
ground_level = params->ground_level;
|
||||
large_cave_depth = params->large_cave_depth;
|
||||
lava_depth = params->lava_depth;
|
||||
cave_width = params->cave_width;
|
||||
lake_threshold = params->lake_threshold;
|
||||
lake_steepness = params->lake_steepness;
|
||||
hill_threshold = params->hill_threshold;
|
||||
hill_steepness = params->hill_steepness;
|
||||
|
||||
// 2D noise
|
||||
noise_filler_depth = new Noise(¶ms->np_filler_depth, seed, csize.X, csize.Z);
|
||||
|
||||
if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
|
||||
noise_terrain = new Noise(¶ms->np_terrain, seed, csize.X, csize.Z);
|
||||
// 3D noise
|
||||
MapgenBasic::np_cave1 = params->np_cave1;
|
||||
MapgenBasic::np_cave2 = params->np_cave2;
|
||||
}
|
||||
|
||||
|
||||
MapgenFlat::~MapgenFlat()
|
||||
{
|
||||
delete noise_filler_depth;
|
||||
|
||||
if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
|
||||
delete noise_terrain;
|
||||
}
|
||||
|
||||
|
||||
MapgenFlatParams::MapgenFlatParams():
|
||||
np_terrain (0, 1, v3f(600, 600, 600), 7244, 5, 0.6, 2.0),
|
||||
np_filler_depth (0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0),
|
||||
np_cave1 (0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0),
|
||||
np_cave2 (0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void MapgenFlatParams::readParams(const Settings *settings)
|
||||
{
|
||||
settings->getFlagStrNoEx("mgflat_spflags", spflags, flagdesc_mapgen_flat);
|
||||
settings->getS16NoEx("mgflat_ground_level", ground_level);
|
||||
settings->getS16NoEx("mgflat_large_cave_depth", large_cave_depth);
|
||||
settings->getS16NoEx("mgflat_lava_depth", lava_depth);
|
||||
settings->getFloatNoEx("mgflat_cave_width", cave_width);
|
||||
settings->getFloatNoEx("mgflat_lake_threshold", lake_threshold);
|
||||
settings->getFloatNoEx("mgflat_lake_steepness", lake_steepness);
|
||||
settings->getFloatNoEx("mgflat_hill_threshold", hill_threshold);
|
||||
settings->getFloatNoEx("mgflat_hill_steepness", hill_steepness);
|
||||
|
||||
settings->getNoiseParams("mgflat_np_terrain", np_terrain);
|
||||
settings->getNoiseParams("mgflat_np_filler_depth", np_filler_depth);
|
||||
settings->getNoiseParams("mgflat_np_cave1", np_cave1);
|
||||
settings->getNoiseParams("mgflat_np_cave2", np_cave2);
|
||||
}
|
||||
|
||||
|
||||
void MapgenFlatParams::writeParams(Settings *settings) const
|
||||
{
|
||||
settings->setFlagStr("mgflat_spflags", spflags, flagdesc_mapgen_flat, U32_MAX);
|
||||
settings->setS16("mgflat_ground_level", ground_level);
|
||||
settings->setS16("mgflat_large_cave_depth", large_cave_depth);
|
||||
settings->setS16("mgflat_lava_depth", lava_depth);
|
||||
settings->setFloat("mgflat_cave_width", cave_width);
|
||||
settings->setFloat("mgflat_lake_threshold", lake_threshold);
|
||||
settings->setFloat("mgflat_lake_steepness", lake_steepness);
|
||||
settings->setFloat("mgflat_hill_threshold", hill_threshold);
|
||||
settings->setFloat("mgflat_hill_steepness", hill_steepness);
|
||||
|
||||
settings->setNoiseParams("mgflat_np_terrain", np_terrain);
|
||||
settings->setNoiseParams("mgflat_np_filler_depth", np_filler_depth);
|
||||
settings->setNoiseParams("mgflat_np_cave1", np_cave1);
|
||||
settings->setNoiseParams("mgflat_np_cave2", np_cave2);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int MapgenFlat::getSpawnLevelAtPoint(v2s16 p)
|
||||
{
|
||||
s16 level_at_point = ground_level;
|
||||
float n_terrain = 0.0f;
|
||||
if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
|
||||
n_terrain = NoisePerlin2D(&noise_terrain->np, p.X, p.Y, seed);
|
||||
|
||||
if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) {
|
||||
level_at_point = ground_level -
|
||||
(lake_threshold - n_terrain) * lake_steepness;
|
||||
} else if ((spflags & MGFLAT_HILLS) && n_terrain > hill_threshold) {
|
||||
level_at_point = ground_level +
|
||||
(n_terrain - hill_threshold) * hill_steepness;
|
||||
}
|
||||
|
||||
if (ground_level < water_level) // Ocean world, allow spawn in water
|
||||
return MYMAX(level_at_point, water_level);
|
||||
|
||||
if (level_at_point > water_level)
|
||||
return level_at_point; // Spawn on land
|
||||
|
||||
return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
|
||||
}
|
||||
|
||||
|
||||
void MapgenFlat::makeChunk(BlockMakeData *data)
|
||||
{
|
||||
// Pre-conditions
|
||||
assert(data->vmanip);
|
||||
assert(data->nodedef);
|
||||
assert(data->blockpos_requested.X >= data->blockpos_min.X &&
|
||||
data->blockpos_requested.Y >= data->blockpos_min.Y &&
|
||||
data->blockpos_requested.Z >= data->blockpos_min.Z);
|
||||
assert(data->blockpos_requested.X <= data->blockpos_max.X &&
|
||||
data->blockpos_requested.Y <= data->blockpos_max.Y &&
|
||||
data->blockpos_requested.Z <= data->blockpos_max.Z);
|
||||
|
||||
this->generating = true;
|
||||
this->vm = data->vmanip;
|
||||
this->ndef = data->nodedef;
|
||||
//TimeTaker t("makeChunk");
|
||||
|
||||
v3s16 blockpos_min = data->blockpos_min;
|
||||
v3s16 blockpos_max = data->blockpos_max;
|
||||
node_min = blockpos_min * MAP_BLOCKSIZE;
|
||||
node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
|
||||
full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
|
||||
blockseed = getBlockSeed2(full_node_min, seed);
|
||||
|
||||
// Generate base terrain, mountains, and ridges with initial heightmaps
|
||||
s16 stone_surface_max_y = generateTerrain();
|
||||
|
||||
// Create heightmap
|
||||
updateHeightmap(node_min, node_max);
|
||||
|
||||
// Init biome generator, place biome-specific nodes, and build biomemap
|
||||
biomegen->calcBiomeNoise(node_min);
|
||||
|
||||
MgStoneType mgstone_type;
|
||||
content_t biome_stone;
|
||||
generateBiomes(&mgstone_type, &biome_stone);
|
||||
|
||||
if (flags & MG_CAVES)
|
||||
generateCaves(stone_surface_max_y, large_cave_depth);
|
||||
|
||||
if (flags & MG_DUNGEONS)
|
||||
generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
|
||||
|
||||
// Generate the registered decorations
|
||||
if (flags & MG_DECORATIONS)
|
||||
m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
|
||||
|
||||
// Generate the registered ores
|
||||
m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
|
||||
|
||||
// Sprinkle some dust on top after everything else was generated
|
||||
dustTopNodes();
|
||||
|
||||
//printf("makeChunk: %dms\n", t.stop());
|
||||
|
||||
updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
|
||||
|
||||
if (flags & MG_LIGHT)
|
||||
calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
|
||||
full_node_min, full_node_max);
|
||||
|
||||
//setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
|
||||
// node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
|
||||
|
||||
this->generating = false;
|
||||
}
|
||||
|
||||
|
||||
s16 MapgenFlat::generateTerrain()
|
||||
{
|
||||
MapNode n_air(CONTENT_AIR);
|
||||
MapNode n_stone(c_stone);
|
||||
MapNode n_water(c_water_source);
|
||||
|
||||
const v3s16 &em = vm->m_area.getExtent();
|
||||
s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
|
||||
u32 ni2d = 0;
|
||||
|
||||
bool use_noise = (spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS);
|
||||
if (use_noise)
|
||||
noise_terrain->perlinMap2D(node_min.X, node_min.Z);
|
||||
|
||||
for (s16 z = node_min.Z; z <= node_max.Z; z++)
|
||||
for (s16 x = node_min.X; x <= node_max.X; x++, ni2d++) {
|
||||
s16 stone_level = ground_level;
|
||||
float n_terrain = use_noise ? noise_terrain->result[ni2d] : 0.0f;
|
||||
|
||||
if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) {
|
||||
s16 depress = (lake_threshold - n_terrain) * lake_steepness;
|
||||
stone_level = ground_level - depress;
|
||||
} else if ((spflags & MGFLAT_HILLS) && n_terrain > hill_threshold) {
|
||||
s16 rise = (n_terrain - hill_threshold) * hill_steepness;
|
||||
stone_level = ground_level + rise;
|
||||
}
|
||||
|
||||
u32 vi = vm->m_area.index(x, node_min.Y - 1, z);
|
||||
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
|
||||
if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
|
||||
if (y <= stone_level) {
|
||||
vm->m_data[vi] = n_stone;
|
||||
if (y > stone_surface_max_y)
|
||||
stone_surface_max_y = y;
|
||||
} else if (y <= water_level) {
|
||||
vm->m_data[vi] = n_water;
|
||||
} else {
|
||||
vm->m_data[vi] = n_air;
|
||||
}
|
||||
}
|
||||
vm->m_area.add_y(em, vi, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return stone_surface_max_y;
|
||||
}
|
76
src/mapgen/mapgen_flat.h
Normal file
76
src/mapgen/mapgen_flat.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2015-2017 paramat
|
||||
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mapgen.h"
|
||||
|
||||
/////// Mapgen Flat flags
|
||||
#define MGFLAT_LAKES 0x01
|
||||
#define MGFLAT_HILLS 0x02
|
||||
|
||||
class BiomeManager;
|
||||
|
||||
extern FlagDesc flagdesc_mapgen_flat[];
|
||||
|
||||
struct MapgenFlatParams : public MapgenParams
|
||||
{
|
||||
u32 spflags = 0;
|
||||
s16 ground_level = 8;
|
||||
s16 large_cave_depth = -33;
|
||||
s16 lava_depth = -256;
|
||||
float cave_width = 0.09f;
|
||||
float lake_threshold = -0.45f;
|
||||
float lake_steepness = 48.0f;
|
||||
float hill_threshold = 0.45f;
|
||||
float hill_steepness = 64.0f;
|
||||
NoiseParams np_terrain;
|
||||
NoiseParams np_filler_depth;
|
||||
NoiseParams np_cave1;
|
||||
NoiseParams np_cave2;
|
||||
|
||||
MapgenFlatParams();
|
||||
~MapgenFlatParams() = default;
|
||||
|
||||
void readParams(const Settings *settings);
|
||||
void writeParams(Settings *settings) const;
|
||||
};
|
||||
|
||||
class MapgenFlat : public MapgenBasic
|
||||
{
|
||||
public:
|
||||
MapgenFlat(int mapgenid, MapgenFlatParams *params, EmergeManager *emerge);
|
||||
~MapgenFlat();
|
||||
|
||||
virtual MapgenType getType() const { return MAPGEN_FLAT; }
|
||||
|
||||
virtual void makeChunk(BlockMakeData *data);
|
||||
int getSpawnLevelAtPoint(v2s16 p);
|
||||
s16 generateTerrain();
|
||||
|
||||
private:
|
||||
s16 ground_level;
|
||||
s16 large_cave_depth;
|
||||
float lake_threshold;
|
||||
float lake_steepness;
|
||||
float hill_threshold;
|
||||
float hill_steepness;
|
||||
Noise *noise_terrain;
|
||||
};
|
404
src/mapgen/mapgen_fractal.cpp
Normal file
404
src/mapgen/mapgen_fractal.cpp
Normal file
|
@ -0,0 +1,404 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2015-2017 paramat
|
||||
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
|
||||
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 "mapgen.h"
|
||||
#include "voxel.h"
|
||||
#include "noise.h"
|
||||
#include "mapblock.h"
|
||||
#include "mapnode.h"
|
||||
#include "map.h"
|
||||
#include "content_sao.h"
|
||||
#include "nodedef.h"
|
||||
#include "voxelalgorithms.h"
|
||||
//#include "profiler.h" // For TimeTaker
|
||||
#include "settings.h" // For g_settings
|
||||
#include "emerge.h"
|
||||
#include "dungeongen.h"
|
||||
#include "cavegen.h"
|
||||
#include "mg_biome.h"
|
||||
#include "mg_ore.h"
|
||||
#include "mg_decoration.h"
|
||||
#include "mapgen_fractal.h"
|
||||
|
||||
|
||||
FlagDesc flagdesc_mapgen_fractal[] = {
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
MapgenFractal::MapgenFractal(int mapgenid, MapgenFractalParams *params, EmergeManager *emerge)
|
||||
: MapgenBasic(mapgenid, params, emerge)
|
||||
{
|
||||
spflags = params->spflags;
|
||||
cave_width = params->cave_width;
|
||||
large_cave_depth = params->large_cave_depth;
|
||||
lava_depth = params->lava_depth;
|
||||
fractal = params->fractal;
|
||||
iterations = params->iterations;
|
||||
scale = params->scale;
|
||||
offset = params->offset;
|
||||
slice_w = params->slice_w;
|
||||
julia_x = params->julia_x;
|
||||
julia_y = params->julia_y;
|
||||
julia_z = params->julia_z;
|
||||
julia_w = params->julia_w;
|
||||
|
||||
//// 2D terrain noise
|
||||
noise_seabed = new Noise(¶ms->np_seabed, seed, csize.X, csize.Z);
|
||||
noise_filler_depth = new Noise(¶ms->np_filler_depth, seed, csize.X, csize.Z);
|
||||
|
||||
MapgenBasic::np_cave1 = params->np_cave1;
|
||||
MapgenBasic::np_cave2 = params->np_cave2;
|
||||
|
||||
formula = fractal / 2 + fractal % 2;
|
||||
julia = fractal % 2 == 0;
|
||||
}
|
||||
|
||||
|
||||
MapgenFractal::~MapgenFractal()
|
||||
{
|
||||
delete noise_seabed;
|
||||
delete noise_filler_depth;
|
||||
}
|
||||
|
||||
|
||||
MapgenFractalParams::MapgenFractalParams():
|
||||
np_seabed (-14, 9, v3f(600, 600, 600), 41900, 5, 0.6, 2.0),
|
||||
np_filler_depth (0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0),
|
||||
np_cave1 (0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0),
|
||||
np_cave2 (0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void MapgenFractalParams::readParams(const Settings *settings)
|
||||
{
|
||||
settings->getFlagStrNoEx("mgfractal_spflags", spflags, flagdesc_mapgen_fractal);
|
||||
settings->getFloatNoEx("mgfractal_cave_width", cave_width);
|
||||
settings->getS16NoEx("mgfractal_large_cave_depth", large_cave_depth);
|
||||
settings->getS16NoEx("mgfractal_lava_depth", lava_depth);
|
||||
settings->getU16NoEx("mgfractal_fractal", fractal);
|
||||
settings->getU16NoEx("mgfractal_iterations", iterations);
|
||||
settings->getV3FNoEx("mgfractal_scale", scale);
|
||||
settings->getV3FNoEx("mgfractal_offset", offset);
|
||||
settings->getFloatNoEx("mgfractal_slice_w", slice_w);
|
||||
settings->getFloatNoEx("mgfractal_julia_x", julia_x);
|
||||
settings->getFloatNoEx("mgfractal_julia_y", julia_y);
|
||||
settings->getFloatNoEx("mgfractal_julia_z", julia_z);
|
||||
settings->getFloatNoEx("mgfractal_julia_w", julia_w);
|
||||
|
||||
settings->getNoiseParams("mgfractal_np_seabed", np_seabed);
|
||||
settings->getNoiseParams("mgfractal_np_filler_depth", np_filler_depth);
|
||||
settings->getNoiseParams("mgfractal_np_cave1", np_cave1);
|
||||
settings->getNoiseParams("mgfractal_np_cave2", np_cave2);
|
||||
}
|
||||
|
||||
|
||||
void MapgenFractalParams::writeParams(Settings *settings) const
|
||||
{
|
||||
settings->setFlagStr("mgfractal_spflags", spflags, flagdesc_mapgen_fractal, U32_MAX);
|
||||
settings->setFloat("mgfractal_cave_width", cave_width);
|
||||
settings->setS16("mgfractal_large_cave_depth", large_cave_depth);
|
||||
settings->setS16("mgfractal_lava_depth", lava_depth);
|
||||
settings->setU16("mgfractal_fractal", fractal);
|
||||
settings->setU16("mgfractal_iterations", iterations);
|
||||
settings->setV3F("mgfractal_scale", scale);
|
||||
settings->setV3F("mgfractal_offset", offset);
|
||||
settings->setFloat("mgfractal_slice_w", slice_w);
|
||||
settings->setFloat("mgfractal_julia_x", julia_x);
|
||||
settings->setFloat("mgfractal_julia_y", julia_y);
|
||||
settings->setFloat("mgfractal_julia_z", julia_z);
|
||||
settings->setFloat("mgfractal_julia_w", julia_w);
|
||||
|
||||
settings->setNoiseParams("mgfractal_np_seabed", np_seabed);
|
||||
settings->setNoiseParams("mgfractal_np_filler_depth", np_filler_depth);
|
||||
settings->setNoiseParams("mgfractal_np_cave1", np_cave1);
|
||||
settings->setNoiseParams("mgfractal_np_cave2", np_cave2);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int MapgenFractal::getSpawnLevelAtPoint(v2s16 p)
|
||||
{
|
||||
bool solid_below = false; // Dry solid node is present below to spawn on
|
||||
u8 air_count = 0; // Consecutive air nodes above the dry solid node
|
||||
s16 seabed_level = NoisePerlin2D(&noise_seabed->np, p.X, p.Y, seed);
|
||||
// Seabed can rise above water_level or might be raised to create dry land
|
||||
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;
|
||||
air_count = 0;
|
||||
} else if (solid_below) { // Air above solid node
|
||||
air_count++;
|
||||
// 3 to account for snowblock dust
|
||||
if (air_count == 3)
|
||||
return y - 2;
|
||||
}
|
||||
}
|
||||
|
||||
return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
|
||||
}
|
||||
|
||||
|
||||
void MapgenFractal::makeChunk(BlockMakeData *data)
|
||||
{
|
||||
// Pre-conditions
|
||||
assert(data->vmanip);
|
||||
assert(data->nodedef);
|
||||
assert(data->blockpos_requested.X >= data->blockpos_min.X &&
|
||||
data->blockpos_requested.Y >= data->blockpos_min.Y &&
|
||||
data->blockpos_requested.Z >= data->blockpos_min.Z);
|
||||
assert(data->blockpos_requested.X <= data->blockpos_max.X &&
|
||||
data->blockpos_requested.Y <= data->blockpos_max.Y &&
|
||||
data->blockpos_requested.Z <= data->blockpos_max.Z);
|
||||
|
||||
this->generating = true;
|
||||
this->vm = data->vmanip;
|
||||
this->ndef = data->nodedef;
|
||||
//TimeTaker t("makeChunk");
|
||||
|
||||
v3s16 blockpos_min = data->blockpos_min;
|
||||
v3s16 blockpos_max = data->blockpos_max;
|
||||
node_min = blockpos_min * MAP_BLOCKSIZE;
|
||||
node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
|
||||
full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
|
||||
blockseed = getBlockSeed2(full_node_min, seed);
|
||||
|
||||
// Generate base terrain, mountains, and ridges with initial heightmaps
|
||||
s16 stone_surface_max_y = generateTerrain();
|
||||
|
||||
// Create heightmap
|
||||
updateHeightmap(node_min, node_max);
|
||||
|
||||
// Init biome generator, place biome-specific nodes, and build biomemap
|
||||
biomegen->calcBiomeNoise(node_min);
|
||||
|
||||
MgStoneType mgstone_type;
|
||||
content_t biome_stone;
|
||||
generateBiomes(&mgstone_type, &biome_stone);
|
||||
|
||||
if (flags & MG_CAVES)
|
||||
generateCaves(stone_surface_max_y, large_cave_depth);
|
||||
|
||||
if (flags & MG_DUNGEONS)
|
||||
generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
|
||||
|
||||
// Generate the registered decorations
|
||||
if (flags & MG_DECORATIONS)
|
||||
m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
|
||||
|
||||
// Generate the registered ores
|
||||
m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
|
||||
|
||||
// Sprinkle some dust on top after everything else was generated
|
||||
dustTopNodes();
|
||||
|
||||
//printf("makeChunk: %dms\n", t.stop());
|
||||
|
||||
updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
|
||||
|
||||
if (flags & MG_LIGHT)
|
||||
calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
|
||||
full_node_min, full_node_max);
|
||||
|
||||
//setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
|
||||
// node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
|
||||
|
||||
this->generating = false;
|
||||
}
|
||||
|
||||
|
||||
bool MapgenFractal::getFractalAtPoint(s16 x, s16 y, s16 z)
|
||||
{
|
||||
float cx, cy, cz, cw, ox, oy, oz, ow;
|
||||
|
||||
if (julia) { // Julia set
|
||||
cx = julia_x;
|
||||
cy = julia_y;
|
||||
cz = julia_z;
|
||||
cw = julia_w;
|
||||
ox = (float)x / scale.X - offset.X;
|
||||
oy = (float)y / scale.Y - offset.Y;
|
||||
oz = (float)z / scale.Z - offset.Z;
|
||||
ow = slice_w;
|
||||
} else { // Mandelbrot set
|
||||
cx = (float)x / scale.X - offset.X;
|
||||
cy = (float)y / scale.Y - offset.Y;
|
||||
cz = (float)z / scale.Z - offset.Z;
|
||||
cw = slice_w;
|
||||
ox = 0.0f;
|
||||
oy = 0.0f;
|
||||
oz = 0.0f;
|
||||
ow = 0.0f;
|
||||
}
|
||||
|
||||
float nx = 0.0f;
|
||||
float ny = 0.0f;
|
||||
float nz = 0.0f;
|
||||
float nw = 0.0f;
|
||||
|
||||
for (u16 iter = 0; iter < iterations; iter++) {
|
||||
switch (formula) {
|
||||
default:
|
||||
case 1: // 4D "Roundy"
|
||||
nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
|
||||
ny = 2.0f * (ox * oy + oz * ow) + cy;
|
||||
nz = 2.0f * (ox * oz + oy * ow) + cz;
|
||||
nw = 2.0f * (ox * ow + oy * oz) + cw;
|
||||
break;
|
||||
case 2: // 4D "Squarry"
|
||||
nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
|
||||
ny = 2.0f * (ox * oy + oz * ow) + cy;
|
||||
nz = 2.0f * (ox * oz + oy * ow) + cz;
|
||||
nw = 2.0f * (ox * ow - oy * oz) + cw;
|
||||
break;
|
||||
case 3: // 4D "Mandy Cousin"
|
||||
nx = ox * ox - oy * oy - oz * oz + ow * ow + cx;
|
||||
ny = 2.0f * (ox * oy + oz * ow) + cy;
|
||||
nz = 2.0f * (ox * oz + oy * ow) + cz;
|
||||
nw = 2.0f * (ox * ow + oy * oz) + cw;
|
||||
break;
|
||||
case 4: // 4D "Variation"
|
||||
nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
|
||||
ny = 2.0f * (ox * oy + oz * ow) + cy;
|
||||
nz = 2.0f * (ox * oz - oy * ow) + cz;
|
||||
nw = 2.0f * (ox * ow + oy * oz) + cw;
|
||||
break;
|
||||
case 5: // 3D "Mandelbrot/Mandelbar"
|
||||
nx = ox * ox - oy * oy - oz * oz + cx;
|
||||
ny = 2.0f * ox * oy + cy;
|
||||
nz = -2.0f * ox * oz + cz;
|
||||
break;
|
||||
case 6: // 3D "Christmas Tree"
|
||||
// Altering the formula here is necessary to avoid division by zero
|
||||
if (fabs(oz) < 0.000000001f) {
|
||||
nx = ox * ox - oy * oy - oz * oz + cx;
|
||||
ny = 2.0f * oy * ox + cy;
|
||||
nz = 4.0f * oz * ox + cz;
|
||||
} else {
|
||||
float a = (2.0f * ox) / (sqrt(oy * oy + oz * oz));
|
||||
nx = ox * ox - oy * oy - oz * oz + cx;
|
||||
ny = a * (oy * oy - oz * oz) + cy;
|
||||
nz = a * 2.0f * oy * oz + cz;
|
||||
}
|
||||
break;
|
||||
case 7: // 3D "Mandelbulb"
|
||||
if (fabs(oy) < 0.000000001f) {
|
||||
nx = ox * ox - oz * oz + cx;
|
||||
ny = cy;
|
||||
nz = -2.0f * oz * sqrt(ox * ox) + cz;
|
||||
} else {
|
||||
float a = 1.0f - (oz * oz) / (ox * ox + oy * oy);
|
||||
nx = (ox * ox - oy * oy) * a + cx;
|
||||
ny = 2.0f * ox * oy * a + cy;
|
||||
nz = -2.0f * oz * sqrt(ox * ox + oy * oy) + cz;
|
||||
}
|
||||
break;
|
||||
case 8: // 3D "Cosine Mandelbulb"
|
||||
if (fabs(oy) < 0.000000001f) {
|
||||
nx = 2.0f * ox * oz + cx;
|
||||
ny = 4.0f * oy * oz + cy;
|
||||
nz = oz * oz - ox * ox - oy * oy + cz;
|
||||
} else {
|
||||
float a = (2.0f * oz) / sqrt(ox * ox + oy * oy);
|
||||
nx = (ox * ox - oy * oy) * a + cx;
|
||||
ny = 2.0f * ox * oy * a + cy;
|
||||
nz = oz * oz - ox * ox - oy * oy + cz;
|
||||
}
|
||||
break;
|
||||
case 9: // 4D "Mandelbulb"
|
||||
float rxy = sqrt(ox * ox + oy * oy);
|
||||
float rxyz = sqrt(ox * ox + oy * oy + oz * oz);
|
||||
if (fabs(ow) < 0.000000001f && fabs(oz) < 0.000000001f) {
|
||||
nx = (ox * ox - oy * oy) + cx;
|
||||
ny = 2.0f * ox * oy + cy;
|
||||
nz = -2.0f * rxy * oz + cz;
|
||||
nw = 2.0f * rxyz * ow + cw;
|
||||
} else {
|
||||
float a = 1.0f - (ow * ow) / (rxyz * rxyz);
|
||||
float b = a * (1.0f - (oz * oz) / (rxy * rxy));
|
||||
nx = (ox * ox - oy * oy) * b + cx;
|
||||
ny = 2.0f * ox * oy * b + cy;
|
||||
nz = -2.0f * rxy * oz * a + cz;
|
||||
nw = 2.0f * rxyz * ow + cw;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (nx * nx + ny * ny + nz * nz + nw * nw > 4.0f)
|
||||
return false;
|
||||
|
||||
ox = nx;
|
||||
oy = ny;
|
||||
oz = nz;
|
||||
ow = nw;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
s16 MapgenFractal::generateTerrain()
|
||||
{
|
||||
MapNode n_air(CONTENT_AIR);
|
||||
MapNode n_stone(c_stone);
|
||||
MapNode n_water(c_water_source);
|
||||
|
||||
s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
|
||||
u32 index2d = 0;
|
||||
|
||||
noise_seabed->perlinMap2D(node_min.X, node_min.Z);
|
||||
|
||||
for (s16 z = node_min.Z; z <= node_max.Z; z++) {
|
||||
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
|
||||
u32 vi = vm->m_area.index(node_min.X, y, z);
|
||||
for (s16 x = node_min.X; x <= node_max.X; x++, vi++, index2d++) {
|
||||
if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
|
||||
s16 seabed_height = noise_seabed->result[index2d];
|
||||
|
||||
if (y <= seabed_height || getFractalAtPoint(x, y, z)) {
|
||||
vm->m_data[vi] = n_stone;
|
||||
if (y > stone_surface_max_y)
|
||||
stone_surface_max_y = y;
|
||||
} else if (y <= water_level) {
|
||||
vm->m_data[vi] = n_water;
|
||||
} else {
|
||||
vm->m_data[vi] = n_air;
|
||||
}
|
||||
}
|
||||
}
|
||||
index2d -= ystride;
|
||||
}
|
||||
index2d += ystride;
|
||||
}
|
||||
|
||||
return stone_surface_max_y;
|
||||
}
|
87
src/mapgen/mapgen_fractal.h
Normal file
87
src/mapgen/mapgen_fractal.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2015-2017 paramat
|
||||
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
|
||||
Fractal formulas from http://www.bugman123.com/Hypercomplex/index.html
|
||||
by Paul Nylander, and from http://www.fractalforums.com, thank you.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mapgen.h"
|
||||
|
||||
class BiomeManager;
|
||||
|
||||
extern FlagDesc flagdesc_mapgen_fractal[];
|
||||
|
||||
struct MapgenFractalParams : public MapgenParams
|
||||
{
|
||||
u32 spflags = 0;
|
||||
float cave_width = 0.09f;
|
||||
s16 large_cave_depth = -33;
|
||||
s16 lava_depth = -256;
|
||||
u16 fractal = 1;
|
||||
u16 iterations = 11;
|
||||
v3f scale = v3f(4096.0, 1024.0, 4096.0);
|
||||
v3f offset = v3f(1.52, 0.0, 0.0);
|
||||
float slice_w = 0.0f;
|
||||
float julia_x = 0.267f;
|
||||
float julia_y = 0.2f;
|
||||
float julia_z = 0.133f;
|
||||
float julia_w = 0.067f;
|
||||
NoiseParams np_seabed;
|
||||
NoiseParams np_filler_depth;
|
||||
NoiseParams np_cave1;
|
||||
NoiseParams np_cave2;
|
||||
|
||||
MapgenFractalParams();
|
||||
~MapgenFractalParams() = default;
|
||||
|
||||
void readParams(const Settings *settings);
|
||||
void writeParams(Settings *settings) const;
|
||||
};
|
||||
|
||||
class MapgenFractal : public MapgenBasic
|
||||
{
|
||||
public:
|
||||
MapgenFractal(int mapgenid, MapgenFractalParams *params, EmergeManager *emerge);
|
||||
~MapgenFractal();
|
||||
|
||||
virtual MapgenType getType() const { return MAPGEN_FRACTAL; }
|
||||
|
||||
virtual void makeChunk(BlockMakeData *data);
|
||||
int getSpawnLevelAtPoint(v2s16 p);
|
||||
bool getFractalAtPoint(s16 x, s16 y, s16 z);
|
||||
s16 generateTerrain();
|
||||
|
||||
private:
|
||||
u16 formula;
|
||||
bool julia;
|
||||
|
||||
s16 large_cave_depth;
|
||||
u16 fractal;
|
||||
u16 iterations;
|
||||
v3f scale;
|
||||
v3f offset;
|
||||
float slice_w;
|
||||
float julia_x;
|
||||
float julia_y;
|
||||
float julia_z;
|
||||
float julia_w;
|
||||
Noise *noise_seabed;
|
||||
};
|
102
src/mapgen/mapgen_singlenode.cpp
Normal file
102
src/mapgen/mapgen_singlenode.cpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2015-2017 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 "mapgen_singlenode.h"
|
||||
#include "voxel.h"
|
||||
#include "mapblock.h"
|
||||
#include "mapnode.h"
|
||||
#include "map.h"
|
||||
#include "nodedef.h"
|
||||
#include "voxelalgorithms.h"
|
||||
#include "emerge.h"
|
||||
|
||||
|
||||
MapgenSinglenode::MapgenSinglenode(int mapgenid,
|
||||
MapgenParams *params, EmergeManager *emerge)
|
||||
: Mapgen(mapgenid, params, emerge)
|
||||
{
|
||||
flags = params->flags;
|
||||
|
||||
INodeDefManager *ndef = emerge->ndef;
|
||||
|
||||
c_node = ndef->getId("mapgen_singlenode");
|
||||
if (c_node == CONTENT_IGNORE)
|
||||
c_node = CONTENT_AIR;
|
||||
|
||||
MapNode n_node(c_node);
|
||||
set_light = (ndef->get(n_node).sunlight_propagates) ? LIGHT_SUN : 0x00;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////// Map generator
|
||||
|
||||
void MapgenSinglenode::makeChunk(BlockMakeData *data)
|
||||
{
|
||||
// Pre-conditions
|
||||
assert(data->vmanip);
|
||||
assert(data->nodedef);
|
||||
assert(data->blockpos_requested.X >= data->blockpos_min.X &&
|
||||
data->blockpos_requested.Y >= data->blockpos_min.Y &&
|
||||
data->blockpos_requested.Z >= data->blockpos_min.Z);
|
||||
assert(data->blockpos_requested.X <= data->blockpos_max.X &&
|
||||
data->blockpos_requested.Y <= data->blockpos_max.Y &&
|
||||
data->blockpos_requested.Z <= data->blockpos_max.Z);
|
||||
|
||||
this->generating = true;
|
||||
this->vm = data->vmanip;
|
||||
this->ndef = data->nodedef;
|
||||
|
||||
v3s16 blockpos_min = data->blockpos_min;
|
||||
v3s16 blockpos_max = data->blockpos_max;
|
||||
|
||||
// Area of central chunk
|
||||
v3s16 node_min = blockpos_min * MAP_BLOCKSIZE;
|
||||
v3s16 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
|
||||
blockseed = getBlockSeed2(node_min, data->seed);
|
||||
|
||||
MapNode n_node(c_node);
|
||||
|
||||
for (s16 z = node_min.Z; z <= node_max.Z; z++)
|
||||
for (s16 y = node_min.Y; y <= node_max.Y; y++) {
|
||||
u32 i = vm->m_area.index(node_min.X, y, z);
|
||||
for (s16 x = node_min.X; x <= node_max.X; x++) {
|
||||
if (vm->m_data[i].getContent() == CONTENT_IGNORE)
|
||||
vm->m_data[i] = n_node;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Add top and bottom side of water to transforming_liquid queue
|
||||
updateLiquid(&data->transforming_liquid, node_min, node_max);
|
||||
|
||||
// Set lighting
|
||||
if ((flags & MG_LIGHT) && set_light == LIGHT_SUN)
|
||||
setLighting(LIGHT_SUN, node_min, node_max);
|
||||
|
||||
this->generating = false;
|
||||
}
|
||||
|
||||
|
||||
int MapgenSinglenode::getSpawnLevelAtPoint(v2s16 p)
|
||||
{
|
||||
return 0;
|
||||
}
|
49
src/mapgen/mapgen_singlenode.h
Normal file
49
src/mapgen/mapgen_singlenode.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2015-2017 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mapgen.h"
|
||||
|
||||
struct MapgenSinglenodeParams : public MapgenParams
|
||||
{
|
||||
MapgenSinglenodeParams() = default;
|
||||
~MapgenSinglenodeParams() = default;
|
||||
|
||||
void readParams(const Settings *settings) {}
|
||||
void writeParams(Settings *settings) const {}
|
||||
};
|
||||
|
||||
class MapgenSinglenode : public Mapgen
|
||||
{
|
||||
public:
|
||||
u32 flags;
|
||||
content_t c_node;
|
||||
u8 set_light;
|
||||
|
||||
MapgenSinglenode(int mapgenid, MapgenParams *params, EmergeManager *emerge);
|
||||
~MapgenSinglenode() = default;
|
||||
|
||||
virtual MapgenType getType() const { return MAPGEN_SINGLENODE; }
|
||||
|
||||
void makeChunk(BlockMakeData *data);
|
||||
int getSpawnLevelAtPoint(v2s16 p);
|
||||
};
|
295
src/mapgen/mapgen_v5.cpp
Normal file
295
src/mapgen/mapgen_v5.cpp
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2017 paramat
|
||||
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
|
||||
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 "mapgen.h"
|
||||
#include "voxel.h"
|
||||
#include "noise.h"
|
||||
#include "mapblock.h"
|
||||
#include "mapnode.h"
|
||||
#include "map.h"
|
||||
#include "content_sao.h"
|
||||
#include "nodedef.h"
|
||||
#include "voxelalgorithms.h"
|
||||
//#include "profiler.h" // For TimeTaker
|
||||
#include "settings.h" // For g_settings
|
||||
#include "emerge.h"
|
||||
#include "dungeongen.h"
|
||||
#include "cavegen.h"
|
||||
#include "mg_biome.h"
|
||||
#include "mg_ore.h"
|
||||
#include "mg_decoration.h"
|
||||
#include "mapgen_v5.h"
|
||||
|
||||
|
||||
FlagDesc flagdesc_mapgen_v5[] = {
|
||||
{"caverns", MGV5_CAVERNS},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
|
||||
MapgenV5::MapgenV5(int mapgenid, MapgenV5Params *params, EmergeManager *emerge)
|
||||
: MapgenBasic(mapgenid, params, emerge)
|
||||
{
|
||||
spflags = params->spflags;
|
||||
cave_width = params->cave_width;
|
||||
large_cave_depth = params->large_cave_depth;
|
||||
lava_depth = params->lava_depth;
|
||||
cavern_limit = params->cavern_limit;
|
||||
cavern_taper = params->cavern_taper;
|
||||
cavern_threshold = params->cavern_threshold;
|
||||
|
||||
// Terrain noise
|
||||
noise_filler_depth = new Noise(¶ms->np_filler_depth, seed, csize.X, csize.Z);
|
||||
noise_factor = new Noise(¶ms->np_factor, seed, csize.X, csize.Z);
|
||||
noise_height = new Noise(¶ms->np_height, seed, csize.X, csize.Z);
|
||||
|
||||
// 3D terrain noise
|
||||
// 1-up 1-down overgeneration
|
||||
noise_ground = new Noise(¶ms->np_ground, seed, csize.X, csize.Y + 2, csize.Z);
|
||||
// 1 down overgeneration
|
||||
MapgenBasic::np_cave1 = params->np_cave1;
|
||||
MapgenBasic::np_cave2 = params->np_cave2;
|
||||
MapgenBasic::np_cavern = params->np_cavern;
|
||||
}
|
||||
|
||||
|
||||
MapgenV5::~MapgenV5()
|
||||
{
|
||||
delete noise_filler_depth;
|
||||
delete noise_factor;
|
||||
delete noise_height;
|
||||
delete noise_ground;
|
||||
}
|
||||
|
||||
|
||||
MapgenV5Params::MapgenV5Params():
|
||||
np_filler_depth (0, 1, v3f(150, 150, 150), 261, 4, 0.7, 2.0),
|
||||
np_factor (0, 1, v3f(250, 250, 250), 920381, 3, 0.45, 2.0),
|
||||
np_height (0, 10, v3f(250, 250, 250), 84174, 4, 0.5, 2.0),
|
||||
np_ground (0, 40, v3f(80, 80, 80), 983240, 4, 0.55, 2.0, NOISE_FLAG_EASED),
|
||||
np_cave1 (0, 12, v3f(50, 50, 50), 52534, 4, 0.5, 2.0),
|
||||
np_cave2 (0, 12, v3f(50, 50, 50), 10325, 4, 0.5, 2.0),
|
||||
np_cavern (0, 1, v3f(384, 128, 384), 723, 5, 0.63, 2.0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void MapgenV5Params::readParams(const Settings *settings)
|
||||
{
|
||||
settings->getFlagStrNoEx("mgv5_spflags", spflags, flagdesc_mapgen_v5);
|
||||
settings->getFloatNoEx("mgv5_cave_width", cave_width);
|
||||
settings->getS16NoEx("mgv5_large_cave_depth", large_cave_depth);
|
||||
settings->getS16NoEx("mgv5_lava_depth", lava_depth);
|
||||
settings->getS16NoEx("mgv5_cavern_limit", cavern_limit);
|
||||
settings->getS16NoEx("mgv5_cavern_taper", cavern_taper);
|
||||
settings->getFloatNoEx("mgv5_cavern_threshold", cavern_threshold);
|
||||
|
||||
settings->getNoiseParams("mgv5_np_filler_depth", np_filler_depth);
|
||||
settings->getNoiseParams("mgv5_np_factor", np_factor);
|
||||
settings->getNoiseParams("mgv5_np_height", np_height);
|
||||
settings->getNoiseParams("mgv5_np_ground", np_ground);
|
||||
settings->getNoiseParams("mgv5_np_cave1", np_cave1);
|
||||
settings->getNoiseParams("mgv5_np_cave2", np_cave2);
|
||||
settings->getNoiseParams("mgv5_np_cavern", np_cavern);
|
||||
}
|
||||
|
||||
|
||||
void MapgenV5Params::writeParams(Settings *settings) const
|
||||
{
|
||||
settings->setFlagStr("mgv5_spflags", spflags, flagdesc_mapgen_v5, U32_MAX);
|
||||
settings->setFloat("mgv5_cave_width", cave_width);
|
||||
settings->setS16("mgv5_large_cave_depth", large_cave_depth);
|
||||
settings->setS16("mgv5_lava_depth", lava_depth);
|
||||
settings->setS16("mgv5_cavern_limit", cavern_limit);
|
||||
settings->setS16("mgv5_cavern_taper", cavern_taper);
|
||||
settings->setFloat("mgv5_cavern_threshold", cavern_threshold);
|
||||
|
||||
settings->setNoiseParams("mgv5_np_filler_depth", np_filler_depth);
|
||||
settings->setNoiseParams("mgv5_np_factor", np_factor);
|
||||
settings->setNoiseParams("mgv5_np_height", np_height);
|
||||
settings->setNoiseParams("mgv5_np_ground", np_ground);
|
||||
settings->setNoiseParams("mgv5_np_cave1", np_cave1);
|
||||
settings->setNoiseParams("mgv5_np_cave2", np_cave2);
|
||||
settings->setNoiseParams("mgv5_np_cavern", np_cavern);
|
||||
}
|
||||
|
||||
|
||||
int MapgenV5::getSpawnLevelAtPoint(v2s16 p)
|
||||
{
|
||||
|
||||
float f = 0.55 + NoisePerlin2D(&noise_factor->np, p.X, p.Y, seed);
|
||||
if (f < 0.01)
|
||||
f = 0.01;
|
||||
else if (f >= 1.0)
|
||||
f *= 1.6;
|
||||
float h = NoisePerlin2D(&noise_height->np, p.X, p.Y, seed);
|
||||
|
||||
// noise_height 'offset' is the average level of terrain. At least 50% of
|
||||
// terrain will be below this.
|
||||
// Raising the maximum spawn level above 'water_level + 16' is necessary
|
||||
// for when noise_height 'offset' is set much higher than water_level.
|
||||
s16 max_spawn_y = MYMAX(noise_height->np.offset, water_level + 16);
|
||||
|
||||
// Starting spawn search at max_spawn_y + 128 ensures 128 nodes of open
|
||||
// space above spawn position. Avoids spawning in possibly sealed voids.
|
||||
for (s16 y = max_spawn_y + 128; y >= water_level; y--) {
|
||||
float n_ground = NoisePerlin3D(&noise_ground->np, p.X, y, p.Y, seed);
|
||||
|
||||
if (n_ground * f > y - h) { // If solid
|
||||
if (y < water_level || y > max_spawn_y)
|
||||
return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
|
||||
|
||||
// y + 2 because y is surface and due to biome 'dust' nodes.
|
||||
return y + 2;
|
||||
}
|
||||
}
|
||||
// Unsuitable spawn position, no ground found
|
||||
return MAX_MAP_GENERATION_LIMIT;
|
||||
}
|
||||
|
||||
|
||||
void MapgenV5::makeChunk(BlockMakeData *data)
|
||||
{
|
||||
// Pre-conditions
|
||||
assert(data->vmanip);
|
||||
assert(data->nodedef);
|
||||
assert(data->blockpos_requested.X >= data->blockpos_min.X &&
|
||||
data->blockpos_requested.Y >= data->blockpos_min.Y &&
|
||||
data->blockpos_requested.Z >= data->blockpos_min.Z);
|
||||
assert(data->blockpos_requested.X <= data->blockpos_max.X &&
|
||||
data->blockpos_requested.Y <= data->blockpos_max.Y &&
|
||||
data->blockpos_requested.Z <= data->blockpos_max.Z);
|
||||
|
||||
this->generating = true;
|
||||
this->vm = data->vmanip;
|
||||
this->ndef = data->nodedef;
|
||||
//TimeTaker t("makeChunk");
|
||||
|
||||
v3s16 blockpos_min = data->blockpos_min;
|
||||
v3s16 blockpos_max = data->blockpos_max;
|
||||
node_min = blockpos_min * MAP_BLOCKSIZE;
|
||||
node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
|
||||
full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
|
||||
// Create a block-specific seed
|
||||
blockseed = getBlockSeed2(full_node_min, seed);
|
||||
|
||||
// Generate base terrain
|
||||
s16 stone_surface_max_y = generateBaseTerrain();
|
||||
|
||||
// Create heightmap
|
||||
updateHeightmap(node_min, node_max);
|
||||
|
||||
// Init biome generator, place biome-specific nodes, and build biomemap
|
||||
biomegen->calcBiomeNoise(node_min);
|
||||
|
||||
MgStoneType mgstone_type;
|
||||
content_t biome_stone;
|
||||
generateBiomes(&mgstone_type, &biome_stone);
|
||||
|
||||
// Generate caverns, tunnels and classic caves
|
||||
if (flags & MG_CAVES) {
|
||||
bool near_cavern = false;
|
||||
// Generate caverns
|
||||
if (spflags & MGV5_CAVERNS)
|
||||
near_cavern = generateCaverns(stone_surface_max_y);
|
||||
// Generate tunnels and classic caves
|
||||
if (near_cavern)
|
||||
// Disable classic caves in this mapchunk by setting
|
||||
// 'large cave depth' to world base. Avoids excessive liquid in
|
||||
// large caverns and floating blobs of overgenerated liquid.
|
||||
generateCaves(stone_surface_max_y, -MAX_MAP_GENERATION_LIMIT);
|
||||
else
|
||||
generateCaves(stone_surface_max_y, large_cave_depth);
|
||||
}
|
||||
|
||||
// Generate dungeons and desert temples
|
||||
if (flags & MG_DUNGEONS)
|
||||
generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
|
||||
|
||||
// Generate the registered decorations
|
||||
if (flags & MG_DECORATIONS)
|
||||
m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
|
||||
|
||||
// Generate the registered ores
|
||||
m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
|
||||
|
||||
// Sprinkle some dust on top after everything else was generated
|
||||
dustTopNodes();
|
||||
|
||||
//printf("makeChunk: %dms\n", t.stop());
|
||||
|
||||
// Add top and bottom side of water to transforming_liquid queue
|
||||
updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
|
||||
|
||||
// Calculate lighting
|
||||
if (flags & MG_LIGHT) {
|
||||
calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
|
||||
full_node_min, full_node_max);
|
||||
}
|
||||
|
||||
this->generating = false;
|
||||
}
|
||||
|
||||
|
||||
int MapgenV5::generateBaseTerrain()
|
||||
{
|
||||
u32 index = 0;
|
||||
u32 index2d = 0;
|
||||
int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
|
||||
|
||||
noise_factor->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_height->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_ground->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
|
||||
|
||||
for (s16 z=node_min.Z; z<=node_max.Z; z++) {
|
||||
for (s16 y=node_min.Y - 1; y<=node_max.Y + 1; y++) {
|
||||
u32 vi = vm->m_area.index(node_min.X, y, z);
|
||||
for (s16 x=node_min.X; x<=node_max.X; x++, vi++, index++, index2d++) {
|
||||
if (vm->m_data[vi].getContent() != CONTENT_IGNORE)
|
||||
continue;
|
||||
|
||||
float f = 0.55 + noise_factor->result[index2d];
|
||||
if (f < 0.01)
|
||||
f = 0.01;
|
||||
else if (f >= 1.0)
|
||||
f *= 1.6;
|
||||
float h = noise_height->result[index2d];
|
||||
|
||||
if (noise_ground->result[index] * f < y - h) {
|
||||
if (y <= water_level)
|
||||
vm->m_data[vi] = MapNode(c_water_source);
|
||||
else
|
||||
vm->m_data[vi] = MapNode(CONTENT_AIR);
|
||||
} else {
|
||||
vm->m_data[vi] = MapNode(c_stone);
|
||||
if (y > stone_surface_max_y)
|
||||
stone_surface_max_y = y;
|
||||
}
|
||||
}
|
||||
index2d -= ystride;
|
||||
}
|
||||
index2d += ystride;
|
||||
}
|
||||
|
||||
return stone_surface_max_y;
|
||||
}
|
74
src/mapgen/mapgen_v5.h
Normal file
74
src/mapgen/mapgen_v5.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2017 paramat
|
||||
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mapgen.h"
|
||||
|
||||
///////// Mapgen V5 flags
|
||||
#define MGV5_CAVERNS 0x01
|
||||
|
||||
class BiomeManager;
|
||||
|
||||
extern FlagDesc flagdesc_mapgen_v5[];
|
||||
|
||||
struct MapgenV5Params : public MapgenParams
|
||||
{
|
||||
u32 spflags = MGV5_CAVERNS;
|
||||
float cave_width = 0.125f;
|
||||
s16 large_cave_depth = -256;
|
||||
s16 lava_depth = -256;
|
||||
s16 cavern_limit = -256;
|
||||
s16 cavern_taper = 256;
|
||||
float cavern_threshold = 0.7f;
|
||||
|
||||
NoiseParams np_filler_depth;
|
||||
NoiseParams np_factor;
|
||||
NoiseParams np_height;
|
||||
NoiseParams np_ground;
|
||||
NoiseParams np_cave1;
|
||||
NoiseParams np_cave2;
|
||||
NoiseParams np_cavern;
|
||||
|
||||
MapgenV5Params();
|
||||
~MapgenV5Params() = default;
|
||||
|
||||
void readParams(const Settings *settings);
|
||||
void writeParams(Settings *settings) const;
|
||||
};
|
||||
|
||||
class MapgenV5 : public MapgenBasic
|
||||
{
|
||||
public:
|
||||
MapgenV5(int mapgenid, MapgenV5Params *params, EmergeManager *emerge);
|
||||
~MapgenV5();
|
||||
|
||||
virtual MapgenType getType() const { return MAPGEN_V5; }
|
||||
|
||||
virtual void makeChunk(BlockMakeData *data);
|
||||
int getSpawnLevelAtPoint(v2s16 p);
|
||||
int generateBaseTerrain();
|
||||
|
||||
private:
|
||||
s16 large_cave_depth;
|
||||
Noise *noise_factor;
|
||||
Noise *noise_height;
|
||||
Noise *noise_ground;
|
||||
};
|
1123
src/mapgen/mapgen_v6.cpp
Normal file
1123
src/mapgen/mapgen_v6.cpp
Normal file
File diff suppressed because it is too large
Load diff
169
src/mapgen/mapgen_v6.h
Normal file
169
src/mapgen/mapgen_v6.h
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2014-2017 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mapgen.h"
|
||||
#include "noise.h"
|
||||
|
||||
#define MGV6_AVERAGE_MUD_AMOUNT 4
|
||||
#define MGV6_DESERT_STONE_BASE -32
|
||||
#define MGV6_ICE_BASE 0
|
||||
#define MGV6_FREQ_HOT 0.4
|
||||
#define MGV6_FREQ_SNOW -0.4
|
||||
#define MGV6_FREQ_TAIGA 0.5
|
||||
#define MGV6_FREQ_JUNGLE 0.5
|
||||
|
||||
//////////// Mapgen V6 flags
|
||||
#define MGV6_JUNGLES 0x01
|
||||
#define MGV6_BIOMEBLEND 0x02
|
||||
#define MGV6_MUDFLOW 0x04
|
||||
#define MGV6_SNOWBIOMES 0x08
|
||||
#define MGV6_FLAT 0x10
|
||||
#define MGV6_TREES 0x20
|
||||
|
||||
|
||||
extern FlagDesc flagdesc_mapgen_v6[];
|
||||
|
||||
|
||||
enum BiomeV6Type
|
||||
{
|
||||
BT_NORMAL,
|
||||
BT_DESERT,
|
||||
BT_JUNGLE,
|
||||
BT_TUNDRA,
|
||||
BT_TAIGA,
|
||||
};
|
||||
|
||||
|
||||
struct MapgenV6Params : public MapgenParams {
|
||||
u32 spflags = MGV6_JUNGLES | MGV6_SNOWBIOMES | MGV6_TREES |
|
||||
MGV6_BIOMEBLEND | MGV6_MUDFLOW;
|
||||
float freq_desert = 0.45f;
|
||||
float freq_beach = 0.15f;
|
||||
NoiseParams np_terrain_base;
|
||||
NoiseParams np_terrain_higher;
|
||||
NoiseParams np_steepness;
|
||||
NoiseParams np_height_select;
|
||||
NoiseParams np_mud;
|
||||
NoiseParams np_beach;
|
||||
NoiseParams np_biome;
|
||||
NoiseParams np_cave;
|
||||
NoiseParams np_humidity;
|
||||
NoiseParams np_trees;
|
||||
NoiseParams np_apple_trees;
|
||||
|
||||
MapgenV6Params();
|
||||
~MapgenV6Params() = default;
|
||||
|
||||
void readParams(const Settings *settings);
|
||||
void writeParams(Settings *settings) const;
|
||||
};
|
||||
|
||||
|
||||
class MapgenV6 : public Mapgen {
|
||||
public:
|
||||
EmergeManager *m_emerge;
|
||||
|
||||
int ystride;
|
||||
u32 spflags;
|
||||
|
||||
v3s16 node_min;
|
||||
v3s16 node_max;
|
||||
v3s16 full_node_min;
|
||||
v3s16 full_node_max;
|
||||
v3s16 central_area_size;
|
||||
|
||||
Noise *noise_terrain_base;
|
||||
Noise *noise_terrain_higher;
|
||||
Noise *noise_steepness;
|
||||
Noise *noise_height_select;
|
||||
Noise *noise_mud;
|
||||
Noise *noise_beach;
|
||||
Noise *noise_biome;
|
||||
Noise *noise_humidity;
|
||||
NoiseParams *np_cave;
|
||||
NoiseParams *np_humidity;
|
||||
NoiseParams *np_trees;
|
||||
NoiseParams *np_apple_trees;
|
||||
float freq_desert;
|
||||
float freq_beach;
|
||||
|
||||
content_t c_stone;
|
||||
content_t c_dirt;
|
||||
content_t c_dirt_with_grass;
|
||||
content_t c_sand;
|
||||
content_t c_water_source;
|
||||
content_t c_lava_source;
|
||||
content_t c_gravel;
|
||||
content_t c_desert_stone;
|
||||
content_t c_desert_sand;
|
||||
content_t c_dirt_with_snow;
|
||||
content_t c_snow;
|
||||
content_t c_snowblock;
|
||||
content_t c_ice;
|
||||
|
||||
content_t c_cobble;
|
||||
content_t c_mossycobble;
|
||||
content_t c_stair_cobble;
|
||||
content_t c_stair_desert_stone;
|
||||
|
||||
MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge);
|
||||
~MapgenV6();
|
||||
|
||||
virtual MapgenType getType() const { return MAPGEN_V6; }
|
||||
|
||||
void makeChunk(BlockMakeData *data);
|
||||
int getGroundLevelAtPoint(v2s16 p);
|
||||
int getSpawnLevelAtPoint(v2s16 p);
|
||||
|
||||
float baseTerrainLevel(float terrain_base, float terrain_higher,
|
||||
float steepness, float height_select);
|
||||
virtual float baseTerrainLevelFromNoise(v2s16 p);
|
||||
virtual float baseTerrainLevelFromMap(v2s16 p);
|
||||
virtual float baseTerrainLevelFromMap(int index);
|
||||
|
||||
s16 find_stone_level(v2s16 p2d);
|
||||
bool block_is_underground(u64 seed, v3s16 blockpos);
|
||||
s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision);
|
||||
|
||||
float getHumidity(v2s16 p);
|
||||
float getTreeAmount(v2s16 p);
|
||||
bool getHaveAppleTree(v2s16 p);
|
||||
float getMudAmount(v2s16 p);
|
||||
virtual float getMudAmount(int index);
|
||||
bool getHaveBeach(v2s16 p);
|
||||
bool getHaveBeach(int index);
|
||||
BiomeV6Type getBiome(v2s16 p);
|
||||
BiomeV6Type getBiome(int index, v2s16 p);
|
||||
|
||||
u32 get_blockseed(u64 seed, v3s16 p);
|
||||
|
||||
virtual void calculateNoise();
|
||||
int generateGround();
|
||||
void addMud();
|
||||
void flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos);
|
||||
void moveMud(u32 remove_index, u32 place_index,
|
||||
u32 above_remove_index, v2s16 pos, v3s16 em);
|
||||
void growGrass();
|
||||
void placeTreesAndJungleGrass();
|
||||
virtual void generateCaves(int max_stone_y);
|
||||
};
|
743
src/mapgen/mapgen_v7.cpp
Normal file
743
src/mapgen/mapgen_v7.cpp
Normal file
|
@ -0,0 +1,743 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2014-2017 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 "mapgen.h"
|
||||
#include "voxel.h"
|
||||
#include "noise.h"
|
||||
#include "mapblock.h"
|
||||
#include "mapnode.h"
|
||||
#include "map.h"
|
||||
#include "content_sao.h"
|
||||
#include "nodedef.h"
|
||||
#include "voxelalgorithms.h"
|
||||
//#include "profiler.h" // For TimeTaker
|
||||
#include "settings.h" // For g_settings
|
||||
#include "emerge.h"
|
||||
#include "dungeongen.h"
|
||||
#include "cavegen.h"
|
||||
#include "mg_biome.h"
|
||||
#include "mg_ore.h"
|
||||
#include "mg_decoration.h"
|
||||
#include "mapgen_v7.h"
|
||||
|
||||
|
||||
FlagDesc flagdesc_mapgen_v7[] = {
|
||||
{"mountains", MGV7_MOUNTAINS},
|
||||
{"ridges", MGV7_RIDGES},
|
||||
{"floatlands", MGV7_FLOATLANDS},
|
||||
{"caverns", MGV7_CAVERNS},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge)
|
||||
: MapgenBasic(mapgenid, params, emerge)
|
||||
{
|
||||
spflags = params->spflags;
|
||||
mount_zero_level = params->mount_zero_level;
|
||||
cave_width = params->cave_width;
|
||||
large_cave_depth = params->large_cave_depth;
|
||||
lava_depth = params->lava_depth;
|
||||
float_mount_density = params->float_mount_density;
|
||||
floatland_level = params->floatland_level;
|
||||
shadow_limit = params->shadow_limit;
|
||||
cavern_limit = params->cavern_limit;
|
||||
cavern_taper = params->cavern_taper;
|
||||
cavern_threshold = params->cavern_threshold;
|
||||
|
||||
// This is to avoid a divide-by-zero.
|
||||
// Parameter will be saved to map_meta.txt in limited form.
|
||||
params->float_mount_height = MYMAX(params->float_mount_height, 1.0f);
|
||||
float_mount_height = params->float_mount_height;
|
||||
|
||||
// 2D noise
|
||||
noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Z);
|
||||
noise_terrain_alt = new Noise(¶ms->np_terrain_alt, seed, csize.X, csize.Z);
|
||||
noise_terrain_persist = new Noise(¶ms->np_terrain_persist, seed, csize.X, csize.Z);
|
||||
noise_height_select = new Noise(¶ms->np_height_select, seed, csize.X, csize.Z);
|
||||
noise_filler_depth = new Noise(¶ms->np_filler_depth, seed, csize.X, csize.Z);
|
||||
|
||||
if (spflags & MGV7_MOUNTAINS)
|
||||
noise_mount_height = new Noise(¶ms->np_mount_height, seed, csize.X, csize.Z);
|
||||
|
||||
if (spflags & MGV7_FLOATLANDS) {
|
||||
noise_floatland_base = new Noise(¶ms->np_floatland_base, seed, csize.X, csize.Z);
|
||||
noise_float_base_height = new Noise(¶ms->np_float_base_height, seed, csize.X, csize.Z);
|
||||
}
|
||||
|
||||
if (spflags & MGV7_RIDGES) {
|
||||
noise_ridge_uwater = new Noise(¶ms->np_ridge_uwater, seed, csize.X, csize.Z);
|
||||
// 3D noise, 1-up 1-down overgeneration
|
||||
noise_ridge = new Noise(¶ms->np_ridge, seed, csize.X, csize.Y + 2, csize.Z);
|
||||
}
|
||||
// 3D noise, 1 up, 1 down overgeneration
|
||||
if ((spflags & MGV7_MOUNTAINS) || (spflags & MGV7_FLOATLANDS))
|
||||
noise_mountain = new Noise(¶ms->np_mountain, seed, csize.X, csize.Y + 2, csize.Z);
|
||||
// 3D noise, 1 down overgeneration
|
||||
MapgenBasic::np_cave1 = params->np_cave1;
|
||||
MapgenBasic::np_cave2 = params->np_cave2;
|
||||
MapgenBasic::np_cavern = params->np_cavern;
|
||||
}
|
||||
|
||||
|
||||
MapgenV7::~MapgenV7()
|
||||
{
|
||||
delete noise_terrain_base;
|
||||
delete noise_terrain_alt;
|
||||
delete noise_terrain_persist;
|
||||
delete noise_height_select;
|
||||
delete noise_filler_depth;
|
||||
|
||||
if (spflags & MGV7_MOUNTAINS)
|
||||
delete noise_mount_height;
|
||||
|
||||
if (spflags & MGV7_FLOATLANDS) {
|
||||
delete noise_floatland_base;
|
||||
delete noise_float_base_height;
|
||||
}
|
||||
|
||||
if (spflags & MGV7_RIDGES) {
|
||||
delete noise_ridge_uwater;
|
||||
delete noise_ridge;
|
||||
}
|
||||
|
||||
if ((spflags & MGV7_MOUNTAINS) || (spflags & MGV7_FLOATLANDS))
|
||||
delete noise_mountain;
|
||||
}
|
||||
|
||||
|
||||
MapgenV7Params::MapgenV7Params():
|
||||
np_terrain_base (4, 70, v3f(600, 600, 600), 82341, 5, 0.6, 2.0),
|
||||
np_terrain_alt (4, 25, v3f(600, 600, 600), 5934, 5, 0.6, 2.0),
|
||||
np_terrain_persist (0.6, 0.1, v3f(2000, 2000, 2000), 539, 3, 0.6, 2.0),
|
||||
np_height_select (-8, 16, v3f(500, 500, 500), 4213, 6, 0.7, 2.0),
|
||||
np_filler_depth (0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0),
|
||||
np_mount_height (256, 112, v3f(1000, 1000, 1000), 72449, 3, 0.6, 2.0),
|
||||
np_ridge_uwater (0, 1, v3f(1000, 1000, 1000), 85039, 5, 0.6, 2.0),
|
||||
np_floatland_base (-0.6, 1.5, v3f(600, 600, 600), 114, 5, 0.6, 2.0),
|
||||
np_float_base_height (48, 24, v3f(300, 300, 300), 907, 4, 0.7, 2.0),
|
||||
np_mountain (-0.6, 1, v3f(250, 350, 250), 5333, 5, 0.63, 2.0),
|
||||
np_ridge (0, 1, v3f(100, 100, 100), 6467, 4, 0.75, 2.0),
|
||||
np_cavern (0, 1, v3f(384, 128, 384), 723, 5, 0.63, 2.0),
|
||||
np_cave1 (0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0),
|
||||
np_cave2 (0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void MapgenV7Params::readParams(const Settings *settings)
|
||||
{
|
||||
settings->getFlagStrNoEx("mgv7_spflags", spflags, flagdesc_mapgen_v7);
|
||||
settings->getS16NoEx("mgv7_mount_zero_level", mount_zero_level);
|
||||
settings->getFloatNoEx("mgv7_cave_width", cave_width);
|
||||
settings->getS16NoEx("mgv7_large_cave_depth", large_cave_depth);
|
||||
settings->getS16NoEx("mgv7_lava_depth", lava_depth);
|
||||
settings->getFloatNoEx("mgv7_float_mount_density", float_mount_density);
|
||||
settings->getFloatNoEx("mgv7_float_mount_height", float_mount_height);
|
||||
settings->getS16NoEx("mgv7_floatland_level", floatland_level);
|
||||
settings->getS16NoEx("mgv7_shadow_limit", shadow_limit);
|
||||
settings->getS16NoEx("mgv7_cavern_limit", cavern_limit);
|
||||
settings->getS16NoEx("mgv7_cavern_taper", cavern_taper);
|
||||
settings->getFloatNoEx("mgv7_cavern_threshold", cavern_threshold);
|
||||
|
||||
settings->getNoiseParams("mgv7_np_terrain_base", np_terrain_base);
|
||||
settings->getNoiseParams("mgv7_np_terrain_alt", np_terrain_alt);
|
||||
settings->getNoiseParams("mgv7_np_terrain_persist", np_terrain_persist);
|
||||
settings->getNoiseParams("mgv7_np_height_select", np_height_select);
|
||||
settings->getNoiseParams("mgv7_np_filler_depth", np_filler_depth);
|
||||
settings->getNoiseParams("mgv7_np_mount_height", np_mount_height);
|
||||
settings->getNoiseParams("mgv7_np_ridge_uwater", np_ridge_uwater);
|
||||
settings->getNoiseParams("mgv7_np_floatland_base", np_floatland_base);
|
||||
settings->getNoiseParams("mgv7_np_float_base_height", np_float_base_height);
|
||||
settings->getNoiseParams("mgv7_np_mountain", np_mountain);
|
||||
settings->getNoiseParams("mgv7_np_ridge", np_ridge);
|
||||
settings->getNoiseParams("mgv7_np_cavern", np_cavern);
|
||||
settings->getNoiseParams("mgv7_np_cave1", np_cave1);
|
||||
settings->getNoiseParams("mgv7_np_cave2", np_cave2);
|
||||
}
|
||||
|
||||
|
||||
void MapgenV7Params::writeParams(Settings *settings) const
|
||||
{
|
||||
settings->setFlagStr("mgv7_spflags", spflags, flagdesc_mapgen_v7, U32_MAX);
|
||||
settings->setS16("mgv7_mount_zero_level", mount_zero_level);
|
||||
settings->setFloat("mgv7_cave_width", cave_width);
|
||||
settings->setS16("mgv7_large_cave_depth", large_cave_depth);
|
||||
settings->setS16("mgv7_lava_depth", lava_depth);
|
||||
settings->setFloat("mgv7_float_mount_density", float_mount_density);
|
||||
settings->setFloat("mgv7_float_mount_height", float_mount_height);
|
||||
settings->setS16("mgv7_floatland_level", floatland_level);
|
||||
settings->setS16("mgv7_shadow_limit", shadow_limit);
|
||||
settings->setS16("mgv7_cavern_limit", cavern_limit);
|
||||
settings->setS16("mgv7_cavern_taper", cavern_taper);
|
||||
settings->setFloat("mgv7_cavern_threshold", cavern_threshold);
|
||||
|
||||
settings->setNoiseParams("mgv7_np_terrain_base", np_terrain_base);
|
||||
settings->setNoiseParams("mgv7_np_terrain_alt", np_terrain_alt);
|
||||
settings->setNoiseParams("mgv7_np_terrain_persist", np_terrain_persist);
|
||||
settings->setNoiseParams("mgv7_np_height_select", np_height_select);
|
||||
settings->setNoiseParams("mgv7_np_filler_depth", np_filler_depth);
|
||||
settings->setNoiseParams("mgv7_np_mount_height", np_mount_height);
|
||||
settings->setNoiseParams("mgv7_np_ridge_uwater", np_ridge_uwater);
|
||||
settings->setNoiseParams("mgv7_np_floatland_base", np_floatland_base);
|
||||
settings->setNoiseParams("mgv7_np_float_base_height", np_float_base_height);
|
||||
settings->setNoiseParams("mgv7_np_mountain", np_mountain);
|
||||
settings->setNoiseParams("mgv7_np_ridge", np_ridge);
|
||||
settings->setNoiseParams("mgv7_np_cavern", np_cavern);
|
||||
settings->setNoiseParams("mgv7_np_cave1", np_cave1);
|
||||
settings->setNoiseParams("mgv7_np_cave2", np_cave2);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int MapgenV7::getSpawnLevelAtPoint(v2s16 p)
|
||||
{
|
||||
// If rivers are enabled, first check if in a river
|
||||
if (spflags & MGV7_RIDGES) {
|
||||
float width = 0.2;
|
||||
float uwatern = NoisePerlin2D(&noise_ridge_uwater->np, p.X, p.Y, seed) * 2;
|
||||
if (fabs(uwatern) <= width)
|
||||
return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
|
||||
}
|
||||
|
||||
// Terrain noise 'offset' is the average level of that terrain.
|
||||
// At least 50% of terrain will be below the higher of base and alt terrain
|
||||
// 'offset's.
|
||||
// Raising the maximum spawn level above 'water_level + 16' is necessary
|
||||
// for when terrain 'offset's are set much higher than water_level.
|
||||
s16 max_spawn_y = MYMAX(MYMAX(noise_terrain_alt->np.offset,
|
||||
noise_terrain_base->np.offset),
|
||||
water_level + 16);
|
||||
// Base terrain calculation
|
||||
s16 y = baseTerrainLevelAtPoint(p.X, p.Y);
|
||||
|
||||
// If mountains are disabled, terrain level is base terrain level.
|
||||
// Avoids mid-air spawn where mountain terrain would have been.
|
||||
if (!(spflags & MGV7_MOUNTAINS)) {
|
||||
if (y < water_level || y > max_spawn_y)
|
||||
return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
|
||||
|
||||
// y + 2 because y is surface level and due to biome 'dust'
|
||||
return y + 2;
|
||||
}
|
||||
|
||||
// Search upwards for first node without mountain terrain
|
||||
int iters = 256;
|
||||
while (iters > 0 && y <= max_spawn_y) {
|
||||
if (!getMountainTerrainAtPoint(p.X, y + 1, p.Y)) {
|
||||
if (y <= water_level || y > max_spawn_y)
|
||||
return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
|
||||
|
||||
// y + 1 due to biome 'dust'
|
||||
return y + 1;
|
||||
}
|
||||
y++;
|
||||
iters--;
|
||||
}
|
||||
|
||||
// Unsuitable spawn point
|
||||
return MAX_MAP_GENERATION_LIMIT;
|
||||
}
|
||||
|
||||
|
||||
void MapgenV7::makeChunk(BlockMakeData *data)
|
||||
{
|
||||
// Pre-conditions
|
||||
assert(data->vmanip);
|
||||
assert(data->nodedef);
|
||||
assert(data->blockpos_requested.X >= data->blockpos_min.X &&
|
||||
data->blockpos_requested.Y >= data->blockpos_min.Y &&
|
||||
data->blockpos_requested.Z >= data->blockpos_min.Z);
|
||||
assert(data->blockpos_requested.X <= data->blockpos_max.X &&
|
||||
data->blockpos_requested.Y <= data->blockpos_max.Y &&
|
||||
data->blockpos_requested.Z <= data->blockpos_max.Z);
|
||||
|
||||
this->generating = true;
|
||||
this->vm = data->vmanip;
|
||||
this->ndef = data->nodedef;
|
||||
//TimeTaker t("makeChunk");
|
||||
|
||||
v3s16 blockpos_min = data->blockpos_min;
|
||||
v3s16 blockpos_max = data->blockpos_max;
|
||||
node_min = blockpos_min * MAP_BLOCKSIZE;
|
||||
node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
|
||||
full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
|
||||
blockseed = getBlockSeed2(full_node_min, seed);
|
||||
|
||||
// Generate base and mountain terrain
|
||||
// An initial heightmap is no longer created here for use in generateRidgeTerrain()
|
||||
s16 stone_surface_max_y = generateTerrain();
|
||||
|
||||
// Generate rivers
|
||||
if (spflags & MGV7_RIDGES)
|
||||
generateRidgeTerrain();
|
||||
|
||||
// Create heightmap
|
||||
updateHeightmap(node_min, node_max);
|
||||
|
||||
// Init biome generator, place biome-specific nodes, and build biomemap
|
||||
biomegen->calcBiomeNoise(node_min);
|
||||
|
||||
MgStoneType mgstone_type;
|
||||
content_t biome_stone;
|
||||
generateBiomes(&mgstone_type, &biome_stone);
|
||||
|
||||
// Generate caverns, tunnels and classic caves
|
||||
if (flags & MG_CAVES) {
|
||||
bool near_cavern = false;
|
||||
// Generate caverns
|
||||
if (spflags & MGV7_CAVERNS)
|
||||
near_cavern = generateCaverns(stone_surface_max_y);
|
||||
// Generate tunnels and classic caves
|
||||
if (near_cavern)
|
||||
// Disable classic caves in this mapchunk by setting
|
||||
// 'large cave depth' to world base. Avoids excessive liquid in
|
||||
// large caverns and floating blobs of overgenerated liquid.
|
||||
generateCaves(stone_surface_max_y, -MAX_MAP_GENERATION_LIMIT);
|
||||
else
|
||||
generateCaves(stone_surface_max_y, large_cave_depth);
|
||||
}
|
||||
|
||||
// Generate dungeons
|
||||
if (flags & MG_DUNGEONS)
|
||||
generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
|
||||
|
||||
// Generate the registered decorations
|
||||
if (flags & MG_DECORATIONS)
|
||||
m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
|
||||
|
||||
// Generate the registered ores
|
||||
m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
|
||||
|
||||
// Sprinkle some dust on top after everything else was generated
|
||||
dustTopNodes();
|
||||
|
||||
//printf("makeChunk: %dms\n", t.stop());
|
||||
|
||||
// Update liquids
|
||||
updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
|
||||
|
||||
// Calculate lighting
|
||||
// Limit floatland shadow
|
||||
bool propagate_shadow = !((spflags & MGV7_FLOATLANDS) &&
|
||||
node_min.Y <= shadow_limit && node_max.Y >= shadow_limit);
|
||||
|
||||
if (flags & MG_LIGHT)
|
||||
calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
|
||||
full_node_min, full_node_max, propagate_shadow);
|
||||
|
||||
//setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
|
||||
// node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
|
||||
|
||||
this->generating = false;
|
||||
}
|
||||
|
||||
|
||||
float MapgenV7::baseTerrainLevelAtPoint(s16 x, s16 z)
|
||||
{
|
||||
float hselect = NoisePerlin2D(&noise_height_select->np, x, z, seed);
|
||||
hselect = rangelim(hselect, 0.0, 1.0);
|
||||
|
||||
float persist = NoisePerlin2D(&noise_terrain_persist->np, x, z, seed);
|
||||
|
||||
noise_terrain_base->np.persist = persist;
|
||||
float height_base = NoisePerlin2D(&noise_terrain_base->np, x, z, seed);
|
||||
|
||||
noise_terrain_alt->np.persist = persist;
|
||||
float height_alt = NoisePerlin2D(&noise_terrain_alt->np, x, z, seed);
|
||||
|
||||
if (height_alt > height_base)
|
||||
return height_alt;
|
||||
|
||||
return (height_base * hselect) + (height_alt * (1.0 - hselect));
|
||||
}
|
||||
|
||||
|
||||
float MapgenV7::baseTerrainLevelFromMap(int index)
|
||||
{
|
||||
float hselect = rangelim(noise_height_select->result[index], 0.0, 1.0);
|
||||
float height_base = noise_terrain_base->result[index];
|
||||
float height_alt = noise_terrain_alt->result[index];
|
||||
|
||||
if (height_alt > height_base)
|
||||
return height_alt;
|
||||
|
||||
return (height_base * hselect) + (height_alt * (1.0 - hselect));
|
||||
}
|
||||
|
||||
|
||||
bool MapgenV7::getMountainTerrainAtPoint(s16 x, s16 y, s16 z)
|
||||
{
|
||||
float mnt_h_n =
|
||||
MYMAX(NoisePerlin2D(&noise_mount_height->np, x, z, seed), 1.0f);
|
||||
float density_gradient = -((float)(y - mount_zero_level) / mnt_h_n);
|
||||
float mnt_n = NoisePerlin3D(&noise_mountain->np, x, y, z, seed);
|
||||
|
||||
return mnt_n + density_gradient >= 0.0;
|
||||
}
|
||||
|
||||
|
||||
bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y)
|
||||
{
|
||||
float mounthn = MYMAX(noise_mount_height->result[idx_xz], 1.0f);
|
||||
float density_gradient = -((float)(y - mount_zero_level) / mounthn);
|
||||
float mountn = noise_mountain->result[idx_xyz];
|
||||
|
||||
return mountn + density_gradient >= 0.0;
|
||||
}
|
||||
|
||||
|
||||
bool MapgenV7::getFloatlandMountainFromMap(int idx_xyz, int idx_xz, s16 y)
|
||||
{
|
||||
// Make rim 2 nodes thick to match floatland base terrain
|
||||
float density_gradient = (y >= floatland_level) ?
|
||||
-pow((float)(y - floatland_level) / float_mount_height, 0.75f) :
|
||||
-pow((float)(floatland_level - 1 - y) / float_mount_height, 0.75f);
|
||||
|
||||
float floatn = noise_mountain->result[idx_xyz] + float_mount_density;
|
||||
|
||||
return floatn + density_gradient >= 0.0f;
|
||||
}
|
||||
|
||||
|
||||
void MapgenV7::floatBaseExtentFromMap(s16 *float_base_min, s16 *float_base_max, int idx_xz)
|
||||
{
|
||||
// '+1' to avoid a layer of stone at y = MAX_MAP_GENERATION_LIMIT
|
||||
s16 base_min = MAX_MAP_GENERATION_LIMIT + 1;
|
||||
s16 base_max = MAX_MAP_GENERATION_LIMIT;
|
||||
|
||||
float n_base = noise_floatland_base->result[idx_xz];
|
||||
if (n_base > 0.0f) {
|
||||
float n_base_height =
|
||||
MYMAX(noise_float_base_height->result[idx_xz], 1.0f);
|
||||
float amp = n_base * n_base_height;
|
||||
float ridge = n_base_height / 3.0f;
|
||||
base_min = floatland_level - amp / 1.5f;
|
||||
|
||||
if (amp > ridge * 2.0f) {
|
||||
// Lake bed
|
||||
base_max = floatland_level - (amp - ridge * 2.0f) / 2.0f;
|
||||
} else {
|
||||
// Hills and ridges
|
||||
float diff = fabs(amp - ridge) / ridge;
|
||||
// Smooth ridges using the 'smoothstep function'
|
||||
float smooth_diff = diff * diff * (3.0f - 2.0f * diff);
|
||||
base_max = floatland_level + ridge - smooth_diff * ridge;
|
||||
}
|
||||
}
|
||||
|
||||
*float_base_min = base_min;
|
||||
*float_base_max = base_max;
|
||||
}
|
||||
|
||||
|
||||
int MapgenV7::generateTerrain()
|
||||
{
|
||||
MapNode n_air(CONTENT_AIR);
|
||||
MapNode n_stone(c_stone);
|
||||
MapNode n_water(c_water_source);
|
||||
|
||||
//// Calculate noise for terrain generation
|
||||
noise_terrain_persist->perlinMap2D(node_min.X, node_min.Z);
|
||||
float *persistmap = noise_terrain_persist->result;
|
||||
|
||||
noise_terrain_base->perlinMap2D(node_min.X, node_min.Z, persistmap);
|
||||
noise_terrain_alt->perlinMap2D(node_min.X, node_min.Z, persistmap);
|
||||
noise_height_select->perlinMap2D(node_min.X, node_min.Z);
|
||||
|
||||
if ((spflags & MGV7_MOUNTAINS) || (spflags & MGV7_FLOATLANDS)) {
|
||||
noise_mountain->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
|
||||
}
|
||||
|
||||
if (spflags & MGV7_MOUNTAINS) {
|
||||
noise_mount_height->perlinMap2D(node_min.X, node_min.Z);
|
||||
}
|
||||
|
||||
if (spflags & MGV7_FLOATLANDS) {
|
||||
noise_floatland_base->perlinMap2D(node_min.X, node_min.Z);
|
||||
noise_float_base_height->perlinMap2D(node_min.X, node_min.Z);
|
||||
}
|
||||
|
||||
//// Place nodes
|
||||
const v3s16 &em = vm->m_area.getExtent();
|
||||
s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
|
||||
u32 index2d = 0;
|
||||
|
||||
for (s16 z = node_min.Z; z <= node_max.Z; z++)
|
||||
for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) {
|
||||
s16 surface_y = baseTerrainLevelFromMap(index2d);
|
||||
if (surface_y > stone_surface_max_y)
|
||||
stone_surface_max_y = surface_y;
|
||||
|
||||
// Get extent of floatland base terrain
|
||||
// '+1' to avoid a layer of stone at y = MAX_MAP_GENERATION_LIMIT
|
||||
s16 float_base_min = MAX_MAP_GENERATION_LIMIT + 1;
|
||||
s16 float_base_max = MAX_MAP_GENERATION_LIMIT;
|
||||
if (spflags & MGV7_FLOATLANDS)
|
||||
floatBaseExtentFromMap(&float_base_min, &float_base_max, index2d);
|
||||
|
||||
u32 vi = vm->m_area.index(x, node_min.Y - 1, z);
|
||||
u32 index3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X);
|
||||
|
||||
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
|
||||
if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
|
||||
if (y <= surface_y) {
|
||||
vm->m_data[vi] = n_stone; // Base terrain
|
||||
} else if ((spflags & MGV7_MOUNTAINS) &&
|
||||
getMountainTerrainFromMap(index3d, index2d, y)) {
|
||||
vm->m_data[vi] = n_stone; // Mountain terrain
|
||||
if (y > stone_surface_max_y)
|
||||
stone_surface_max_y = y;
|
||||
} else if ((spflags & MGV7_FLOATLANDS) &&
|
||||
((y >= float_base_min && y <= float_base_max) ||
|
||||
getFloatlandMountainFromMap(index3d, index2d, y))) {
|
||||
vm->m_data[vi] = n_stone; // Floatland terrain
|
||||
stone_surface_max_y = node_max.Y;
|
||||
} else if (y <= water_level) {
|
||||
vm->m_data[vi] = n_water; // Ground level water
|
||||
} else if ((spflags & MGV7_FLOATLANDS) &&
|
||||
(y >= float_base_max && y <= floatland_level)) {
|
||||
vm->m_data[vi] = n_water; // Floatland water
|
||||
} else {
|
||||
vm->m_data[vi] = n_air;
|
||||
}
|
||||
}
|
||||
vm->m_area.add_y(em, vi, 1);
|
||||
index3d += ystride;
|
||||
}
|
||||
}
|
||||
|
||||
return stone_surface_max_y;
|
||||
}
|
||||
|
||||
|
||||
void MapgenV7::generateRidgeTerrain()
|
||||
{
|
||||
if (node_max.Y < water_level - 16 ||
|
||||
((spflags & MGV7_FLOATLANDS) && node_max.Y > shadow_limit))
|
||||
return;
|
||||
|
||||
noise_ridge->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
|
||||
noise_ridge_uwater->perlinMap2D(node_min.X, node_min.Z);
|
||||
|
||||
MapNode n_water(c_water_source);
|
||||
MapNode n_air(CONTENT_AIR);
|
||||
u32 index = 0;
|
||||
float width = 0.2;
|
||||
|
||||
for (s16 z = node_min.Z; z <= node_max.Z; z++)
|
||||
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
|
||||
u32 vi = vm->m_area.index(node_min.X, y, z);
|
||||
for (s16 x = node_min.X; x <= node_max.X; x++, index++, vi++) {
|
||||
int j = (z - node_min.Z) * csize.X + (x - node_min.X);
|
||||
|
||||
float uwatern = noise_ridge_uwater->result[j] * 2;
|
||||
if (fabs(uwatern) > width)
|
||||
continue;
|
||||
|
||||
float altitude = y - water_level;
|
||||
float height_mod = (altitude + 17) / 2.5;
|
||||
float width_mod = width - fabs(uwatern);
|
||||
float nridge = noise_ridge->result[index] * MYMAX(altitude, 0) / 7.0;
|
||||
|
||||
if (nridge + width_mod * height_mod < 0.6)
|
||||
continue;
|
||||
|
||||
vm->m_data[vi] = (y > water_level) ? n_air : n_water;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Code Boneyard
|
||||
////
|
||||
//// Much of the stuff here has potential to become useful again at some point
|
||||
//// in the future, but we don't want it to get lost or forgotten in version
|
||||
//// control.
|
||||
////
|
||||
|
||||
#if 0
|
||||
int MapgenV7::generateMountainTerrain(s16 ymax)
|
||||
{
|
||||
MapNode n_stone(c_stone);
|
||||
u32 j = 0;
|
||||
|
||||
for (s16 z = node_min.Z; z <= node_max.Z; z++)
|
||||
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
|
||||
u32 vi = vm->m_area.index(node_min.X, y, z);
|
||||
for (s16 x = node_min.X; x <= node_max.X; x++) {
|
||||
int index = (z - node_min.Z) * csize.X + (x - node_min.X);
|
||||
content_t c = vm->m_data[vi].getContent();
|
||||
|
||||
if (getMountainTerrainFromMap(j, index, y)
|
||||
&& (c == CONTENT_AIR || c == c_water_source)) {
|
||||
vm->m_data[vi] = n_stone;
|
||||
if (y > ymax)
|
||||
ymax = y;
|
||||
}
|
||||
|
||||
vi++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
return ymax;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if 0
|
||||
void MapgenV7::carveRivers() {
|
||||
MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
|
||||
MapNode n_stone(c_stone);
|
||||
u32 index = 0;
|
||||
|
||||
int river_depth = 4;
|
||||
|
||||
for (s16 z = node_min.Z; z <= node_max.Z; z++)
|
||||
for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
|
||||
float terrain_mod = noise_terrain_mod->result[index];
|
||||
NoiseParams *np = noise_terrain_river->np;
|
||||
np.persist = noise_terrain_persist->result[index];
|
||||
float terrain_river = NoisePerlin2DNoTxfm(np, x, z, seed);
|
||||
float height = terrain_river * (1 - abs(terrain_mod)) *
|
||||
noise_terrain_river->np.scale;
|
||||
height = log(height * height); //log(h^3) is pretty interesting for terrain
|
||||
|
||||
s16 y = heightmap[index];
|
||||
if (height < 1.0 && y > river_depth &&
|
||||
y - river_depth >= node_min.Y && y <= node_max.Y) {
|
||||
|
||||
for (s16 ry = y; ry != y - river_depth; ry--) {
|
||||
u32 vi = vm->m_area.index(x, ry, z);
|
||||
vm->m_data[vi] = n_air;
|
||||
}
|
||||
|
||||
u32 vi = vm->m_area.index(x, y - river_depth, z);
|
||||
vm->m_data[vi] = n_water_source;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if 0
|
||||
void MapgenV7::addTopNodes()
|
||||
{
|
||||
v3s16 em = vm->m_area.getExtent();
|
||||
s16 ntopnodes;
|
||||
u32 index = 0;
|
||||
|
||||
for (s16 z = node_min.Z; z <= node_max.Z; z++)
|
||||
for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
|
||||
Biome *biome = bmgr->biomes[biomemap[index]];
|
||||
|
||||
//////////////////// First, add top nodes below the ridge
|
||||
s16 y = ridge_heightmap[index];
|
||||
|
||||
// This cutoff is good enough, but not perfect.
|
||||
// It will cut off potentially placed top nodes at chunk boundaries
|
||||
if (y < node_min.Y)
|
||||
continue;
|
||||
if (y > node_max.Y) {
|
||||
y = node_max.Y; // Let's see if we can still go downward anyway
|
||||
u32 vi = vm->m_area.index(x, y, z);
|
||||
content_t c = vm->m_data[vi].getContent();
|
||||
if (ndef->get(c).walkable)
|
||||
continue;
|
||||
}
|
||||
|
||||
// N.B. It is necessary to search downward since ridge_heightmap[i]
|
||||
// might not be the actual height, just the lowest part in the chunk
|
||||
// where a ridge had been carved
|
||||
u32 i = vm->m_area.index(x, y, z);
|
||||
for (; y >= node_min.Y; y--) {
|
||||
content_t c = vm->m_data[i].getContent();
|
||||
if (ndef->get(c).walkable)
|
||||
break;
|
||||
vm->m_area.add_y(em, i, -1);
|
||||
}
|
||||
|
||||
if (y != node_min.Y - 1 && y >= water_level) {
|
||||
ridge_heightmap[index] = y; //update ridgeheight
|
||||
ntopnodes = biome->top_depth;
|
||||
for (; y <= node_max.Y && ntopnodes; y++) {
|
||||
ntopnodes--;
|
||||
vm->m_data[i] = MapNode(biome->c_top);
|
||||
vm->m_area.add_y(em, i, 1);
|
||||
}
|
||||
// If dirt, grow grass on it.
|
||||
if (y > water_level - 10 &&
|
||||
vm->m_data[i].getContent() == CONTENT_AIR) {
|
||||
vm->m_area.add_y(em, i, -1);
|
||||
if (vm->m_data[i].getContent() == c_dirt)
|
||||
vm->m_data[i] = MapNode(c_dirt_with_grass);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////// Now, add top nodes on top of the ridge
|
||||
y = heightmap[index];
|
||||
if (y > node_max.Y) {
|
||||
y = node_max.Y; // Let's see if we can still go downward anyway
|
||||
u32 vi = vm->m_area.index(x, y, z);
|
||||
content_t c = vm->m_data[vi].getContent();
|
||||
if (ndef->get(c).walkable)
|
||||
continue;
|
||||
}
|
||||
|
||||
i = vm->m_area.index(x, y, z);
|
||||
for (; y >= node_min.Y; y--) {
|
||||
content_t c = vm->m_data[i].getContent();
|
||||
if (ndef->get(c).walkable)
|
||||
break;
|
||||
vm->m_area.add_y(em, i, -1);
|
||||
}
|
||||
|
||||
if (y != node_min.Y - 1) {
|
||||
ntopnodes = biome->top_depth;
|
||||
// Let's see if we've already added it...
|
||||
if (y == ridge_heightmap[index] + ntopnodes - 1)
|
||||
continue;
|
||||
|
||||
for (; y <= node_max.Y && ntopnodes; y++) {
|
||||
ntopnodes--;
|
||||
vm->m_data[i] = MapNode(biome->c_top);
|
||||
vm->m_area.add_y(em, i, 1);
|
||||
}
|
||||
// If dirt, grow grass on it.
|
||||
if (y > water_level - 10 &&
|
||||
vm->m_data[i].getContent() == CONTENT_AIR) {
|
||||
vm->m_area.add_y(em, i, -1);
|
||||
if (vm->m_data[i].getContent() == c_dirt)
|
||||
vm->m_data[i] = MapNode(c_dirt_with_grass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
111
src/mapgen/mapgen_v7.h
Normal file
111
src/mapgen/mapgen_v7.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2014-2017 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mapgen.h"
|
||||
|
||||
///////////// Mapgen V7 flags
|
||||
#define MGV7_MOUNTAINS 0x01
|
||||
#define MGV7_RIDGES 0x02
|
||||
#define MGV7_FLOATLANDS 0x04
|
||||
#define MGV7_CAVERNS 0x08
|
||||
#define MGV7_BIOMEREPEAT 0x10 // Now unused
|
||||
|
||||
class BiomeManager;
|
||||
|
||||
extern FlagDesc flagdesc_mapgen_v7[];
|
||||
|
||||
|
||||
struct MapgenV7Params : public MapgenParams {
|
||||
u32 spflags = MGV7_MOUNTAINS | MGV7_RIDGES | MGV7_CAVERNS;
|
||||
s16 mount_zero_level = 0;
|
||||
float cave_width = 0.09f;
|
||||
s16 large_cave_depth = -33;
|
||||
s16 lava_depth = -256;
|
||||
float float_mount_density = 0.6f;
|
||||
float float_mount_height = 128.0f;
|
||||
s16 floatland_level = 1280;
|
||||
s16 shadow_limit = 1024;
|
||||
s16 cavern_limit = -256;
|
||||
s16 cavern_taper = 256;
|
||||
float cavern_threshold = 0.7f;
|
||||
|
||||
NoiseParams np_terrain_base;
|
||||
NoiseParams np_terrain_alt;
|
||||
NoiseParams np_terrain_persist;
|
||||
NoiseParams np_height_select;
|
||||
NoiseParams np_filler_depth;
|
||||
NoiseParams np_mount_height;
|
||||
NoiseParams np_ridge_uwater;
|
||||
NoiseParams np_floatland_base;
|
||||
NoiseParams np_float_base_height;
|
||||
NoiseParams np_mountain;
|
||||
NoiseParams np_ridge;
|
||||
NoiseParams np_cavern;
|
||||
NoiseParams np_cave1;
|
||||
NoiseParams np_cave2;
|
||||
|
||||
MapgenV7Params();
|
||||
~MapgenV7Params() = default;
|
||||
|
||||
void readParams(const Settings *settings);
|
||||
void writeParams(Settings *settings) const;
|
||||
};
|
||||
|
||||
class MapgenV7 : public MapgenBasic {
|
||||
public:
|
||||
MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge);
|
||||
~MapgenV7();
|
||||
|
||||
virtual MapgenType getType() const { return MAPGEN_V7; }
|
||||
|
||||
virtual void makeChunk(BlockMakeData *data);
|
||||
int getSpawnLevelAtPoint(v2s16 p);
|
||||
|
||||
float baseTerrainLevelAtPoint(s16 x, s16 z);
|
||||
float baseTerrainLevelFromMap(int index);
|
||||
bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z);
|
||||
bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y);
|
||||
bool getFloatlandMountainFromMap(int idx_xyz, int idx_xz, s16 y);
|
||||
void floatBaseExtentFromMap(s16 *float_base_min, s16 *float_base_max, int idx_xz);
|
||||
|
||||
int generateTerrain();
|
||||
void generateRidgeTerrain();
|
||||
|
||||
private:
|
||||
s16 mount_zero_level;
|
||||
s16 large_cave_depth;
|
||||
float float_mount_density;
|
||||
float float_mount_height;
|
||||
s16 floatland_level;
|
||||
s16 shadow_limit;
|
||||
|
||||
Noise *noise_terrain_base;
|
||||
Noise *noise_terrain_alt;
|
||||
Noise *noise_terrain_persist;
|
||||
Noise *noise_height_select;
|
||||
Noise *noise_mount_height;
|
||||
Noise *noise_ridge_uwater;
|
||||
Noise *noise_floatland_base;
|
||||
Noise *noise_float_base_height;
|
||||
Noise *noise_mountain;
|
||||
Noise *noise_ridge;
|
||||
};
|
743
src/mapgen/mapgen_valleys.cpp
Normal file
743
src/mapgen/mapgen_valleys.cpp
Normal file
|
@ -0,0 +1,743 @@
|
|||
/*
|
||||
Minetest Valleys C
|
||||
Copyright (C) 2016-2017 Duane Robertson <duane@duanerobertson.com>
|
||||
Copyright (C) 2016-2017 paramat
|
||||
|
||||
Based on Valleys Mapgen by Gael de Sailly
|
||||
(https://forum.minetest.net/viewtopic.php?f=9&t=11430)
|
||||
and mapgen_v7, mapgen_flat by kwolekr and paramat.
|
||||
|
||||
Licensing changed by permission of Gael de Sailly.
|
||||
|
||||
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 "mapgen.h"
|
||||
#include "voxel.h"
|
||||
#include "noise.h"
|
||||
#include "mapblock.h"
|
||||
#include "mapnode.h"
|
||||
#include "map.h"
|
||||
#include "nodedef.h"
|
||||
#include "voxelalgorithms.h"
|
||||
#include "settings.h" // For g_settings
|
||||
#include "emerge.h"
|
||||
#include "dungeongen.h"
|
||||
#include "mg_biome.h"
|
||||
#include "mg_ore.h"
|
||||
#include "mg_decoration.h"
|
||||
#include "mapgen_valleys.h"
|
||||
#include "cavegen.h"
|
||||
|
||||
|
||||
//#undef NDEBUG
|
||||
//#include "assert.h"
|
||||
|
||||
//#include "util/timetaker.h"
|
||||
//#include "profiler.h"
|
||||
|
||||
|
||||
//static Profiler mapgen_prof;
|
||||
//Profiler *mapgen_profiler = &mapgen_prof;
|
||||
|
||||
static FlagDesc flagdesc_mapgen_valleys[] = {
|
||||
{"altitude_chill", MGVALLEYS_ALT_CHILL},
|
||||
{"humid_rivers", MGVALLEYS_HUMID_RIVERS},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
MapgenValleys::MapgenValleys(int mapgenid, MapgenValleysParams *params, EmergeManager *emerge)
|
||||
: MapgenBasic(mapgenid, params, emerge)
|
||||
{
|
||||
// NOTE: MapgenValleys has a hard dependency on BiomeGenOriginal
|
||||
m_bgen = (BiomeGenOriginal *)biomegen;
|
||||
|
||||
BiomeParamsOriginal *bp = (BiomeParamsOriginal *)params->bparams;
|
||||
|
||||
spflags = params->spflags;
|
||||
altitude_chill = params->altitude_chill;
|
||||
large_cave_depth = params->large_cave_depth;
|
||||
lava_features_lim = rangelim(params->lava_features, 0, 10);
|
||||
massive_cave_depth = params->massive_cave_depth;
|
||||
river_depth_bed = params->river_depth + 1.f;
|
||||
river_size_factor = params->river_size / 100.f;
|
||||
water_features_lim = rangelim(params->water_features, 0, 10);
|
||||
cave_width = params->cave_width;
|
||||
|
||||
//// 2D Terrain noise
|
||||
noise_filler_depth = new Noise(¶ms->np_filler_depth, seed, csize.X, csize.Z);
|
||||
noise_inter_valley_slope = new Noise(¶ms->np_inter_valley_slope, seed, csize.X, csize.Z);
|
||||
noise_rivers = new Noise(¶ms->np_rivers, seed, csize.X, csize.Z);
|
||||
noise_terrain_height = new Noise(¶ms->np_terrain_height, seed, csize.X, csize.Z);
|
||||
noise_valley_depth = new Noise(¶ms->np_valley_depth, seed, csize.X, csize.Z);
|
||||
noise_valley_profile = new Noise(¶ms->np_valley_profile, seed, csize.X, csize.Z);
|
||||
|
||||
//// 3D Terrain noise
|
||||
// 1-up 1-down overgeneration
|
||||
noise_inter_valley_fill = new Noise(¶ms->np_inter_valley_fill, seed, csize.X, csize.Y + 2, csize.Z);
|
||||
// 1-down overgeneraion
|
||||
noise_cave1 = new Noise(¶ms->np_cave1, seed, csize.X, csize.Y + 1, csize.Z);
|
||||
noise_cave2 = new Noise(¶ms->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
|
||||
noise_massive_caves = new Noise(¶ms->np_massive_caves, seed, csize.X, csize.Y + 1, csize.Z);
|
||||
|
||||
humid_rivers = (spflags & MGVALLEYS_HUMID_RIVERS);
|
||||
use_altitude_chill = (spflags & MGVALLEYS_ALT_CHILL);
|
||||
humidity_adjust = bp->np_humidity.offset - 50.f;
|
||||
|
||||
// a small chance of overflows if the settings are very high
|
||||
cave_water_max_height = water_level + MYMAX(0, water_features_lim - 4) * 50;
|
||||
lava_max_height = water_level + MYMAX(0, lava_features_lim - 4) * 50;
|
||||
|
||||
tcave_cache = new float[csize.Y + 2];
|
||||
}
|
||||
|
||||
|
||||
MapgenValleys::~MapgenValleys()
|
||||
{
|
||||
delete noise_cave1;
|
||||
delete noise_cave2;
|
||||
delete noise_filler_depth;
|
||||
delete noise_inter_valley_fill;
|
||||
delete noise_inter_valley_slope;
|
||||
delete noise_rivers;
|
||||
delete noise_massive_caves;
|
||||
delete noise_terrain_height;
|
||||
delete noise_valley_depth;
|
||||
delete noise_valley_profile;
|
||||
|
||||
delete[] tcave_cache;
|
||||
}
|
||||
|
||||
|
||||
MapgenValleysParams::MapgenValleysParams():
|
||||
np_cave1 (0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0),
|
||||
np_cave2 (0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0),
|
||||
np_filler_depth (0.f, 1.2f, v3f(256, 256, 256), 1605, 3, 0.5f, 2.f),
|
||||
np_inter_valley_fill (0.f, 1.f, v3f(256, 512, 256), 1993, 6, 0.8f, 2.f),
|
||||
np_inter_valley_slope (0.5f, 0.5f, v3f(128, 128, 128), 746, 1, 1.f, 2.f),
|
||||
np_rivers (0.f, 1.f, v3f(256, 256, 256), -6050, 5, 0.6f, 2.f),
|
||||
np_massive_caves (0.f, 1.f, v3f(768, 256, 768), 59033, 6, 0.63f, 2.f),
|
||||
np_terrain_height (-10.f, 50.f, v3f(1024, 1024, 1024), 5202, 6, 0.4f, 2.f),
|
||||
np_valley_depth (5.f, 4.f, v3f(512, 512, 512), -1914, 1, 1.f, 2.f),
|
||||
np_valley_profile (0.6f, 0.5f, v3f(512, 512, 512), 777, 1, 1.f, 2.f)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void MapgenValleysParams::readParams(const Settings *settings)
|
||||
{
|
||||
settings->getFlagStrNoEx("mgvalleys_spflags", spflags, flagdesc_mapgen_valleys);
|
||||
settings->getU16NoEx("mgvalleys_altitude_chill", altitude_chill);
|
||||
settings->getS16NoEx("mgvalleys_large_cave_depth", large_cave_depth);
|
||||
settings->getU16NoEx("mgvalleys_lava_features", lava_features);
|
||||
settings->getS16NoEx("mgvalleys_massive_cave_depth", massive_cave_depth);
|
||||
settings->getU16NoEx("mgvalleys_river_depth", river_depth);
|
||||
settings->getU16NoEx("mgvalleys_river_size", river_size);
|
||||
settings->getU16NoEx("mgvalleys_water_features", water_features);
|
||||
settings->getFloatNoEx("mgvalleys_cave_width", cave_width);
|
||||
|
||||
settings->getNoiseParams("mgvalleys_np_cave1", np_cave1);
|
||||
settings->getNoiseParams("mgvalleys_np_cave2", np_cave2);
|
||||
settings->getNoiseParams("mgvalleys_np_filler_depth", np_filler_depth);
|
||||
settings->getNoiseParams("mgvalleys_np_inter_valley_fill", np_inter_valley_fill);
|
||||
settings->getNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
|
||||
settings->getNoiseParams("mgvalleys_np_rivers", np_rivers);
|
||||
settings->getNoiseParams("mgvalleys_np_massive_caves", np_massive_caves);
|
||||
settings->getNoiseParams("mgvalleys_np_terrain_height", np_terrain_height);
|
||||
settings->getNoiseParams("mgvalleys_np_valley_depth", np_valley_depth);
|
||||
settings->getNoiseParams("mgvalleys_np_valley_profile", np_valley_profile);
|
||||
}
|
||||
|
||||
|
||||
void MapgenValleysParams::writeParams(Settings *settings) const
|
||||
{
|
||||
settings->setFlagStr("mgvalleys_spflags", spflags, flagdesc_mapgen_valleys, U32_MAX);
|
||||
settings->setU16("mgvalleys_altitude_chill", altitude_chill);
|
||||
settings->setS16("mgvalleys_large_cave_depth", large_cave_depth);
|
||||
settings->setU16("mgvalleys_lava_features", lava_features);
|
||||
settings->setS16("mgvalleys_massive_cave_depth", massive_cave_depth);
|
||||
settings->setU16("mgvalleys_river_depth", river_depth);
|
||||
settings->setU16("mgvalleys_river_size", river_size);
|
||||
settings->setU16("mgvalleys_water_features", water_features);
|
||||
settings->setFloat("mgvalleys_cave_width", cave_width);
|
||||
|
||||
settings->setNoiseParams("mgvalleys_np_cave1", np_cave1);
|
||||
settings->setNoiseParams("mgvalleys_np_cave2", np_cave2);
|
||||
settings->setNoiseParams("mgvalleys_np_filler_depth", np_filler_depth);
|
||||
settings->setNoiseParams("mgvalleys_np_inter_valley_fill", np_inter_valley_fill);
|
||||
settings->setNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
|
||||
settings->setNoiseParams("mgvalleys_np_rivers", np_rivers);
|
||||
settings->setNoiseParams("mgvalleys_np_massive_caves", np_massive_caves);
|
||||
settings->setNoiseParams("mgvalleys_np_terrain_height", np_terrain_height);
|
||||
settings->setNoiseParams("mgvalleys_np_valley_depth", np_valley_depth);
|
||||
settings->setNoiseParams("mgvalleys_np_valley_profile", np_valley_profile);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////
|
||||
|
||||
|
||||
void MapgenValleys::makeChunk(BlockMakeData *data)
|
||||
{
|
||||
// Pre-conditions
|
||||
assert(data->vmanip);
|
||||
assert(data->nodedef);
|
||||
assert(data->blockpos_requested.X >= data->blockpos_min.X &&
|
||||
data->blockpos_requested.Y >= data->blockpos_min.Y &&
|
||||
data->blockpos_requested.Z >= data->blockpos_min.Z);
|
||||
assert(data->blockpos_requested.X <= data->blockpos_max.X &&
|
||||
data->blockpos_requested.Y <= data->blockpos_max.Y &&
|
||||
data->blockpos_requested.Z <= data->blockpos_max.Z);
|
||||
|
||||
this->generating = true;
|
||||
this->vm = data->vmanip;
|
||||
this->ndef = data->nodedef;
|
||||
|
||||
//TimeTaker t("makeChunk");
|
||||
|
||||
v3s16 blockpos_min = data->blockpos_min;
|
||||
v3s16 blockpos_max = data->blockpos_max;
|
||||
node_min = blockpos_min * MAP_BLOCKSIZE;
|
||||
node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
|
||||
full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
|
||||
|
||||
blockseed = getBlockSeed2(full_node_min, seed);
|
||||
|
||||
// Generate biome noises. Note this must be executed strictly before
|
||||
// generateTerrain, because generateTerrain depends on intermediate
|
||||
// biome-related noises.
|
||||
m_bgen->calcBiomeNoise(node_min);
|
||||
|
||||
// Generate noise maps and base terrain height.
|
||||
// Modify heat and humidity maps.
|
||||
calculateNoise();
|
||||
|
||||
// Generate base terrain with initial heightmaps
|
||||
s16 stone_surface_max_y = generateTerrain();
|
||||
|
||||
// Recalculate heightmap
|
||||
updateHeightmap(node_min, node_max);
|
||||
|
||||
// Place biome-specific nodes and build biomemap
|
||||
MgStoneType mgstone_type;
|
||||
content_t biome_stone;
|
||||
generateBiomes(&mgstone_type, &biome_stone);
|
||||
|
||||
// Cave creation.
|
||||
if (flags & MG_CAVES)
|
||||
generateCaves(stone_surface_max_y, large_cave_depth);
|
||||
|
||||
// Dungeon creation
|
||||
if ((flags & MG_DUNGEONS) && node_max.Y < 50)
|
||||
generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
|
||||
|
||||
// Generate the registered decorations
|
||||
if (flags & MG_DECORATIONS)
|
||||
m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
|
||||
|
||||
// Generate the registered ores
|
||||
m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
|
||||
|
||||
// Sprinkle some dust on top after everything else was generated
|
||||
dustTopNodes();
|
||||
|
||||
//TimeTaker tll("liquid_lighting");
|
||||
|
||||
updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
|
||||
|
||||
if (flags & MG_LIGHT)
|
||||
calcLighting(
|
||||
node_min - v3s16(0, 1, 0),
|
||||
node_max + v3s16(0, 1, 0),
|
||||
full_node_min,
|
||||
full_node_max);
|
||||
|
||||
//mapgen_profiler->avg("liquid_lighting", tll.stop() / 1000.f);
|
||||
//mapgen_profiler->avg("makeChunk", t.stop() / 1000.f);
|
||||
|
||||
this->generating = false;
|
||||
}
|
||||
|
||||
|
||||
// Populate the noise tables and do most of the
|
||||
// calculation necessary to determine terrain height.
|
||||
void MapgenValleys::calculateNoise()
|
||||
{
|
||||
//TimeTaker t("calculateNoise", NULL, PRECISION_MICRO);
|
||||
|
||||
int x = node_min.X;
|
||||
int y = node_min.Y - 1;
|
||||
int z = node_min.Z;
|
||||
|
||||
//TimeTaker tcn("actualNoise");
|
||||
|
||||
noise_inter_valley_slope->perlinMap2D(x, z);
|
||||
noise_rivers->perlinMap2D(x, z);
|
||||
noise_terrain_height->perlinMap2D(x, z);
|
||||
noise_valley_depth->perlinMap2D(x, z);
|
||||
noise_valley_profile->perlinMap2D(x, z);
|
||||
|
||||
noise_inter_valley_fill->perlinMap3D(x, y, z);
|
||||
|
||||
//mapgen_profiler->avg("noisemaps", tcn.stop() / 1000.f);
|
||||
|
||||
float heat_offset = 0.f;
|
||||
float humidity_scale = 1.f;
|
||||
|
||||
// Altitude chill tends to reduce the average heat.
|
||||
if (use_altitude_chill)
|
||||
heat_offset = 5.f;
|
||||
|
||||
// River humidity tends to increase the humidity range.
|
||||
if (humid_rivers) {
|
||||
humidity_scale = 0.8f;
|
||||
}
|
||||
|
||||
for (s32 index = 0; index < csize.X * csize.Z; index++) {
|
||||
m_bgen->heatmap[index] += heat_offset;
|
||||
m_bgen->humidmap[index] *= humidity_scale;
|
||||
}
|
||||
|
||||
TerrainNoise tn;
|
||||
|
||||
u32 index = 0;
|
||||
for (tn.z = node_min.Z; tn.z <= node_max.Z; tn.z++)
|
||||
for (tn.x = node_min.X; tn.x <= node_max.X; tn.x++, index++) {
|
||||
// The parameters that we actually need to generate terrain
|
||||
// are passed by address (and the return value).
|
||||
tn.terrain_height = noise_terrain_height->result[index];
|
||||
// River noise is replaced with base terrain, which
|
||||
// is basically the height of the water table.
|
||||
tn.rivers = &noise_rivers->result[index];
|
||||
// Valley depth noise is replaced with the valley
|
||||
// number that represents the height of terrain
|
||||
// over rivers and is used to determine about
|
||||
// how close a river is for humidity calculation.
|
||||
tn.valley = &noise_valley_depth->result[index];
|
||||
tn.valley_profile = noise_valley_profile->result[index];
|
||||
// Slope noise is replaced by the calculated slope
|
||||
// which is used to get terrain height in the slow
|
||||
// method, to create sharper mountains.
|
||||
tn.slope = &noise_inter_valley_slope->result[index];
|
||||
tn.inter_valley_fill = noise_inter_valley_fill->result[index];
|
||||
|
||||
// This is the actual terrain height.
|
||||
float mount = terrainLevelFromNoise(&tn);
|
||||
noise_terrain_height->result[index] = mount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This keeps us from having to maintain two similar sets of
|
||||
// complicated code to determine ground level.
|
||||
float MapgenValleys::terrainLevelFromNoise(TerrainNoise *tn)
|
||||
{
|
||||
// The square function changes the behaviour of this noise:
|
||||
// very often small, and sometimes very high.
|
||||
float valley_d = MYSQUARE(*tn->valley);
|
||||
|
||||
// valley_d is here because terrain is generally higher where valleys
|
||||
// are deep (mountains). base represents the height of the
|
||||
// rivers, most of the surface is above.
|
||||
float base = tn->terrain_height + valley_d;
|
||||
|
||||
// "river" represents the distance from the river, in arbitrary units.
|
||||
float river = fabs(*tn->rivers) - river_size_factor;
|
||||
|
||||
// Use the curve of the function 1-exp(-(x/a)^2) to model valleys.
|
||||
// Making "a" vary (0 < a <= 1) changes the shape of the valleys.
|
||||
// Try it with a geometry software !
|
||||
// (here x = "river" and a = valley_profile).
|
||||
// "valley" represents the height of the terrain, from the rivers.
|
||||
{
|
||||
float t = river / tn->valley_profile;
|
||||
*tn->valley = valley_d * (1.f - exp(- MYSQUARE(t)));
|
||||
}
|
||||
|
||||
// approximate height of the terrain at this point
|
||||
float mount = base + *tn->valley;
|
||||
|
||||
*tn->slope *= *tn->valley;
|
||||
|
||||
// Rivers are placed where "river" is negative, so where the original
|
||||
// noise value is close to zero.
|
||||
// Base ground is returned as rivers since it's basically the water table.
|
||||
*tn->rivers = base;
|
||||
if (river < 0.f) {
|
||||
// Use the the function -sqrt(1-x^2) which models a circle.
|
||||
float depth;
|
||||
{
|
||||
float t = river / river_size_factor + 1;
|
||||
depth = (river_depth_bed * sqrt(MYMAX(0, 1.f - MYSQUARE(t))));
|
||||
}
|
||||
|
||||
// base - depth : height of the bottom of the river
|
||||
// water_level - 3 : don't make rivers below 3 nodes under the surface
|
||||
// We use three because that's as low as the swamp biomes go.
|
||||
// There is no logical equivalent to this using rangelim.
|
||||
mount = MYMIN(MYMAX(base - depth, (float)(water_level - 3)), mount);
|
||||
|
||||
// Slope has no influence on rivers.
|
||||
*tn->slope = 0.f;
|
||||
}
|
||||
|
||||
return mount;
|
||||
}
|
||||
|
||||
|
||||
// This avoids duplicating the code in terrainLevelFromNoise, adding
|
||||
// only the final step of terrain generation without a noise map.
|
||||
float MapgenValleys::adjustedTerrainLevelFromNoise(TerrainNoise *tn)
|
||||
{
|
||||
float mount = terrainLevelFromNoise(tn);
|
||||
s16 y_start = myround(mount);
|
||||
|
||||
for (s16 y = y_start; y <= y_start + 1000; y++) {
|
||||
float fill = NoisePerlin3D(&noise_inter_valley_fill->np, tn->x, y, tn->z, seed);
|
||||
|
||||
if (fill * *tn->slope < y - mount) {
|
||||
mount = MYMAX(y - 1, mount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mount;
|
||||
}
|
||||
|
||||
|
||||
int MapgenValleys::getSpawnLevelAtPoint(v2s16 p)
|
||||
{
|
||||
// Check to make sure this isn't a request for a location in a river.
|
||||
float rivers = NoisePerlin2D(&noise_rivers->np, p.X, p.Y, seed);
|
||||
if (fabs(rivers) < river_size_factor)
|
||||
return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
|
||||
|
||||
s16 level_at_point = terrainLevelAtPoint(p.X, p.Y);
|
||||
if (level_at_point <= water_level ||
|
||||
level_at_point > water_level + 32)
|
||||
return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
|
||||
|
||||
return level_at_point;
|
||||
}
|
||||
|
||||
|
||||
float MapgenValleys::terrainLevelAtPoint(s16 x, s16 z)
|
||||
{
|
||||
TerrainNoise tn;
|
||||
|
||||
float rivers = NoisePerlin2D(&noise_rivers->np, x, z, seed);
|
||||
float valley = NoisePerlin2D(&noise_valley_depth->np, x, z, seed);
|
||||
float inter_valley_slope = NoisePerlin2D(&noise_inter_valley_slope->np, x, z, seed);
|
||||
|
||||
tn.x = x;
|
||||
tn.z = z;
|
||||
tn.terrain_height = NoisePerlin2D(&noise_terrain_height->np, x, z, seed);
|
||||
tn.rivers = &rivers;
|
||||
tn.valley = &valley;
|
||||
tn.valley_profile = NoisePerlin2D(&noise_valley_profile->np, x, z, seed);
|
||||
tn.slope = &inter_valley_slope;
|
||||
tn.inter_valley_fill = 0.f;
|
||||
|
||||
return adjustedTerrainLevelFromNoise(&tn);
|
||||
}
|
||||
|
||||
|
||||
int MapgenValleys::generateTerrain()
|
||||
{
|
||||
// Raising this reduces the rate of evaporation.
|
||||
static const float evaporation = 300.f;
|
||||
// from the lua
|
||||
static const float humidity_dropoff = 4.f;
|
||||
// constant to convert altitude chill (compatible with lua) to heat
|
||||
static const float alt_to_heat = 20.f;
|
||||
// humidity reduction by altitude
|
||||
static const float alt_to_humid = 10.f;
|
||||
|
||||
MapNode n_air(CONTENT_AIR);
|
||||
MapNode n_river_water(c_river_water_source);
|
||||
MapNode n_stone(c_stone);
|
||||
MapNode n_water(c_water_source);
|
||||
|
||||
const v3s16 &em = vm->m_area.getExtent();
|
||||
s16 surface_max_y = -MAX_MAP_GENERATION_LIMIT;
|
||||
u32 index_2d = 0;
|
||||
|
||||
for (s16 z = node_min.Z; z <= node_max.Z; z++)
|
||||
for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
|
||||
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 = m_bgen->heatmap[index_2d];
|
||||
|
||||
heightmap[index_2d] = -MAX_MAP_GENERATION_LIMIT;
|
||||
|
||||
if (surface_y > surface_max_y)
|
||||
surface_max_y = ceil(surface_y);
|
||||
|
||||
if (humid_rivers) {
|
||||
// Derive heat from (base) altitude. This will be most correct
|
||||
// at rivers, since other surface heights may vary below.
|
||||
if (use_altitude_chill && (surface_y > 0.f || river_y > 0.f))
|
||||
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 = 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);
|
||||
}
|
||||
}
|
||||
|
||||
u32 index_3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X);
|
||||
u32 index_data = vm->m_area.index(x, node_min.Y - 1, z);
|
||||
|
||||
// Mapgens concern themselves with stone and water.
|
||||
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
|
||||
if (vm->m_data[index_data].getContent() == CONTENT_IGNORE) {
|
||||
float fill = noise_inter_valley_fill->result[index_3d];
|
||||
float surface_delta = (float)y - surface_y;
|
||||
bool river = y + 1 < river_y;
|
||||
|
||||
if (slope * fill > surface_delta) {
|
||||
// ground
|
||||
vm->m_data[index_data] = n_stone;
|
||||
if (y > heightmap[index_2d])
|
||||
heightmap[index_2d] = y;
|
||||
if (y > surface_max_y)
|
||||
surface_max_y = y;
|
||||
} else if (y <= water_level) {
|
||||
// sea
|
||||
vm->m_data[index_data] = n_water;
|
||||
} else if (river) {
|
||||
// river
|
||||
vm->m_data[index_data] = n_river_water;
|
||||
} else { // air
|
||||
vm->m_data[index_data] = n_air;
|
||||
}
|
||||
}
|
||||
|
||||
vm->m_area.add_y(em, index_data, 1);
|
||||
index_3d += ystride;
|
||||
}
|
||||
|
||||
if (heightmap[index_2d] == -MAX_MAP_GENERATION_LIMIT) {
|
||||
s16 surface_y_int = myround(surface_y);
|
||||
if (surface_y_int > node_max.Y + 1 || surface_y_int < node_min.Y - 1) {
|
||||
// If surface_y is outside the chunk, it's good enough.
|
||||
heightmap[index_2d] = surface_y_int;
|
||||
} else {
|
||||
// If the ground is outside of this chunk, but surface_y
|
||||
// is within the chunk, give a value outside.
|
||||
heightmap[index_2d] = node_min.Y - 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (humid_rivers) {
|
||||
// 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 = 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));
|
||||
|
||||
// Reduce humidity with altitude (ignoring riverbeds).
|
||||
// This is similar to the lua version's seawater adjustment,
|
||||
// but doesn't increase the base humidity, which causes
|
||||
// problems with the default biomes.
|
||||
if (t_alt > 0.f)
|
||||
humid -= alt_to_humid * t_alt / altitude_chill;
|
||||
|
||||
m_bgen->humidmap[index_2d] = humid;
|
||||
}
|
||||
|
||||
// Assign the heat adjusted by any changed altitudes.
|
||||
// The altitude will change about half the time.
|
||||
if (use_altitude_chill) {
|
||||
// ground height ignoring riverbeds
|
||||
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.
|
||||
m_bgen->heatmap[index_2d] = t_heat;
|
||||
else if (t_alt > 0.f)
|
||||
m_bgen->heatmap[index_2d] -= alt_to_heat * t_alt / altitude_chill;
|
||||
}
|
||||
}
|
||||
|
||||
return surface_max_y;
|
||||
}
|
||||
|
||||
void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth)
|
||||
{
|
||||
if (max_stone_y < node_min.Y)
|
||||
return;
|
||||
|
||||
noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
|
||||
noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
|
||||
|
||||
PseudoRandom ps(blockseed + 72202);
|
||||
|
||||
MapNode n_air(CONTENT_AIR);
|
||||
MapNode n_lava(c_lava_source);
|
||||
MapNode n_water(c_river_water_source);
|
||||
|
||||
const v3s16 &em = vm->m_area.getExtent();
|
||||
|
||||
// Cave blend distance near YMIN, YMAX
|
||||
const float massive_cave_blend = 128.f;
|
||||
// noise threshold for massive caves
|
||||
const float massive_cave_threshold = 0.6f;
|
||||
// mct: 1 = small rare caves, 0.5 1/3rd ground volume, 0 = 1/2 ground volume.
|
||||
|
||||
float yblmin = -mapgen_limit + massive_cave_blend * 1.5f;
|
||||
float yblmax = massive_cave_depth - massive_cave_blend * 1.5f;
|
||||
bool made_a_big_one = false;
|
||||
|
||||
// Cache the tcave values as they only vary by altitude.
|
||||
if (node_max.Y <= massive_cave_depth) {
|
||||
noise_massive_caves->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
|
||||
|
||||
for (s16 y = node_min.Y - 1; y <= node_max.Y; y++) {
|
||||
float tcave = massive_cave_threshold;
|
||||
|
||||
if (y < yblmin) {
|
||||
float t = (yblmin - y) / massive_cave_blend;
|
||||
tcave += MYSQUARE(t);
|
||||
} else if (y > yblmax) {
|
||||
float t = (y - yblmax) / massive_cave_blend;
|
||||
tcave += MYSQUARE(t);
|
||||
}
|
||||
|
||||
tcave_cache[y - node_min.Y + 1] = tcave;
|
||||
}
|
||||
}
|
||||
|
||||
// lava_depth varies between one and ten as you approach
|
||||
// the bottom of the world.
|
||||
s16 lava_depth = ceil((lava_max_height - node_min.Y + 1) * 10.f / mapgen_limit);
|
||||
// This allows random lava spawns to be less common at the surface.
|
||||
s16 lava_chance = MYCUBE(lava_features_lim) * lava_depth;
|
||||
// water_depth varies between ten and one on the way down.
|
||||
s16 water_depth = ceil((mapgen_limit - abs(node_min.Y) + 1) * 10.f / mapgen_limit);
|
||||
// This allows random water spawns to be more common at the surface.
|
||||
s16 water_chance = MYCUBE(water_features_lim) * water_depth;
|
||||
|
||||
// Reduce the odds of overflows even further.
|
||||
if (node_max.Y > water_level) {
|
||||
lava_chance /= 3;
|
||||
water_chance /= 3;
|
||||
}
|
||||
|
||||
u32 index_2d = 0;
|
||||
for (s16 z = node_min.Z; z <= node_max.Z; z++)
|
||||
for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
|
||||
Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index_2d]);
|
||||
bool tunnel_air_above = false;
|
||||
bool is_under_river = false;
|
||||
bool underground = false;
|
||||
u32 index_data = vm->m_area.index(x, node_max.Y, z);
|
||||
u32 index_3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + (x - node_min.X);
|
||||
|
||||
// Dig caves on down loop to check for air above.
|
||||
// Don't excavate the overgenerated stone at node_max.Y + 1,
|
||||
// this creates a 'roof' over the tunnel, preventing light in
|
||||
// tunnels at mapchunk borders when generating mapchunks upwards.
|
||||
// This 'roof' is removed when the mapchunk above is generated.
|
||||
for (s16 y = node_max.Y; y >= node_min.Y - 1; y--,
|
||||
index_3d -= ystride,
|
||||
vm->m_area.add_y(em, index_data, -1)) {
|
||||
|
||||
float terrain = noise_terrain_height->result[index_2d];
|
||||
|
||||
// Saves some time.
|
||||
if (y > terrain + 10)
|
||||
continue;
|
||||
|
||||
if (y < terrain - 40)
|
||||
underground = true;
|
||||
|
||||
// Dig massive caves.
|
||||
if (node_max.Y <= massive_cave_depth
|
||||
&& noise_massive_caves->result[index_3d]
|
||||
> tcave_cache[y - node_min.Y + 1]) {
|
||||
vm->m_data[index_data] = n_air;
|
||||
made_a_big_one = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
content_t c = vm->m_data[index_data].getContent();
|
||||
// Detect river water to place riverbed nodes in tunnels
|
||||
if (c == biome->c_river_water)
|
||||
is_under_river = true;
|
||||
|
||||
float d1 = contour(noise_cave1->result[index_3d]);
|
||||
float d2 = contour(noise_cave2->result[index_3d]);
|
||||
|
||||
if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) {
|
||||
// in a tunnel
|
||||
vm->m_data[index_data] = n_air;
|
||||
tunnel_air_above = true;
|
||||
} else if (c == biome->c_filler || c == biome->c_stone) {
|
||||
if (tunnel_air_above) {
|
||||
// at the tunnel floor
|
||||
s16 sr = ps.range(0, 39);
|
||||
u32 j = index_data;
|
||||
vm->m_area.add_y(em, j, 1);
|
||||
|
||||
if (sr > terrain - y) {
|
||||
// Put biome nodes in tunnels near the surface
|
||||
if (is_under_river)
|
||||
vm->m_data[index_data] = MapNode(biome->c_riverbed);
|
||||
else if (underground)
|
||||
vm->m_data[index_data] = MapNode(biome->c_filler);
|
||||
else
|
||||
vm->m_data[index_data] = MapNode(biome->c_top);
|
||||
} else if (sr < 3 && underground) {
|
||||
sr = abs(ps.next());
|
||||
if (lava_features_lim > 0 && y <= lava_max_height
|
||||
&& c == biome->c_stone && sr < lava_chance)
|
||||
vm->m_data[j] = n_lava;
|
||||
|
||||
sr -= lava_chance;
|
||||
|
||||
// If sr < 0 then we should have already placed lava --
|
||||
// don't immediately dump water on it.
|
||||
if (water_features_lim > 0 && y <= cave_water_max_height
|
||||
&& sr >= 0 && sr < water_chance)
|
||||
vm->m_data[j] = n_water;
|
||||
}
|
||||
}
|
||||
|
||||
tunnel_air_above = false;
|
||||
underground = true;
|
||||
} else {
|
||||
tunnel_air_above = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node_max.Y <= large_cave_depth && !made_a_big_one) {
|
||||
u32 bruises_count = ps.range(0, 2);
|
||||
for (u32 i = 0; i < bruises_count; i++) {
|
||||
CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
|
||||
c_water_source, c_lava_source, lava_max_height);
|
||||
|
||||
cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
|
||||
}
|
||||
}
|
||||
}
|
134
src/mapgen/mapgen_valleys.h
Normal file
134
src/mapgen/mapgen_valleys.h
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
Minetest Valleys C
|
||||
Copyright (C) 2016-2017 Duane Robertson <duane@duanerobertson.com>
|
||||
Copyright (C) 2016-2017 paramat
|
||||
|
||||
Based on Valleys Mapgen by Gael de Sailly
|
||||
(https://forum.minetest.net/viewtopic.php?f=9&t=11430)
|
||||
and mapgen_v7 by kwolekr and paramat.
|
||||
|
||||
Licensing changed by permission of Gael de Sailly.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mapgen.h"
|
||||
|
||||
////////////// Mapgen Valleys flags
|
||||
#define MGVALLEYS_ALT_CHILL 0x01
|
||||
#define MGVALLEYS_HUMID_RIVERS 0x02
|
||||
|
||||
// Feed only one variable into these.
|
||||
#define MYSQUARE(x) (x) * (x)
|
||||
#define MYCUBE(x) (x) * (x) * (x)
|
||||
|
||||
class BiomeManager;
|
||||
class BiomeGenOriginal;
|
||||
|
||||
// Global profiler
|
||||
//class Profiler;
|
||||
//extern Profiler *mapgen_profiler;
|
||||
|
||||
|
||||
struct MapgenValleysParams : public MapgenParams {
|
||||
u32 spflags = MGVALLEYS_HUMID_RIVERS | MGVALLEYS_ALT_CHILL;
|
||||
s16 large_cave_depth = -33;
|
||||
s16 massive_cave_depth = -256; // highest altitude of massive caves
|
||||
u16 altitude_chill = 90; // The altitude at which temperature drops by 20C.
|
||||
u16 lava_features = 0; // How often water will occur in caves.
|
||||
u16 river_depth = 4; // How deep to carve river channels.
|
||||
u16 river_size = 5; // How wide to make rivers.
|
||||
u16 water_features = 0; // How often water will occur in caves.
|
||||
float cave_width = 0.09f;
|
||||
NoiseParams np_cave1;
|
||||
NoiseParams np_cave2;
|
||||
NoiseParams np_filler_depth;
|
||||
NoiseParams np_inter_valley_fill;
|
||||
NoiseParams np_inter_valley_slope;
|
||||
NoiseParams np_rivers;
|
||||
NoiseParams np_massive_caves;
|
||||
NoiseParams np_terrain_height;
|
||||
NoiseParams np_valley_depth;
|
||||
NoiseParams np_valley_profile;
|
||||
|
||||
MapgenValleysParams();
|
||||
~MapgenValleysParams() = default;
|
||||
|
||||
void readParams(const Settings *settings);
|
||||
void writeParams(Settings *settings) const;
|
||||
};
|
||||
|
||||
struct TerrainNoise {
|
||||
s16 x;
|
||||
s16 z;
|
||||
float terrain_height;
|
||||
float *rivers;
|
||||
float *valley;
|
||||
float valley_profile;
|
||||
float *slope;
|
||||
float inter_valley_fill;
|
||||
};
|
||||
|
||||
class MapgenValleys : public MapgenBasic {
|
||||
public:
|
||||
|
||||
MapgenValleys(int mapgenid, MapgenValleysParams *params, EmergeManager *emerge);
|
||||
~MapgenValleys();
|
||||
|
||||
virtual MapgenType getType() const { return MAPGEN_VALLEYS; }
|
||||
|
||||
virtual void makeChunk(BlockMakeData *data);
|
||||
int getSpawnLevelAtPoint(v2s16 p);
|
||||
|
||||
s16 large_cave_depth;
|
||||
|
||||
private:
|
||||
BiomeGenOriginal *m_bgen;
|
||||
|
||||
bool humid_rivers;
|
||||
bool use_altitude_chill;
|
||||
float humidity_adjust;
|
||||
s16 cave_water_max_height;
|
||||
s16 lava_max_height;
|
||||
|
||||
float altitude_chill;
|
||||
s16 lava_features_lim;
|
||||
s16 massive_cave_depth;
|
||||
float river_depth_bed;
|
||||
float river_size_factor;
|
||||
float *tcave_cache;
|
||||
s16 water_features_lim;
|
||||
Noise *noise_inter_valley_fill;
|
||||
Noise *noise_inter_valley_slope;
|
||||
Noise *noise_rivers;
|
||||
Noise *noise_cave1;
|
||||
Noise *noise_cave2;
|
||||
Noise *noise_massive_caves;
|
||||
Noise *noise_terrain_height;
|
||||
Noise *noise_valley_depth;
|
||||
Noise *noise_valley_profile;
|
||||
|
||||
float terrainLevelAtPoint(s16 x, s16 z);
|
||||
|
||||
void calculateNoise();
|
||||
|
||||
virtual int generateTerrain();
|
||||
float terrainLevelFromNoise(TerrainNoise *tn);
|
||||
float adjustedTerrainLevelFromNoise(TerrainNoise *tn);
|
||||
|
||||
virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth);
|
||||
};
|
238
src/mapgen/mg_biome.cpp
Normal file
238
src/mapgen/mg_biome.cpp
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2014-2017 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->y_min = -MAX_MAP_GENERATION_LIMIT;
|
||||
b->y_max = MAX_MAP_GENERATION_LIMIT;
|
||||
b->heat_point = 0.0;
|
||||
b->humidity_point = 0.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");
|
||||
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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
biome_t *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()
|
||||
{
|
||||
getIdFromNrBacklog(&c_top, "mapgen_stone", CONTENT_AIR);
|
||||
getIdFromNrBacklog(&c_filler, "mapgen_stone", CONTENT_AIR);
|
||||
getIdFromNrBacklog(&c_stone, "mapgen_stone", CONTENT_AIR);
|
||||
getIdFromNrBacklog(&c_water_top, "mapgen_water_source", CONTENT_AIR);
|
||||
getIdFromNrBacklog(&c_water, "mapgen_water_source", CONTENT_AIR);
|
||||
getIdFromNrBacklog(&c_river_water, "mapgen_river_water_source", CONTENT_AIR);
|
||||
getIdFromNrBacklog(&c_riverbed, "mapgen_stone", CONTENT_AIR);
|
||||
getIdFromNrBacklog(&c_dust, "ignore", CONTENT_IGNORE);
|
||||
}
|
230
src/mapgen/mg_biome.h
Normal file
230
src/mapgen/mg_biome.h
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2014-2017 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "objdef.h"
|
||||
#include "nodedef.h"
|
||||
#include "noise.h"
|
||||
|
||||
class Server;
|
||||
class Settings;
|
||||
class BiomeManager;
|
||||
|
||||
////
|
||||
//// Biome
|
||||
////
|
||||
|
||||
typedef u8 biome_t;
|
||||
|
||||
#define BIOME_NONE ((biome_t)0)
|
||||
|
||||
// TODO(hmmmm): Decide whether this is obsolete or will be used in the future
|
||||
enum BiomeType {
|
||||
BIOMETYPE_NORMAL,
|
||||
BIOMETYPE_LIQUID,
|
||||
BIOMETYPE_NETHER,
|
||||
BIOMETYPE_AETHER,
|
||||
BIOMETYPE_FLAT,
|
||||
};
|
||||
|
||||
class Biome : public ObjDef, public NodeResolver {
|
||||
public:
|
||||
u32 flags;
|
||||
|
||||
content_t c_top;
|
||||
content_t c_filler;
|
||||
content_t c_stone;
|
||||
content_t c_water_top;
|
||||
content_t c_water;
|
||||
content_t c_river_water;
|
||||
content_t c_riverbed;
|
||||
content_t c_dust;
|
||||
|
||||
s16 depth_top;
|
||||
s16 depth_filler;
|
||||
s16 depth_water_top;
|
||||
s16 depth_riverbed;
|
||||
|
||||
s16 y_min;
|
||||
s16 y_max;
|
||||
float heat_point;
|
||||
float humidity_point;
|
||||
|
||||
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() = default;
|
||||
|
||||
s32 seed;
|
||||
};
|
||||
|
||||
class BiomeGen {
|
||||
public:
|
||||
virtual ~BiomeGen() = default;
|
||||
|
||||
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 biome_t *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.
|
||||
biome_t *biomemap = nullptr;
|
||||
|
||||
protected:
|
||||
BiomeManager *m_bmgr = nullptr;
|
||||
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(1000.0, 1000.0, 1000.0), 5349, 3, 0.5, 2.0),
|
||||
np_humidity(50, 50, v3f(1000.0, 1000.0, 1000.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);
|
||||
|
||||
biome_t *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:
|
||||
BiomeManager(Server *server);
|
||||
virtual ~BiomeManager() = default;
|
||||
|
||||
const char *getObjectTitle() const
|
||||
{
|
||||
return "biome";
|
||||
}
|
||||
|
||||
static Biome *create(BiomeType type)
|
||||
{
|
||||
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();
|
||||
|
||||
private:
|
||||
Server *m_server;
|
||||
|
||||
};
|
377
src/mapgen/mg_decoration.cpp
Normal file
377
src/mapgen/mg_decoration.cpp
Normal file
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2015-2017 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_decoration.h"
|
||||
#include "mg_schematic.h"
|
||||
#include "mapgen.h"
|
||||
#include "noise.h"
|
||||
#include "map.h"
|
||||
#include "log.h"
|
||||
#include "util/numeric.h"
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
FlagDesc flagdesc_deco[] = {
|
||||
{"place_center_x", DECO_PLACE_CENTER_X},
|
||||
{"place_center_y", DECO_PLACE_CENTER_Y},
|
||||
{"place_center_z", DECO_PLACE_CENTER_Z},
|
||||
{"force_placement", DECO_FORCE_PLACEMENT},
|
||||
{"liquid_surface", DECO_LIQUID_SURFACE},
|
||||
{"all_floors", DECO_ALL_FLOORS},
|
||||
{"all_ceilings", DECO_ALL_CEILINGS},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
DecorationManager::DecorationManager(IGameDef *gamedef) :
|
||||
ObjDefManager(gamedef, OBJDEF_DECORATION)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
size_t DecorationManager::placeAllDecos(Mapgen *mg, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax)
|
||||
{
|
||||
size_t nplaced = 0;
|
||||
|
||||
for (size_t i = 0; i != m_objects.size(); i++) {
|
||||
Decoration *deco = (Decoration *)m_objects[i];
|
||||
if (!deco)
|
||||
continue;
|
||||
|
||||
nplaced += deco->placeDeco(mg, blockseed, nmin, nmax);
|
||||
blockseed++;
|
||||
}
|
||||
|
||||
return nplaced;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void Decoration::resolveNodeNames()
|
||||
{
|
||||
getIdsFromNrBacklog(&c_place_on);
|
||||
getIdsFromNrBacklog(&c_spawnby);
|
||||
}
|
||||
|
||||
|
||||
bool Decoration::canPlaceDecoration(MMVManip *vm, v3s16 p)
|
||||
{
|
||||
// Check if the decoration can be placed on this node
|
||||
u32 vi = vm->m_area.index(p);
|
||||
if (!CONTAINS(c_place_on, vm->m_data[vi].getContent()))
|
||||
return false;
|
||||
|
||||
// Don't continue if there are no spawnby constraints
|
||||
if (nspawnby == -1)
|
||||
return true;
|
||||
|
||||
int nneighs = 0;
|
||||
static const v3s16 dirs[16] = {
|
||||
v3s16( 0, 0, 1),
|
||||
v3s16( 0, 0, -1),
|
||||
v3s16( 1, 0, 0),
|
||||
v3s16(-1, 0, 0),
|
||||
v3s16( 1, 0, 1),
|
||||
v3s16(-1, 0, 1),
|
||||
v3s16(-1, 0, -1),
|
||||
v3s16( 1, 0, -1),
|
||||
|
||||
v3s16( 0, 1, 1),
|
||||
v3s16( 0, 1, -1),
|
||||
v3s16( 1, 1, 0),
|
||||
v3s16(-1, 1, 0),
|
||||
v3s16( 1, 1, 1),
|
||||
v3s16(-1, 1, 1),
|
||||
v3s16(-1, 1, -1),
|
||||
v3s16( 1, 1, -1)
|
||||
};
|
||||
|
||||
// Check these 16 neighbouring nodes for enough spawnby nodes
|
||||
for (size_t i = 0; i != ARRLEN(dirs); i++) {
|
||||
u32 index = vm->m_area.index(p + dirs[i]);
|
||||
if (!vm->m_area.contains(index))
|
||||
continue;
|
||||
|
||||
if (CONTAINS(c_spawnby, vm->m_data[index].getContent()))
|
||||
nneighs++;
|
||||
}
|
||||
|
||||
if (nneighs < nspawnby)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
|
||||
{
|
||||
PcgRandom ps(blockseed + 53);
|
||||
int carea_size = nmax.X - nmin.X + 1;
|
||||
|
||||
// Divide area into parts
|
||||
// If chunksize is changed it may no longer be divisable by sidelen
|
||||
if (carea_size % sidelen)
|
||||
sidelen = carea_size;
|
||||
|
||||
s16 divlen = carea_size / sidelen;
|
||||
int area = sidelen * sidelen;
|
||||
|
||||
for (s16 z0 = 0; z0 < divlen; z0++)
|
||||
for (s16 x0 = 0; x0 < divlen; x0++) {
|
||||
v2s16 p2d_center( // Center position of part of division
|
||||
nmin.X + sidelen / 2 + sidelen * x0,
|
||||
nmin.Z + sidelen / 2 + sidelen * z0
|
||||
);
|
||||
v2s16 p2d_min( // Minimum edge of part of division
|
||||
nmin.X + sidelen * x0,
|
||||
nmin.Z + sidelen * z0
|
||||
);
|
||||
v2s16 p2d_max( // Maximum edge of part of division
|
||||
nmin.X + sidelen + sidelen * x0 - 1,
|
||||
nmin.Z + sidelen + sidelen * z0 - 1
|
||||
);
|
||||
|
||||
// Amount of decorations
|
||||
float nval = (flags & DECO_USE_NOISE) ?
|
||||
NoisePerlin2D(&np, p2d_center.X, p2d_center.Y, mapseed) :
|
||||
fill_ratio;
|
||||
u32 deco_count = 0;
|
||||
float deco_count_f = (float)area * nval;
|
||||
if (deco_count_f >= 1.f) {
|
||||
deco_count = deco_count_f;
|
||||
} else if (deco_count_f > 0.f) {
|
||||
// For low density decorations calculate a chance for 1 decoration
|
||||
if (ps.range(1000) <= deco_count_f * 1000.f)
|
||||
deco_count = 1;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < deco_count; i++) {
|
||||
s16 x = ps.range(p2d_min.X, p2d_max.X);
|
||||
s16 z = ps.range(p2d_min.Y, p2d_max.Y);
|
||||
int mapindex = carea_size * (z - nmin.Z) + (x - nmin.X);
|
||||
|
||||
if ((flags & DECO_ALL_FLOORS) ||
|
||||
(flags & DECO_ALL_CEILINGS)) {
|
||||
// All-surfaces decorations
|
||||
// Check biome of column
|
||||
if (mg->biomemap && !biomes.empty()) {
|
||||
std::unordered_set<u8>::const_iterator iter =
|
||||
biomes.find(mg->biomemap[mapindex]);
|
||||
if (iter == biomes.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get all floors and ceilings in node column
|
||||
u16 size = (nmax.Y - nmin.Y + 1) / 2;
|
||||
s16 floors[size];
|
||||
s16 ceilings[size];
|
||||
u16 num_floors = 0;
|
||||
u16 num_ceilings = 0;
|
||||
|
||||
mg->getSurfaces(v2s16(x, z), nmin.Y, nmax.Y,
|
||||
floors, ceilings, &num_floors, &num_ceilings);
|
||||
|
||||
if ((flags & DECO_ALL_FLOORS) && num_floors > 0) {
|
||||
// Floor decorations
|
||||
for (u16 fi = 0; fi < num_floors; fi++) {
|
||||
s16 y = floors[fi];
|
||||
if (y < y_min || y > y_max)
|
||||
continue;
|
||||
|
||||
v3s16 pos(x, y, z);
|
||||
if (generate(mg->vm, &ps, pos, false))
|
||||
mg->gennotify.addEvent(
|
||||
GENNOTIFY_DECORATION, pos, index);
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & DECO_ALL_CEILINGS) && num_ceilings > 0) {
|
||||
// Ceiling decorations
|
||||
for (u16 ci = 0; ci < num_ceilings; ci++) {
|
||||
s16 y = ceilings[ci];
|
||||
if (y < y_min || y > y_max)
|
||||
continue;
|
||||
|
||||
v3s16 pos(x, y, z);
|
||||
if (generate(mg->vm, &ps, pos, true))
|
||||
mg->gennotify.addEvent(
|
||||
GENNOTIFY_DECORATION, pos, index);
|
||||
}
|
||||
}
|
||||
} else { // Heightmap decorations
|
||||
s16 y = -MAX_MAP_GENERATION_LIMIT;
|
||||
if (flags & DECO_LIQUID_SURFACE)
|
||||
y = mg->findLiquidSurface(v2s16(x, z), nmin.Y, nmax.Y);
|
||||
else if (mg->heightmap)
|
||||
y = mg->heightmap[mapindex];
|
||||
else
|
||||
y = mg->findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
|
||||
|
||||
if (y < y_min || y > y_max || y < nmin.Y || y > nmax.Y)
|
||||
continue;
|
||||
|
||||
if (mg->biomemap && !biomes.empty()) {
|
||||
std::unordered_set<u8>::const_iterator iter =
|
||||
biomes.find(mg->biomemap[mapindex]);
|
||||
if (iter == biomes.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
v3s16 pos(x, y, z);
|
||||
if (generate(mg->vm, &ps, pos, false))
|
||||
mg->gennotify.addEvent(GENNOTIFY_DECORATION, pos, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void DecoSimple::resolveNodeNames()
|
||||
{
|
||||
Decoration::resolveNodeNames();
|
||||
getIdsFromNrBacklog(&c_decos);
|
||||
}
|
||||
|
||||
|
||||
size_t DecoSimple::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling)
|
||||
{
|
||||
// Don't bother if there aren't any decorations to place
|
||||
if (c_decos.empty())
|
||||
return 0;
|
||||
|
||||
if (!canPlaceDecoration(vm, p))
|
||||
return 0;
|
||||
|
||||
// Check for placement outside the voxelmanip volume
|
||||
if (ceiling) {
|
||||
// Ceiling decorations
|
||||
// 'place offset y' is inverted
|
||||
if (p.Y - place_offset_y - std::max(deco_height, deco_height_max) <
|
||||
vm->m_area.MinEdge.Y)
|
||||
return 0;
|
||||
|
||||
if (p.Y - 1 - place_offset_y > vm->m_area.MaxEdge.Y)
|
||||
return 0;
|
||||
|
||||
} else { // Heightmap and floor decorations
|
||||
if (p.Y + place_offset_y + std::max(deco_height, deco_height_max) >
|
||||
vm->m_area.MaxEdge.Y)
|
||||
return 0;
|
||||
|
||||
if (p.Y + 1 + place_offset_y < vm->m_area.MinEdge.Y)
|
||||
return 0;
|
||||
}
|
||||
|
||||
content_t c_place = c_decos[pr->range(0, c_decos.size() - 1)];
|
||||
s16 height = (deco_height_max > 0) ?
|
||||
pr->range(deco_height, deco_height_max) : deco_height;
|
||||
u8 param2 = (deco_param2_max > 0) ?
|
||||
pr->range(deco_param2, deco_param2_max) : deco_param2;
|
||||
bool force_placement = (flags & DECO_FORCE_PLACEMENT);
|
||||
|
||||
const v3s16 &em = vm->m_area.getExtent();
|
||||
u32 vi = vm->m_area.index(p);
|
||||
|
||||
if (ceiling) {
|
||||
// Ceiling decorations
|
||||
// 'place offset y' is inverted
|
||||
vm->m_area.add_y(em, vi, -place_offset_y);
|
||||
|
||||
for (int i = 0; i < height; i++) {
|
||||
vm->m_area.add_y(em, vi, -1);
|
||||
content_t c = vm->m_data[vi].getContent();
|
||||
if (c != CONTENT_AIR && c != CONTENT_IGNORE && !force_placement)
|
||||
break;
|
||||
|
||||
vm->m_data[vi] = MapNode(c_place, 0, param2);
|
||||
}
|
||||
} else { // Heightmap and floor decorations
|
||||
vm->m_area.add_y(em, vi, place_offset_y);
|
||||
|
||||
for (int i = 0; i < height; i++) {
|
||||
vm->m_area.add_y(em, vi, 1);
|
||||
content_t c = vm->m_data[vi].getContent();
|
||||
if (c != CONTENT_AIR && c != CONTENT_IGNORE && !force_placement)
|
||||
break;
|
||||
|
||||
vm->m_data[vi] = MapNode(c_place, 0, param2);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
size_t DecoSchematic::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling)
|
||||
{
|
||||
// Schematic could have been unloaded but not the decoration
|
||||
// In this case generate() does nothing (but doesn't *fail*)
|
||||
if (schematic == NULL)
|
||||
return 0;
|
||||
|
||||
if (!canPlaceDecoration(vm, p))
|
||||
return 0;
|
||||
|
||||
if (flags & DECO_PLACE_CENTER_Y) {
|
||||
p.Y -= (schematic->size.Y - 1) / 2;
|
||||
} else {
|
||||
// Only apply 'place offset y' if not 'deco place center y'
|
||||
if (ceiling)
|
||||
// Shift down so that schematic top layer is level with ceiling
|
||||
// 'place offset y' is inverted
|
||||
p.Y -= (place_offset_y + schematic->size.Y - 1);
|
||||
else
|
||||
p.Y += place_offset_y;
|
||||
}
|
||||
|
||||
// Check schematic top and base are in voxelmanip
|
||||
if (p.Y + schematic->size.Y - 1 > vm->m_area.MaxEdge.Y)
|
||||
return 0;
|
||||
|
||||
if (p.Y < vm->m_area.MinEdge.Y)
|
||||
return 0;
|
||||
|
||||
if (flags & DECO_PLACE_CENTER_X)
|
||||
p.X -= (schematic->size.X - 1) / 2;
|
||||
if (flags & DECO_PLACE_CENTER_Z)
|
||||
p.Z -= (schematic->size.Z - 1) / 2;
|
||||
|
||||
Rotation rot = (rotation == ROTATE_RAND) ?
|
||||
(Rotation)pr->range(ROTATE_0, ROTATE_270) : rotation;
|
||||
bool force_placement = (flags & DECO_FORCE_PLACEMENT);
|
||||
|
||||
schematic->blitToVManip(vm, p, rot, force_placement);
|
||||
|
||||
return 1;
|
||||
}
|
136
src/mapgen/mg_decoration.h
Normal file
136
src/mapgen/mg_decoration.h
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2015-2017 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
#include "objdef.h"
|
||||
#include "noise.h"
|
||||
#include "nodedef.h"
|
||||
|
||||
class Mapgen;
|
||||
class MMVManip;
|
||||
class PcgRandom;
|
||||
class Schematic;
|
||||
|
||||
enum DecorationType {
|
||||
DECO_SIMPLE,
|
||||
DECO_SCHEMATIC,
|
||||
DECO_LSYSTEM
|
||||
};
|
||||
|
||||
#define DECO_PLACE_CENTER_X 0x01
|
||||
#define DECO_PLACE_CENTER_Y 0x02
|
||||
#define DECO_PLACE_CENTER_Z 0x04
|
||||
#define DECO_USE_NOISE 0x08
|
||||
#define DECO_FORCE_PLACEMENT 0x10
|
||||
#define DECO_LIQUID_SURFACE 0x20
|
||||
#define DECO_ALL_FLOORS 0x40
|
||||
#define DECO_ALL_CEILINGS 0x80
|
||||
|
||||
extern FlagDesc flagdesc_deco[];
|
||||
|
||||
|
||||
class Decoration : public ObjDef, public NodeResolver {
|
||||
public:
|
||||
Decoration() = default;
|
||||
virtual ~Decoration() = default;
|
||||
|
||||
virtual void resolveNodeNames();
|
||||
|
||||
bool canPlaceDecoration(MMVManip *vm, v3s16 p);
|
||||
size_t placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
|
||||
|
||||
virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling) = 0;
|
||||
|
||||
u32 flags = 0;
|
||||
int mapseed = 0;
|
||||
std::vector<content_t> c_place_on;
|
||||
s16 sidelen = 1;
|
||||
s16 y_min;
|
||||
s16 y_max;
|
||||
float fill_ratio = 0.0f;
|
||||
NoiseParams np;
|
||||
std::vector<content_t> c_spawnby;
|
||||
s16 nspawnby;
|
||||
s16 place_offset_y = 0;
|
||||
|
||||
std::unordered_set<u8> biomes;
|
||||
};
|
||||
|
||||
|
||||
class DecoSimple : public Decoration {
|
||||
public:
|
||||
virtual void resolveNodeNames();
|
||||
virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling);
|
||||
|
||||
std::vector<content_t> c_decos;
|
||||
s16 deco_height;
|
||||
s16 deco_height_max;
|
||||
u8 deco_param2;
|
||||
u8 deco_param2_max;
|
||||
};
|
||||
|
||||
|
||||
class DecoSchematic : public Decoration {
|
||||
public:
|
||||
DecoSchematic() = default;
|
||||
|
||||
virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling);
|
||||
|
||||
Rotation rotation;
|
||||
Schematic *schematic = nullptr;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
class DecoLSystem : public Decoration {
|
||||
public:
|
||||
virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
class DecorationManager : public ObjDefManager {
|
||||
public:
|
||||
DecorationManager(IGameDef *gamedef);
|
||||
virtual ~DecorationManager() = default;
|
||||
|
||||
const char *getObjectTitle() const
|
||||
{
|
||||
return "decoration";
|
||||
}
|
||||
|
||||
static Decoration *create(DecorationType type)
|
||||
{
|
||||
switch (type) {
|
||||
case DECO_SIMPLE:
|
||||
return new DecoSimple;
|
||||
case DECO_SCHEMATIC:
|
||||
return new DecoSchematic;
|
||||
//case DECO_LSYSTEM:
|
||||
// return new DecoLSystem;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
size_t placeAllDecos(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
|
||||
};
|
483
src/mapgen/mg_ore.cpp
Normal file
483
src/mapgen/mg_ore.cpp
Normal file
|
@ -0,0 +1,483 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2015-2017 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_ore.h"
|
||||
#include "mapgen.h"
|
||||
#include "noise.h"
|
||||
#include "map.h"
|
||||
#include "log.h"
|
||||
#include "util/numeric.h"
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
FlagDesc flagdesc_ore[] = {
|
||||
{"absheight", OREFLAG_ABSHEIGHT}, // Non-functional
|
||||
{"puff_cliffs", OREFLAG_PUFF_CLIFFS},
|
||||
{"puff_additive_composition", OREFLAG_PUFF_ADDITIVE},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
OreManager::OreManager(IGameDef *gamedef) :
|
||||
ObjDefManager(gamedef, OBJDEF_ORE)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
size_t OreManager::placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
|
||||
{
|
||||
size_t nplaced = 0;
|
||||
|
||||
for (size_t i = 0; i != m_objects.size(); i++) {
|
||||
Ore *ore = (Ore *)m_objects[i];
|
||||
if (!ore)
|
||||
continue;
|
||||
|
||||
nplaced += ore->placeOre(mg, blockseed, nmin, nmax);
|
||||
blockseed++;
|
||||
}
|
||||
|
||||
return nplaced;
|
||||
}
|
||||
|
||||
|
||||
void OreManager::clear()
|
||||
{
|
||||
for (ObjDef *object : m_objects) {
|
||||
Ore *ore = (Ore *) object;
|
||||
delete ore;
|
||||
}
|
||||
m_objects.clear();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
Ore::~Ore()
|
||||
{
|
||||
delete noise;
|
||||
}
|
||||
|
||||
|
||||
void Ore::resolveNodeNames()
|
||||
{
|
||||
getIdFromNrBacklog(&c_ore, "", CONTENT_AIR);
|
||||
getIdsFromNrBacklog(&c_wherein);
|
||||
}
|
||||
|
||||
|
||||
size_t Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
|
||||
{
|
||||
if (nmin.Y > y_max || nmax.Y < y_min)
|
||||
return 0;
|
||||
|
||||
int actual_ymin = MYMAX(nmin.Y, y_min);
|
||||
int actual_ymax = MYMIN(nmax.Y, y_max);
|
||||
if (clust_size >= actual_ymax - actual_ymin + 1)
|
||||
return 0;
|
||||
|
||||
nmin.Y = actual_ymin;
|
||||
nmax.Y = actual_ymax;
|
||||
generate(mg->vm, mg->seed, blockseed, nmin, nmax, mg->biomemap);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap)
|
||||
{
|
||||
PcgRandom pr(blockseed);
|
||||
MapNode n_ore(c_ore, 0, ore_param2);
|
||||
|
||||
u32 sizex = (nmax.X - nmin.X + 1);
|
||||
u32 volume = (nmax.X - nmin.X + 1) *
|
||||
(nmax.Y - nmin.Y + 1) *
|
||||
(nmax.Z - nmin.Z + 1);
|
||||
u32 csize = clust_size;
|
||||
u32 cvolume = csize * csize * csize;
|
||||
u32 nclusters = volume / clust_scarcity;
|
||||
|
||||
for (u32 i = 0; i != nclusters; i++) {
|
||||
int x0 = pr.range(nmin.X, nmax.X - csize + 1);
|
||||
int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
|
||||
int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
|
||||
|
||||
if ((flags & OREFLAG_USE_NOISE) &&
|
||||
(NoisePerlin3D(&np, x0, y0, z0, mapseed) < nthresh))
|
||||
continue;
|
||||
|
||||
if (biomemap && !biomes.empty()) {
|
||||
u32 index = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
|
||||
std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
|
||||
if (it == biomes.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
for (u32 z1 = 0; z1 != csize; z1++)
|
||||
for (u32 y1 = 0; y1 != csize; y1++)
|
||||
for (u32 x1 = 0; x1 != csize; x1++) {
|
||||
if (pr.range(1, cvolume) > clust_num_ores)
|
||||
continue;
|
||||
|
||||
u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
|
||||
if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
|
||||
continue;
|
||||
|
||||
vm->m_data[i] = n_ore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap)
|
||||
{
|
||||
PcgRandom pr(blockseed + 4234);
|
||||
MapNode n_ore(c_ore, 0, ore_param2);
|
||||
|
||||
u16 max_height = column_height_max;
|
||||
int y_start_min = nmin.Y + max_height;
|
||||
int y_start_max = nmax.Y - max_height;
|
||||
|
||||
int y_start = y_start_min < y_start_max ?
|
||||
pr.range(y_start_min, y_start_max) :
|
||||
(y_start_min + y_start_max) / 2;
|
||||
|
||||
if (!noise) {
|
||||
int sx = nmax.X - nmin.X + 1;
|
||||
int sz = nmax.Z - nmin.Z + 1;
|
||||
noise = new Noise(&np, 0, sx, sz);
|
||||
}
|
||||
noise->seed = mapseed + y_start;
|
||||
noise->perlinMap2D(nmin.X, nmin.Z);
|
||||
|
||||
size_t index = 0;
|
||||
for (int z = nmin.Z; z <= nmax.Z; z++)
|
||||
for (int x = nmin.X; x <= nmax.X; x++, index++) {
|
||||
float noiseval = noise->result[index];
|
||||
if (noiseval < nthresh)
|
||||
continue;
|
||||
|
||||
if (biomemap && !biomes.empty()) {
|
||||
std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
|
||||
if (it == biomes.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
u16 height = pr.range(column_height_min, column_height_max);
|
||||
int ymidpoint = y_start + noiseval;
|
||||
int y0 = MYMAX(nmin.Y, ymidpoint - height * (1 - column_midpoint_factor));
|
||||
int y1 = MYMIN(nmax.Y, y0 + height - 1);
|
||||
|
||||
for (int y = y0; y <= y1; y++) {
|
||||
u32 i = vm->m_area.index(x, y, z);
|
||||
if (!vm->m_area.contains(i))
|
||||
continue;
|
||||
if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
|
||||
continue;
|
||||
|
||||
vm->m_data[i] = n_ore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
OrePuff::~OrePuff()
|
||||
{
|
||||
delete noise_puff_top;
|
||||
delete noise_puff_bottom;
|
||||
}
|
||||
|
||||
|
||||
void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap)
|
||||
{
|
||||
PcgRandom pr(blockseed + 4234);
|
||||
MapNode n_ore(c_ore, 0, ore_param2);
|
||||
|
||||
int y_start = pr.range(nmin.Y, nmax.Y);
|
||||
|
||||
if (!noise) {
|
||||
int sx = nmax.X - nmin.X + 1;
|
||||
int sz = nmax.Z - nmin.Z + 1;
|
||||
noise = new Noise(&np, 0, sx, sz);
|
||||
noise_puff_top = new Noise(&np_puff_top, 0, sx, sz);
|
||||
noise_puff_bottom = new Noise(&np_puff_bottom, 0, sx, sz);
|
||||
}
|
||||
|
||||
noise->seed = mapseed + y_start;
|
||||
noise->perlinMap2D(nmin.X, nmin.Z);
|
||||
bool noise_generated = false;
|
||||
|
||||
size_t index = 0;
|
||||
for (int z = nmin.Z; z <= nmax.Z; z++)
|
||||
for (int x = nmin.X; x <= nmax.X; x++, index++) {
|
||||
float noiseval = noise->result[index];
|
||||
if (noiseval < nthresh)
|
||||
continue;
|
||||
|
||||
if (biomemap && !biomes.empty()) {
|
||||
std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
|
||||
if (it == biomes.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!noise_generated) {
|
||||
noise_generated = true;
|
||||
noise_puff_top->perlinMap2D(nmin.X, nmin.Z);
|
||||
noise_puff_bottom->perlinMap2D(nmin.X, nmin.Z);
|
||||
}
|
||||
|
||||
float ntop = noise_puff_top->result[index];
|
||||
float nbottom = noise_puff_bottom->result[index];
|
||||
|
||||
if (!(flags & OREFLAG_PUFF_CLIFFS)) {
|
||||
float ndiff = noiseval - nthresh;
|
||||
if (ndiff < 1.0f) {
|
||||
ntop *= ndiff;
|
||||
nbottom *= ndiff;
|
||||
}
|
||||
}
|
||||
|
||||
int ymid = y_start;
|
||||
int y0 = ymid - nbottom;
|
||||
int y1 = ymid + ntop;
|
||||
|
||||
if ((flags & OREFLAG_PUFF_ADDITIVE) && (y0 > y1))
|
||||
SWAP(int, y0, y1);
|
||||
|
||||
for (int y = y0; y <= y1; y++) {
|
||||
u32 i = vm->m_area.index(x, y, z);
|
||||
if (!vm->m_area.contains(i))
|
||||
continue;
|
||||
if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
|
||||
continue;
|
||||
|
||||
vm->m_data[i] = n_ore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap)
|
||||
{
|
||||
PcgRandom pr(blockseed + 2404);
|
||||
MapNode n_ore(c_ore, 0, ore_param2);
|
||||
|
||||
u32 sizex = (nmax.X - nmin.X + 1);
|
||||
u32 volume = (nmax.X - nmin.X + 1) *
|
||||
(nmax.Y - nmin.Y + 1) *
|
||||
(nmax.Z - nmin.Z + 1);
|
||||
u32 csize = clust_size;
|
||||
u32 nblobs = volume / clust_scarcity;
|
||||
|
||||
if (!noise)
|
||||
noise = new Noise(&np, mapseed, csize, csize, csize);
|
||||
|
||||
for (u32 i = 0; i != nblobs; i++) {
|
||||
int x0 = pr.range(nmin.X, nmax.X - csize + 1);
|
||||
int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
|
||||
int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
|
||||
|
||||
if (biomemap && !biomes.empty()) {
|
||||
u32 bmapidx = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
|
||||
std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[bmapidx]);
|
||||
if (it == biomes.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
bool noise_generated = false;
|
||||
noise->seed = blockseed + i;
|
||||
|
||||
size_t index = 0;
|
||||
for (u32 z1 = 0; z1 != csize; z1++)
|
||||
for (u32 y1 = 0; y1 != csize; y1++)
|
||||
for (u32 x1 = 0; x1 != csize; x1++, index++) {
|
||||
u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
|
||||
if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
|
||||
continue;
|
||||
|
||||
// Lazily generate noise only if there's a chance of ore being placed
|
||||
// This simple optimization makes calls 6x faster on average
|
||||
if (!noise_generated) {
|
||||
noise_generated = true;
|
||||
noise->perlinMap3D(x0, y0, z0);
|
||||
}
|
||||
|
||||
float noiseval = noise->result[index];
|
||||
|
||||
float xdist = (s32)x1 - (s32)csize / 2;
|
||||
float ydist = (s32)y1 - (s32)csize / 2;
|
||||
float zdist = (s32)z1 - (s32)csize / 2;
|
||||
|
||||
noiseval -= (sqrt(xdist * xdist + ydist * ydist + zdist * zdist) / csize);
|
||||
|
||||
if (noiseval < nthresh)
|
||||
continue;
|
||||
|
||||
vm->m_data[i] = n_ore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
OreVein::~OreVein()
|
||||
{
|
||||
delete noise2;
|
||||
}
|
||||
|
||||
|
||||
void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap)
|
||||
{
|
||||
PcgRandom pr(blockseed + 520);
|
||||
MapNode n_ore(c_ore, 0, ore_param2);
|
||||
|
||||
u32 sizex = (nmax.X - nmin.X + 1);
|
||||
|
||||
if (!noise) {
|
||||
int sx = nmax.X - nmin.X + 1;
|
||||
int sy = nmax.Y - nmin.Y + 1;
|
||||
int sz = nmax.Z - nmin.Z + 1;
|
||||
noise = new Noise(&np, mapseed, sx, sy, sz);
|
||||
noise2 = new Noise(&np, mapseed + 436, sx, sy, sz);
|
||||
}
|
||||
bool noise_generated = false;
|
||||
|
||||
size_t index = 0;
|
||||
for (int z = nmin.Z; z <= nmax.Z; z++)
|
||||
for (int y = nmin.Y; y <= nmax.Y; y++)
|
||||
for (int x = nmin.X; x <= nmax.X; x++, index++) {
|
||||
u32 i = vm->m_area.index(x, y, z);
|
||||
if (!vm->m_area.contains(i))
|
||||
continue;
|
||||
if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
|
||||
continue;
|
||||
|
||||
if (biomemap && !biomes.empty()) {
|
||||
u32 bmapidx = sizex * (z - nmin.Z) + (x - nmin.X);
|
||||
std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[bmapidx]);
|
||||
if (it == biomes.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
// Same lazy generation optimization as in OreBlob
|
||||
if (!noise_generated) {
|
||||
noise_generated = true;
|
||||
noise->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
|
||||
noise2->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
|
||||
}
|
||||
|
||||
// randval ranges from -1..1
|
||||
float randval = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f;
|
||||
float noiseval = contour(noise->result[index]);
|
||||
float noiseval2 = contour(noise2->result[index]);
|
||||
if (noiseval * noiseval2 + randval * random_factor < nthresh)
|
||||
continue;
|
||||
|
||||
vm->m_data[i] = n_ore;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
OreStratum::~OreStratum()
|
||||
{
|
||||
delete noise_stratum_thickness;
|
||||
}
|
||||
|
||||
|
||||
void OreStratum::generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap)
|
||||
{
|
||||
PcgRandom pr(blockseed + 4234);
|
||||
MapNode n_ore(c_ore, 0, ore_param2);
|
||||
|
||||
if (flags & OREFLAG_USE_NOISE) {
|
||||
if (!(noise && noise_stratum_thickness)) {
|
||||
int sx = nmax.X - nmin.X + 1;
|
||||
int sz = nmax.Z - nmin.Z + 1;
|
||||
noise = new Noise(&np, 0, sx, sz);
|
||||
noise_stratum_thickness = new Noise(&np_stratum_thickness, 0, sx, sz);
|
||||
}
|
||||
noise->perlinMap2D(nmin.X, nmin.Z);
|
||||
noise_stratum_thickness->perlinMap2D(nmin.X, nmin.Z);
|
||||
}
|
||||
|
||||
size_t index = 0;
|
||||
|
||||
for (int z = nmin.Z; z <= nmax.Z; z++)
|
||||
for (int x = nmin.X; x <= nmax.X; x++, index++) {
|
||||
if (biomemap && !biomes.empty()) {
|
||||
std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
|
||||
if (it == biomes.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
int y0;
|
||||
int y1;
|
||||
|
||||
if (flags & OREFLAG_USE_NOISE) {
|
||||
float nmid = noise->result[index];
|
||||
float nhalfthick = noise_stratum_thickness->result[index] / 2.0f;
|
||||
y0 = MYMAX(nmin.Y, nmid - nhalfthick);
|
||||
y1 = MYMIN(nmax.Y, nmid + nhalfthick);
|
||||
} else {
|
||||
y0 = nmin.Y;
|
||||
y1 = nmax.Y;
|
||||
}
|
||||
|
||||
for (int y = y0; y <= y1; y++) {
|
||||
if (pr.range(1, clust_scarcity) != 1)
|
||||
continue;
|
||||
|
||||
u32 i = vm->m_area.index(x, y, z);
|
||||
if (!vm->m_area.contains(i))
|
||||
continue;
|
||||
if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
|
||||
continue;
|
||||
|
||||
vm->m_data[i] = n_ore;
|
||||
}
|
||||
}
|
||||
}
|
183
src/mapgen/mg_ore.h
Normal file
183
src/mapgen/mg_ore.h
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2015-2017 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
#include "objdef.h"
|
||||
#include "noise.h"
|
||||
#include "nodedef.h"
|
||||
|
||||
class Noise;
|
||||
class Mapgen;
|
||||
class MMVManip;
|
||||
|
||||
/////////////////// Ore generation flags
|
||||
|
||||
#define OREFLAG_ABSHEIGHT 0x01 // Non-functional but kept to not break flags
|
||||
#define OREFLAG_PUFF_CLIFFS 0x02
|
||||
#define OREFLAG_PUFF_ADDITIVE 0x04
|
||||
#define OREFLAG_USE_NOISE 0x08
|
||||
|
||||
enum OreType {
|
||||
ORE_SCATTER,
|
||||
ORE_SHEET,
|
||||
ORE_PUFF,
|
||||
ORE_BLOB,
|
||||
ORE_VEIN,
|
||||
ORE_STRATUM,
|
||||
};
|
||||
|
||||
extern FlagDesc flagdesc_ore[];
|
||||
|
||||
class Ore : public ObjDef, public NodeResolver {
|
||||
public:
|
||||
static const bool NEEDS_NOISE = false;
|
||||
|
||||
content_t c_ore; // the node to place
|
||||
std::vector<content_t> c_wherein; // the nodes to be placed in
|
||||
u32 clust_scarcity; // ore cluster has a 1-in-clust_scarcity chance of appearing at a node
|
||||
s16 clust_num_ores; // how many ore nodes are in a chunk
|
||||
s16 clust_size; // how large (in nodes) a chunk of ore is
|
||||
s16 y_min;
|
||||
s16 y_max;
|
||||
u8 ore_param2; // to set node-specific attributes
|
||||
u32 flags = 0; // attributes for this ore
|
||||
float nthresh; // threshold for noise at which an ore is placed
|
||||
NoiseParams np; // noise for distribution of clusters (NULL for uniform scattering)
|
||||
Noise *noise = nullptr;
|
||||
std::unordered_set<u8> biomes;
|
||||
|
||||
Ore() = default;;
|
||||
virtual ~Ore();
|
||||
|
||||
virtual void resolveNodeNames();
|
||||
|
||||
size_t placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
|
||||
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap) = 0;
|
||||
};
|
||||
|
||||
class OreScatter : public Ore {
|
||||
public:
|
||||
static const bool NEEDS_NOISE = false;
|
||||
|
||||
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap);
|
||||
};
|
||||
|
||||
class OreSheet : public Ore {
|
||||
public:
|
||||
static const bool NEEDS_NOISE = true;
|
||||
|
||||
u16 column_height_min;
|
||||
u16 column_height_max;
|
||||
float column_midpoint_factor;
|
||||
|
||||
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap);
|
||||
};
|
||||
|
||||
class OrePuff : public Ore {
|
||||
public:
|
||||
static const bool NEEDS_NOISE = true;
|
||||
|
||||
NoiseParams np_puff_top;
|
||||
NoiseParams np_puff_bottom;
|
||||
Noise *noise_puff_top = nullptr;
|
||||
Noise *noise_puff_bottom = nullptr;
|
||||
|
||||
OrePuff() = default;
|
||||
virtual ~OrePuff();
|
||||
|
||||
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap);
|
||||
};
|
||||
|
||||
class OreBlob : public Ore {
|
||||
public:
|
||||
static const bool NEEDS_NOISE = true;
|
||||
|
||||
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap);
|
||||
};
|
||||
|
||||
class OreVein : public Ore {
|
||||
public:
|
||||
static const bool NEEDS_NOISE = true;
|
||||
|
||||
float random_factor;
|
||||
Noise *noise2 = nullptr;
|
||||
|
||||
OreVein() = default;
|
||||
virtual ~OreVein();
|
||||
|
||||
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap);
|
||||
};
|
||||
|
||||
class OreStratum : public Ore {
|
||||
public:
|
||||
static const bool NEEDS_NOISE = false;
|
||||
|
||||
NoiseParams np_stratum_thickness;
|
||||
Noise *noise_stratum_thickness = nullptr;
|
||||
|
||||
OreStratum() = default;
|
||||
virtual ~OreStratum();
|
||||
|
||||
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
|
||||
v3s16 nmin, v3s16 nmax, u8 *biomemap);
|
||||
};
|
||||
|
||||
class OreManager : public ObjDefManager {
|
||||
public:
|
||||
OreManager(IGameDef *gamedef);
|
||||
virtual ~OreManager() = default;
|
||||
|
||||
const char *getObjectTitle() const
|
||||
{
|
||||
return "ore";
|
||||
}
|
||||
|
||||
static Ore *create(OreType type)
|
||||
{
|
||||
switch (type) {
|
||||
case ORE_SCATTER:
|
||||
return new OreScatter;
|
||||
case ORE_SHEET:
|
||||
return new OreSheet;
|
||||
case ORE_PUFF:
|
||||
return new OrePuff;
|
||||
case ORE_BLOB:
|
||||
return new OreBlob;
|
||||
case ORE_VEIN:
|
||||
return new OreVein;
|
||||
case ORE_STRATUM:
|
||||
return new OreStratum;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void clear();
|
||||
|
||||
size_t placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
|
||||
};
|
578
src/mapgen/mg_schematic.cpp
Normal file
578
src/mapgen/mg_schematic.cpp
Normal file
|
@ -0,0 +1,578 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2015-2017 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 <fstream>
|
||||
#include <typeinfo>
|
||||
#include "mg_schematic.h"
|
||||
#include "server.h"
|
||||
#include "mapgen.h"
|
||||
#include "emerge.h"
|
||||
#include "map.h"
|
||||
#include "mapblock.h"
|
||||
#include "log.h"
|
||||
#include "util/numeric.h"
|
||||
#include "util/serialize.h"
|
||||
#include "serialization.h"
|
||||
#include "filesys.h"
|
||||
#include "voxelalgorithms.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
SchematicManager::SchematicManager(Server *server) :
|
||||
ObjDefManager(server, OBJDEF_SCHEMATIC),
|
||||
m_server(server)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void SchematicManager::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);
|
||||
|
||||
try {
|
||||
DecoSchematic *dschem = dynamic_cast<DecoSchematic *>(deco);
|
||||
if (dschem)
|
||||
dschem->schematic = NULL;
|
||||
} catch (const std::bad_cast &) {
|
||||
}
|
||||
}
|
||||
|
||||
ObjDefManager::clear();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
Schematic::Schematic()
|
||||
= default;
|
||||
|
||||
|
||||
Schematic::~Schematic()
|
||||
{
|
||||
delete []schemdata;
|
||||
delete []slice_probs;
|
||||
}
|
||||
|
||||
|
||||
void Schematic::resolveNodeNames()
|
||||
{
|
||||
getIdsFromNrBacklog(&c_nodes, true, CONTENT_AIR);
|
||||
|
||||
size_t bufsize = size.X * size.Y * size.Z;
|
||||
for (size_t i = 0; i != bufsize; i++) {
|
||||
content_t c_original = schemdata[i].getContent();
|
||||
content_t c_new = c_nodes[c_original];
|
||||
schemdata[i].setContent(c_new);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Schematic::blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place)
|
||||
{
|
||||
sanity_check(m_ndef != NULL);
|
||||
|
||||
int xstride = 1;
|
||||
int ystride = size.X;
|
||||
int zstride = size.X * size.Y;
|
||||
|
||||
s16 sx = size.X;
|
||||
s16 sy = size.Y;
|
||||
s16 sz = size.Z;
|
||||
|
||||
int i_start, i_step_x, i_step_z;
|
||||
switch (rot) {
|
||||
case ROTATE_90:
|
||||
i_start = sx - 1;
|
||||
i_step_x = zstride;
|
||||
i_step_z = -xstride;
|
||||
SWAP(s16, sx, sz);
|
||||
break;
|
||||
case ROTATE_180:
|
||||
i_start = zstride * (sz - 1) + sx - 1;
|
||||
i_step_x = -xstride;
|
||||
i_step_z = -zstride;
|
||||
break;
|
||||
case ROTATE_270:
|
||||
i_start = zstride * (sz - 1);
|
||||
i_step_x = -zstride;
|
||||
i_step_z = xstride;
|
||||
SWAP(s16, sx, sz);
|
||||
break;
|
||||
default:
|
||||
i_start = 0;
|
||||
i_step_x = xstride;
|
||||
i_step_z = zstride;
|
||||
}
|
||||
|
||||
s16 y_map = p.Y;
|
||||
for (s16 y = 0; y != sy; y++) {
|
||||
if ((slice_probs[y] != MTSCHEM_PROB_ALWAYS) &&
|
||||
(slice_probs[y] <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
|
||||
continue;
|
||||
|
||||
for (s16 z = 0; z != sz; z++) {
|
||||
u32 i = z * i_step_z + y * ystride + i_start;
|
||||
for (s16 x = 0; x != sx; x++, i += i_step_x) {
|
||||
u32 vi = vm->m_area.index(p.X + x, y_map, p.Z + z);
|
||||
if (!vm->m_area.contains(vi))
|
||||
continue;
|
||||
|
||||
if (schemdata[i].getContent() == CONTENT_IGNORE)
|
||||
continue;
|
||||
|
||||
u8 placement_prob = schemdata[i].param1 & MTSCHEM_PROB_MASK;
|
||||
bool force_place_node = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
|
||||
|
||||
if (placement_prob == MTSCHEM_PROB_NEVER)
|
||||
continue;
|
||||
|
||||
if (!force_place && !force_place_node) {
|
||||
content_t c = vm->m_data[vi].getContent();
|
||||
if (c != CONTENT_AIR && c != CONTENT_IGNORE)
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((placement_prob != MTSCHEM_PROB_ALWAYS) &&
|
||||
(placement_prob <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
|
||||
continue;
|
||||
|
||||
vm->m_data[vi] = schemdata[i];
|
||||
vm->m_data[vi].param1 = 0;
|
||||
|
||||
if (rot)
|
||||
vm->m_data[vi].rotateAlongYAxis(m_ndef, rot);
|
||||
}
|
||||
}
|
||||
y_map++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Schematic::placeOnVManip(MMVManip *vm, v3s16 p, u32 flags,
|
||||
Rotation rot, bool force_place)
|
||||
{
|
||||
assert(vm != NULL);
|
||||
assert(schemdata != NULL);
|
||||
sanity_check(m_ndef != NULL);
|
||||
|
||||
//// Determine effective rotation and effective schematic dimensions
|
||||
if (rot == ROTATE_RAND)
|
||||
rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270);
|
||||
|
||||
v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ?
|
||||
v3s16(size.Z, size.Y, size.X) : size;
|
||||
|
||||
//// Adjust placement position if necessary
|
||||
if (flags & DECO_PLACE_CENTER_X)
|
||||
p.X -= (s.X + 1) / 2;
|
||||
if (flags & DECO_PLACE_CENTER_Y)
|
||||
p.Y -= (s.Y + 1) / 2;
|
||||
if (flags & DECO_PLACE_CENTER_Z)
|
||||
p.Z -= (s.Z + 1) / 2;
|
||||
|
||||
blitToVManip(vm, p, rot, force_place);
|
||||
|
||||
return vm->m_area.contains(VoxelArea(p, p + s - v3s16(1,1,1)));
|
||||
}
|
||||
|
||||
void Schematic::placeOnMap(ServerMap *map, v3s16 p, u32 flags,
|
||||
Rotation rot, bool force_place)
|
||||
{
|
||||
std::map<v3s16, MapBlock *> lighting_modified_blocks;
|
||||
std::map<v3s16, MapBlock *> modified_blocks;
|
||||
std::map<v3s16, MapBlock *>::iterator it;
|
||||
|
||||
assert(map != NULL);
|
||||
assert(schemdata != NULL);
|
||||
sanity_check(m_ndef != NULL);
|
||||
|
||||
//// Determine effective rotation and effective schematic dimensions
|
||||
if (rot == ROTATE_RAND)
|
||||
rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270);
|
||||
|
||||
v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ?
|
||||
v3s16(size.Z, size.Y, size.X) : size;
|
||||
|
||||
//// Adjust placement position if necessary
|
||||
if (flags & DECO_PLACE_CENTER_X)
|
||||
p.X -= (s.X + 1) / 2;
|
||||
if (flags & DECO_PLACE_CENTER_Y)
|
||||
p.Y -= (s.Y + 1) / 2;
|
||||
if (flags & DECO_PLACE_CENTER_Z)
|
||||
p.Z -= (s.Z + 1) / 2;
|
||||
|
||||
//// Create VManip for effected area, emerge our area, modify area
|
||||
//// inside VManip, then blit back.
|
||||
v3s16 bp1 = getNodeBlockPos(p);
|
||||
v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1));
|
||||
|
||||
MMVManip vm(map);
|
||||
vm.initialEmerge(bp1, bp2);
|
||||
|
||||
blitToVManip(&vm, p, rot, force_place);
|
||||
|
||||
voxalgo::blit_back_with_light(map, &vm, &modified_blocks);
|
||||
|
||||
//// Carry out post-map-modification actions
|
||||
|
||||
//// Create & dispatch map modification events to observers
|
||||
MapEditEvent event;
|
||||
event.type = MEET_OTHER;
|
||||
for (it = modified_blocks.begin(); it != modified_blocks.end(); ++it)
|
||||
event.modified_blocks.insert(it->first);
|
||||
|
||||
map->dispatchEvent(&event);
|
||||
}
|
||||
|
||||
|
||||
bool Schematic::deserializeFromMts(std::istream *is,
|
||||
std::vector<std::string> *names)
|
||||
{
|
||||
std::istream &ss = *is;
|
||||
content_t cignore = CONTENT_IGNORE;
|
||||
bool have_cignore = false;
|
||||
|
||||
//// Read signature
|
||||
u32 signature = readU32(ss);
|
||||
if (signature != MTSCHEM_FILE_SIGNATURE) {
|
||||
errorstream << __FUNCTION__ << ": invalid schematic "
|
||||
"file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
//// Read version
|
||||
u16 version = readU16(ss);
|
||||
if (version > MTSCHEM_FILE_VER_HIGHEST_READ) {
|
||||
errorstream << __FUNCTION__ << ": unsupported schematic "
|
||||
"file version" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
//// Read size
|
||||
size = readV3S16(ss);
|
||||
|
||||
//// Read Y-slice probability values
|
||||
delete []slice_probs;
|
||||
slice_probs = new u8[size.Y];
|
||||
for (int y = 0; y != size.Y; y++)
|
||||
slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS_OLD;
|
||||
|
||||
//// Read node names
|
||||
u16 nidmapcount = readU16(ss);
|
||||
for (int i = 0; i != nidmapcount; i++) {
|
||||
std::string name = deSerializeString(ss);
|
||||
|
||||
// Instances of "ignore" from v1 are converted to air (and instances
|
||||
// are fixed to have MTSCHEM_PROB_NEVER later on).
|
||||
if (name == "ignore") {
|
||||
name = "air";
|
||||
cignore = i;
|
||||
have_cignore = true;
|
||||
}
|
||||
|
||||
names->push_back(name);
|
||||
}
|
||||
|
||||
//// Read node data
|
||||
size_t nodecount = size.X * size.Y * size.Z;
|
||||
|
||||
delete []schemdata;
|
||||
schemdata = new MapNode[nodecount];
|
||||
|
||||
MapNode::deSerializeBulk(ss, SER_FMT_VER_HIGHEST_READ, schemdata,
|
||||
nodecount, 2, 2, true);
|
||||
|
||||
// Fix probability values for nodes that were ignore; removed in v2
|
||||
if (version < 2) {
|
||||
for (size_t i = 0; i != nodecount; i++) {
|
||||
if (schemdata[i].param1 == 0)
|
||||
schemdata[i].param1 = MTSCHEM_PROB_ALWAYS_OLD;
|
||||
if (have_cignore && schemdata[i].getContent() == cignore)
|
||||
schemdata[i].param1 = MTSCHEM_PROB_NEVER;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix probability values for probability range truncation introduced in v4
|
||||
if (version < 4) {
|
||||
for (s16 y = 0; y != size.Y; y++)
|
||||
slice_probs[y] >>= 1;
|
||||
for (size_t i = 0; i != nodecount; i++)
|
||||
schemdata[i].param1 >>= 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Schematic::serializeToMts(std::ostream *os,
|
||||
const std::vector<std::string> &names)
|
||||
{
|
||||
std::ostream &ss = *os;
|
||||
|
||||
writeU32(ss, MTSCHEM_FILE_SIGNATURE); // signature
|
||||
writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version
|
||||
writeV3S16(ss, size); // schematic size
|
||||
|
||||
for (int y = 0; y != size.Y; y++) // Y slice probabilities
|
||||
writeU8(ss, slice_probs[y]);
|
||||
|
||||
writeU16(ss, names.size()); // name count
|
||||
for (size_t i = 0; i != names.size(); i++)
|
||||
ss << serializeString(names[i]); // node names
|
||||
|
||||
// compressed bulk node data
|
||||
MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE,
|
||||
schemdata, size.X * size.Y * size.Z, 2, 2, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Schematic::serializeToLua(std::ostream *os,
|
||||
const std::vector<std::string> &names, bool use_comments, u32 indent_spaces)
|
||||
{
|
||||
std::ostream &ss = *os;
|
||||
|
||||
std::string indent("\t");
|
||||
if (indent_spaces > 0)
|
||||
indent.assign(indent_spaces, ' ');
|
||||
|
||||
//// Write header
|
||||
{
|
||||
ss << "schematic = {" << std::endl;
|
||||
ss << indent << "size = "
|
||||
<< "{x=" << size.X
|
||||
<< ", y=" << size.Y
|
||||
<< ", z=" << size.Z
|
||||
<< "}," << std::endl;
|
||||
}
|
||||
|
||||
//// Write y-slice probabilities
|
||||
{
|
||||
ss << indent << "yslice_prob = {" << std::endl;
|
||||
|
||||
for (u16 y = 0; y != size.Y; y++) {
|
||||
u8 probability = slice_probs[y] & MTSCHEM_PROB_MASK;
|
||||
|
||||
ss << indent << indent << "{"
|
||||
<< "ypos=" << y
|
||||
<< ", prob=" << (u16)probability * 2
|
||||
<< "}," << std::endl;
|
||||
}
|
||||
|
||||
ss << indent << "}," << std::endl;
|
||||
}
|
||||
|
||||
//// Write node data
|
||||
{
|
||||
ss << indent << "data = {" << std::endl;
|
||||
|
||||
u32 i = 0;
|
||||
for (u16 z = 0; z != size.Z; z++)
|
||||
for (u16 y = 0; y != size.Y; y++) {
|
||||
if (use_comments) {
|
||||
ss << std::endl
|
||||
<< indent << indent
|
||||
<< "-- z=" << z
|
||||
<< ", y=" << y << std::endl;
|
||||
}
|
||||
|
||||
for (u16 x = 0; x != size.X; x++, i++) {
|
||||
u8 probability = schemdata[i].param1 & MTSCHEM_PROB_MASK;
|
||||
bool force_place = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
|
||||
|
||||
ss << indent << indent << "{"
|
||||
<< "name=\"" << names[schemdata[i].getContent()]
|
||||
<< "\", prob=" << (u16)probability * 2
|
||||
<< ", param2=" << (u16)schemdata[i].param2;
|
||||
|
||||
if (force_place)
|
||||
ss << ", force_place=true";
|
||||
|
||||
ss << "}," << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
ss << indent << "}," << std::endl;
|
||||
}
|
||||
|
||||
ss << "}" << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Schematic::loadSchematicFromFile(const std::string &filename,
|
||||
INodeDefManager *ndef, StringMap *replace_names)
|
||||
{
|
||||
std::ifstream is(filename.c_str(), std::ios_base::binary);
|
||||
if (!is.good()) {
|
||||
errorstream << __FUNCTION__ << ": unable to open file '"
|
||||
<< filename << "'" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t origsize = m_nodenames.size();
|
||||
if (!deserializeFromMts(&is, &m_nodenames))
|
||||
return false;
|
||||
|
||||
m_nnlistsizes.push_back(m_nodenames.size() - origsize);
|
||||
|
||||
name = filename;
|
||||
|
||||
if (replace_names) {
|
||||
for (size_t i = origsize; i < m_nodenames.size(); i++) {
|
||||
std::string &node_name = m_nodenames[i];
|
||||
StringMap::iterator it = replace_names->find(node_name);
|
||||
if (it != replace_names->end())
|
||||
node_name = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (ndef)
|
||||
ndef->pendNodeResolve(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Schematic::saveSchematicToFile(const std::string &filename,
|
||||
INodeDefManager *ndef)
|
||||
{
|
||||
MapNode *orig_schemdata = schemdata;
|
||||
std::vector<std::string> ndef_nodenames;
|
||||
std::vector<std::string> *names;
|
||||
|
||||
if (m_resolve_done && ndef == NULL)
|
||||
ndef = m_ndef;
|
||||
|
||||
if (ndef) {
|
||||
names = &ndef_nodenames;
|
||||
|
||||
u32 volume = size.X * size.Y * size.Z;
|
||||
schemdata = new MapNode[volume];
|
||||
for (u32 i = 0; i != volume; i++)
|
||||
schemdata[i] = orig_schemdata[i];
|
||||
|
||||
generate_nodelist_and_update_ids(schemdata, volume, names, ndef);
|
||||
} else { // otherwise, use the names we have on hand in the list
|
||||
names = &m_nodenames;
|
||||
}
|
||||
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
bool status = serializeToMts(&os, *names);
|
||||
|
||||
if (ndef) {
|
||||
delete []schemdata;
|
||||
schemdata = orig_schemdata;
|
||||
}
|
||||
|
||||
if (!status)
|
||||
return false;
|
||||
|
||||
return fs::safeWriteToFile(filename, os.str());
|
||||
}
|
||||
|
||||
|
||||
bool Schematic::getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2)
|
||||
{
|
||||
MMVManip *vm = new MMVManip(map);
|
||||
|
||||
v3s16 bp1 = getNodeBlockPos(p1);
|
||||
v3s16 bp2 = getNodeBlockPos(p2);
|
||||
vm->initialEmerge(bp1, bp2);
|
||||
|
||||
size = p2 - p1 + 1;
|
||||
|
||||
slice_probs = new u8[size.Y];
|
||||
for (s16 y = 0; y != size.Y; y++)
|
||||
slice_probs[y] = MTSCHEM_PROB_ALWAYS;
|
||||
|
||||
schemdata = new MapNode[size.X * size.Y * size.Z];
|
||||
|
||||
u32 i = 0;
|
||||
for (s16 z = p1.Z; z <= p2.Z; z++)
|
||||
for (s16 y = p1.Y; y <= p2.Y; y++) {
|
||||
u32 vi = vm->m_area.index(p1.X, y, z);
|
||||
for (s16 x = p1.X; x <= p2.X; x++, i++, vi++) {
|
||||
schemdata[i] = vm->m_data[vi];
|
||||
schemdata[i].param1 = MTSCHEM_PROB_ALWAYS;
|
||||
}
|
||||
}
|
||||
|
||||
delete vm;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Schematic::applyProbabilities(v3s16 p0,
|
||||
std::vector<std::pair<v3s16, u8> > *plist,
|
||||
std::vector<std::pair<s16, u8> > *splist)
|
||||
{
|
||||
for (size_t i = 0; i != plist->size(); i++) {
|
||||
v3s16 p = (*plist)[i].first - p0;
|
||||
int index = p.Z * (size.Y * size.X) + p.Y * size.X + p.X;
|
||||
if (index < size.Z * size.Y * size.X) {
|
||||
u8 prob = (*plist)[i].second;
|
||||
schemdata[index].param1 = prob;
|
||||
|
||||
// trim unnecessary node names from schematic
|
||||
if (prob == MTSCHEM_PROB_NEVER)
|
||||
schemdata[index].setContent(CONTENT_AIR);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i != splist->size(); i++) {
|
||||
s16 y = (*splist)[i].first - p0.Y;
|
||||
slice_probs[y] = (*splist)[i].second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount,
|
||||
std::vector<std::string> *usednodes, INodeDefManager *ndef)
|
||||
{
|
||||
std::unordered_map<content_t, content_t> nodeidmap;
|
||||
content_t numids = 0;
|
||||
|
||||
for (size_t i = 0; i != nodecount; i++) {
|
||||
content_t id;
|
||||
content_t c = nodes[i].getContent();
|
||||
|
||||
std::unordered_map<content_t, content_t>::const_iterator it = nodeidmap.find(c);
|
||||
if (it == nodeidmap.end()) {
|
||||
id = numids;
|
||||
numids++;
|
||||
|
||||
usednodes->push_back(ndef->get(c).name);
|
||||
nodeidmap.insert(std::make_pair(c, id));
|
||||
} else {
|
||||
id = it->second;
|
||||
}
|
||||
nodes[i].setContent(id);
|
||||
}
|
||||
}
|
147
src/mapgen/mg_schematic.h
Normal file
147
src/mapgen/mg_schematic.h
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
|
||||
Copyright (C) 2015-2017 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include "mg_decoration.h"
|
||||
#include "util/string.h"
|
||||
|
||||
class Map;
|
||||
class ServerMap;
|
||||
class Mapgen;
|
||||
class MMVManip;
|
||||
class PseudoRandom;
|
||||
class NodeResolver;
|
||||
class Server;
|
||||
|
||||
/*
|
||||
Minetest Schematic File Format
|
||||
|
||||
All values are stored in big-endian byte order.
|
||||
[u32] signature: 'MTSM'
|
||||
[u16] version: 4
|
||||
[u16] size X
|
||||
[u16] size Y
|
||||
[u16] size Z
|
||||
For each Y:
|
||||
[u8] slice probability value
|
||||
[Name-ID table] Name ID Mapping Table
|
||||
[u16] name-id count
|
||||
For each name-id mapping:
|
||||
[u16] name length
|
||||
[u8[]] name
|
||||
ZLib deflated {
|
||||
For each node in schematic: (for z, y, x)
|
||||
[u16] content
|
||||
For each node in schematic:
|
||||
[u8] param1
|
||||
bit 0-6: probability
|
||||
bit 7: specific node force placement
|
||||
For each node in schematic:
|
||||
[u8] param2
|
||||
}
|
||||
|
||||
Version changes:
|
||||
1 - Initial version
|
||||
2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
|
||||
3 - Added y-slice probabilities; this allows for variable height structures
|
||||
4 - Compressed range of node occurence prob., added per-node force placement bit
|
||||
*/
|
||||
|
||||
//// Schematic constants
|
||||
#define MTSCHEM_FILE_SIGNATURE 0x4d54534d // 'MTSM'
|
||||
#define MTSCHEM_FILE_VER_HIGHEST_READ 4
|
||||
#define MTSCHEM_FILE_VER_HIGHEST_WRITE 4
|
||||
|
||||
#define MTSCHEM_PROB_MASK 0x7F
|
||||
|
||||
#define MTSCHEM_PROB_NEVER 0x00
|
||||
#define MTSCHEM_PROB_ALWAYS 0x7F
|
||||
#define MTSCHEM_PROB_ALWAYS_OLD 0xFF
|
||||
|
||||
#define MTSCHEM_FORCE_PLACE 0x80
|
||||
|
||||
enum SchematicType
|
||||
{
|
||||
SCHEMATIC_NORMAL,
|
||||
};
|
||||
|
||||
enum SchematicFormatType {
|
||||
SCHEM_FMT_HANDLE,
|
||||
SCHEM_FMT_MTS,
|
||||
SCHEM_FMT_LUA,
|
||||
};
|
||||
|
||||
class Schematic : public ObjDef, public NodeResolver {
|
||||
public:
|
||||
Schematic();
|
||||
virtual ~Schematic();
|
||||
|
||||
virtual void resolveNodeNames();
|
||||
|
||||
bool loadSchematicFromFile(const std::string &filename, INodeDefManager *ndef,
|
||||
StringMap *replace_names=NULL);
|
||||
bool saveSchematicToFile(const std::string &filename, INodeDefManager *ndef);
|
||||
bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2);
|
||||
|
||||
bool deserializeFromMts(std::istream *is, std::vector<std::string> *names);
|
||||
bool serializeToMts(std::ostream *os, const std::vector<std::string> &names);
|
||||
bool serializeToLua(std::ostream *os, const std::vector<std::string> &names,
|
||||
bool use_comments, u32 indent_spaces);
|
||||
|
||||
void blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place);
|
||||
bool placeOnVManip(MMVManip *vm, v3s16 p, u32 flags, Rotation rot, bool force_place);
|
||||
void placeOnMap(ServerMap *map, v3s16 p, u32 flags, Rotation rot, bool force_place);
|
||||
|
||||
void applyProbabilities(v3s16 p0,
|
||||
std::vector<std::pair<v3s16, u8> > *plist,
|
||||
std::vector<std::pair<s16, u8> > *splist);
|
||||
|
||||
std::vector<content_t> c_nodes;
|
||||
u32 flags = 0;
|
||||
v3s16 size;
|
||||
MapNode *schemdata = nullptr;
|
||||
u8 *slice_probs = nullptr;
|
||||
};
|
||||
|
||||
class SchematicManager : public ObjDefManager {
|
||||
public:
|
||||
SchematicManager(Server *server);
|
||||
virtual ~SchematicManager() = default;
|
||||
|
||||
virtual void clear();
|
||||
|
||||
const char *getObjectTitle() const
|
||||
{
|
||||
return "schematic";
|
||||
}
|
||||
|
||||
static Schematic *create(SchematicType type)
|
||||
{
|
||||
return new Schematic;
|
||||
}
|
||||
|
||||
private:
|
||||
Server *m_server;
|
||||
};
|
||||
|
||||
void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount,
|
||||
std::vector<std::string> *usednodes, INodeDefManager *ndef);
|
872
src/mapgen/treegen.cpp
Normal file
872
src/mapgen/treegen.cpp
Normal file
|
@ -0,0 +1,872 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>,
|
||||
2012-2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
|
||||
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 "irr_v3d.h"
|
||||
#include <stack>
|
||||
#include "util/pointer.h"
|
||||
#include "util/numeric.h"
|
||||
#include "map.h"
|
||||
#include "mapblock.h"
|
||||
#include "serverenvironment.h"
|
||||
#include "nodedef.h"
|
||||
#include "treegen.h"
|
||||
#include "voxelalgorithms.h"
|
||||
|
||||
namespace treegen
|
||||
{
|
||||
|
||||
void make_tree(MMVManip &vmanip, v3s16 p0,
|
||||
bool is_apple_tree, INodeDefManager *ndef, s32 seed)
|
||||
{
|
||||
/*
|
||||
NOTE: Tree-placing code is currently duplicated in the engine
|
||||
and in games that have saplings; both are deprecated but not
|
||||
replaced yet
|
||||
*/
|
||||
MapNode treenode(ndef->getId("mapgen_tree"));
|
||||
MapNode leavesnode(ndef->getId("mapgen_leaves"));
|
||||
MapNode applenode(ndef->getId("mapgen_apple"));
|
||||
|
||||
PseudoRandom pr(seed);
|
||||
s16 trunk_h = pr.range(4, 5);
|
||||
v3s16 p1 = p0;
|
||||
for (s16 ii = 0; ii < trunk_h; ii++) {
|
||||
if (vmanip.m_area.contains(p1)) {
|
||||
u32 vi = vmanip.m_area.index(p1);
|
||||
vmanip.m_data[vi] = treenode;
|
||||
}
|
||||
p1.Y++;
|
||||
}
|
||||
|
||||
// p1 is now the last piece of the trunk
|
||||
p1.Y -= 1;
|
||||
|
||||
VoxelArea leaves_a(v3s16(-2, -1, -2), v3s16(2, 2, 2));
|
||||
Buffer<u8> leaves_d(leaves_a.getVolume());
|
||||
for (s32 i = 0; i < leaves_a.getVolume(); i++)
|
||||
leaves_d[i] = 0;
|
||||
|
||||
// Force leaves at near the end of the trunk
|
||||
s16 d = 1;
|
||||
for (s16 z = -d; z <= d; z++)
|
||||
for (s16 y = -d; y <= d; y++)
|
||||
for (s16 x = -d; x <= d; x++) {
|
||||
leaves_d[leaves_a.index(v3s16(x, y, z))] = 1;
|
||||
}
|
||||
|
||||
// Add leaves randomly
|
||||
for (u32 iii = 0; iii < 7; iii++) {
|
||||
v3s16 p(
|
||||
pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
|
||||
pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
|
||||
pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z - d)
|
||||
);
|
||||
|
||||
for (s16 z = 0; z <= d; z++)
|
||||
for (s16 y = 0; y <= d; y++)
|
||||
for (s16 x = 0; x <= d; x++) {
|
||||
leaves_d[leaves_a.index(p + v3s16(x, y, z))] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Blit leaves to vmanip
|
||||
for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
|
||||
for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
|
||||
v3s16 pmin(leaves_a.MinEdge.X, y, z);
|
||||
u32 i = leaves_a.index(pmin);
|
||||
u32 vi = vmanip.m_area.index(pmin + p1);
|
||||
for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
|
||||
v3s16 p(x, y, z);
|
||||
if (vmanip.m_area.contains(p + p1) &&
|
||||
(vmanip.m_data[vi].getContent() == CONTENT_AIR ||
|
||||
vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
|
||||
if (leaves_d[i] == 1) {
|
||||
bool is_apple = pr.range(0, 99) < 10;
|
||||
if (is_apple_tree && is_apple)
|
||||
vmanip.m_data[vi] = applenode;
|
||||
else
|
||||
vmanip.m_data[vi] = leavesnode;
|
||||
}
|
||||
}
|
||||
vi++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// L-System tree LUA spawner
|
||||
treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0,
|
||||
INodeDefManager *ndef, const TreeDef &tree_definition)
|
||||
{
|
||||
ServerMap *map = &env->getServerMap();
|
||||
std::map<v3s16, MapBlock*> modified_blocks;
|
||||
MMVManip vmanip(map);
|
||||
v3s16 tree_blockp = getNodeBlockPos(p0);
|
||||
treegen::error e;
|
||||
|
||||
vmanip.initialEmerge(tree_blockp - v3s16(1, 1, 1), tree_blockp + v3s16(1, 3, 1));
|
||||
e = make_ltree(vmanip, p0, ndef, tree_definition);
|
||||
if (e != SUCCESS)
|
||||
return e;
|
||||
|
||||
voxalgo::blit_back_with_light(map, &vmanip, &modified_blocks);
|
||||
|
||||
// Send a MEET_OTHER event
|
||||
MapEditEvent event;
|
||||
event.type = MEET_OTHER;
|
||||
for (auto &modified_block : modified_blocks)
|
||||
event.modified_blocks.insert(modified_block.first);
|
||||
map->dispatchEvent(&event);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
//L-System tree generator
|
||||
treegen::error make_ltree(MMVManip &vmanip, v3s16 p0,
|
||||
INodeDefManager *ndef, TreeDef tree_definition)
|
||||
{
|
||||
MapNode dirtnode(ndef->getId("mapgen_dirt"));
|
||||
s32 seed;
|
||||
if (tree_definition.explicit_seed)
|
||||
seed = tree_definition.seed + 14002;
|
||||
else
|
||||
seed = p0.X * 2 + p0.Y * 4 + p0.Z; // use the tree position to seed PRNG
|
||||
PseudoRandom ps(seed);
|
||||
|
||||
// chance of inserting abcd rules
|
||||
double prop_a = 9;
|
||||
double prop_b = 8;
|
||||
double prop_c = 7;
|
||||
double prop_d = 6;
|
||||
|
||||
//randomize tree growth level, minimum=2
|
||||
s16 iterations = tree_definition.iterations;
|
||||
if (tree_definition.iterations_random_level > 0)
|
||||
iterations -= ps.range(0, tree_definition.iterations_random_level);
|
||||
if (iterations < 2)
|
||||
iterations = 2;
|
||||
|
||||
s16 MAX_ANGLE_OFFSET = 5;
|
||||
double angle_in_radians = (double)tree_definition.angle * M_PI / 180;
|
||||
double angleOffset_in_radians = (s16)(ps.range(0, 1) % MAX_ANGLE_OFFSET) * M_PI / 180;
|
||||
|
||||
//initialize rotation matrix, position and stacks for branches
|
||||
core::matrix4 rotation;
|
||||
rotation = setRotationAxisRadians(rotation, M_PI / 2, v3f(0, 0, 1));
|
||||
v3f position;
|
||||
position.X = p0.X;
|
||||
position.Y = p0.Y;
|
||||
position.Z = p0.Z;
|
||||
std::stack <core::matrix4> stack_orientation;
|
||||
std::stack <v3f> stack_position;
|
||||
|
||||
//generate axiom
|
||||
std::string axiom = tree_definition.initial_axiom;
|
||||
for (s16 i = 0; i < iterations; i++) {
|
||||
std::string temp;
|
||||
for (s16 j = 0; j < (s16)axiom.size(); j++) {
|
||||
char axiom_char = axiom.at(j);
|
||||
switch (axiom_char) {
|
||||
case 'A':
|
||||
temp += tree_definition.rules_a;
|
||||
break;
|
||||
case 'B':
|
||||
temp += tree_definition.rules_b;
|
||||
break;
|
||||
case 'C':
|
||||
temp += tree_definition.rules_c;
|
||||
break;
|
||||
case 'D':
|
||||
temp += tree_definition.rules_d;
|
||||
break;
|
||||
case 'a':
|
||||
if (prop_a >= ps.range(1, 10))
|
||||
temp += tree_definition.rules_a;
|
||||
break;
|
||||
case 'b':
|
||||
if (prop_b >= ps.range(1, 10))
|
||||
temp += tree_definition.rules_b;
|
||||
break;
|
||||
case 'c':
|
||||
if (prop_c >= ps.range(1, 10))
|
||||
temp += tree_definition.rules_c;
|
||||
break;
|
||||
case 'd':
|
||||
if (prop_d >= ps.range(1, 10))
|
||||
temp += tree_definition.rules_d;
|
||||
break;
|
||||
default:
|
||||
temp += axiom_char;
|
||||
break;
|
||||
}
|
||||
}
|
||||
axiom = temp;
|
||||
}
|
||||
|
||||
//make sure tree is not floating in the air
|
||||
if (tree_definition.trunk_type == "double") {
|
||||
tree_node_placement(
|
||||
vmanip,
|
||||
v3f(position.X + 1, position.Y - 1, position.Z),
|
||||
dirtnode
|
||||
);
|
||||
tree_node_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y - 1, position.Z + 1),
|
||||
dirtnode
|
||||
);
|
||||
tree_node_placement(
|
||||
vmanip,
|
||||
v3f(position.X + 1, position.Y - 1, position.Z + 1),
|
||||
dirtnode
|
||||
);
|
||||
} else if (tree_definition.trunk_type == "crossed") {
|
||||
tree_node_placement(
|
||||
vmanip,
|
||||
v3f(position.X + 1, position.Y - 1, position.Z),
|
||||
dirtnode
|
||||
);
|
||||
tree_node_placement(
|
||||
vmanip,
|
||||
v3f(position.X - 1, position.Y - 1, position.Z),
|
||||
dirtnode
|
||||
);
|
||||
tree_node_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y - 1, position.Z + 1),
|
||||
dirtnode
|
||||
);
|
||||
tree_node_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y - 1, position.Z - 1),
|
||||
dirtnode
|
||||
);
|
||||
}
|
||||
|
||||
/* build tree out of generated axiom
|
||||
|
||||
Key for Special L-System Symbols used in Axioms
|
||||
|
||||
G - move forward one unit with the pen up
|
||||
F - move forward one unit with the pen down drawing trunks and branches
|
||||
f - move forward one unit with the pen down drawing leaves (100% chance)
|
||||
T - move forward one unit with the pen down drawing trunks only
|
||||
R - move forward one unit with the pen down placing fruit
|
||||
A - replace with rules set A
|
||||
B - replace with rules set B
|
||||
C - replace with rules set C
|
||||
D - replace with rules set D
|
||||
a - replace with rules set A, chance 90%
|
||||
b - replace with rules set B, chance 80%
|
||||
c - replace with rules set C, chance 70%
|
||||
d - replace with rules set D, chance 60%
|
||||
+ - yaw the turtle right by angle degrees
|
||||
- - yaw the turtle left by angle degrees
|
||||
& - pitch the turtle down by angle degrees
|
||||
^ - pitch the turtle up by angle degrees
|
||||
/ - roll the turtle to the right by angle degrees
|
||||
* - roll the turtle to the left by angle degrees
|
||||
[ - save in stack current state info
|
||||
] - recover from stack state info
|
||||
|
||||
*/
|
||||
|
||||
s16 x,y,z;
|
||||
for (s16 i = 0; i < (s16)axiom.size(); i++) {
|
||||
char axiom_char = axiom.at(i);
|
||||
core::matrix4 temp_rotation;
|
||||
temp_rotation.makeIdentity();
|
||||
v3f dir;
|
||||
switch (axiom_char) {
|
||||
case 'G':
|
||||
dir = v3f(1, 0, 0);
|
||||
dir = transposeMatrix(rotation, dir);
|
||||
position += dir;
|
||||
break;
|
||||
case 'T':
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y, position.Z),
|
||||
tree_definition
|
||||
);
|
||||
if (tree_definition.trunk_type == "double" &&
|
||||
!tree_definition.thin_branches) {
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X + 1, position.Y, position.Z),
|
||||
tree_definition
|
||||
);
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y, position.Z + 1),
|
||||
tree_definition
|
||||
);
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X + 1, position.Y, position.Z + 1),
|
||||
tree_definition
|
||||
);
|
||||
} else if (tree_definition.trunk_type == "crossed" &&
|
||||
!tree_definition.thin_branches) {
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X + 1, position.Y, position.Z),
|
||||
tree_definition
|
||||
);
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X - 1, position.Y, position.Z),
|
||||
tree_definition
|
||||
);
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y, position.Z + 1),
|
||||
tree_definition
|
||||
);
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y, position.Z - 1),
|
||||
tree_definition
|
||||
);
|
||||
}
|
||||
dir = v3f(1, 0, 0);
|
||||
dir = transposeMatrix(rotation, dir);
|
||||
position += dir;
|
||||
break;
|
||||
case 'F':
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y, position.Z),
|
||||
tree_definition
|
||||
);
|
||||
if ((stack_orientation.empty() &&
|
||||
tree_definition.trunk_type == "double") ||
|
||||
(!stack_orientation.empty() &&
|
||||
tree_definition.trunk_type == "double" &&
|
||||
!tree_definition.thin_branches)) {
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X +1 , position.Y, position.Z),
|
||||
tree_definition
|
||||
);
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y, position.Z + 1),
|
||||
tree_definition
|
||||
);
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X + 1, position.Y, position.Z + 1),
|
||||
tree_definition
|
||||
);
|
||||
} else if ((stack_orientation.empty() &&
|
||||
tree_definition.trunk_type == "crossed") ||
|
||||
(!stack_orientation.empty() &&
|
||||
tree_definition.trunk_type == "crossed" &&
|
||||
!tree_definition.thin_branches)) {
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X + 1, position.Y, position.Z),
|
||||
tree_definition
|
||||
);
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X - 1, position.Y, position.Z),
|
||||
tree_definition
|
||||
);
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y, position.Z + 1),
|
||||
tree_definition
|
||||
);
|
||||
tree_trunk_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y, position.Z - 1),
|
||||
tree_definition
|
||||
);
|
||||
} if (!stack_orientation.empty()) {
|
||||
s16 size = 1;
|
||||
for (x = -size; x <= size; x++)
|
||||
for (y = -size; y <= size; y++)
|
||||
for (z = -size; z <= size; z++) {
|
||||
if (abs(x) == size &&
|
||||
abs(y) == size &&
|
||||
abs(z) == size) {
|
||||
tree_leaves_placement(
|
||||
vmanip,
|
||||
v3f(position.X + x + 1, position.Y + y,
|
||||
position.Z + z),
|
||||
ps.next(),
|
||||
tree_definition
|
||||
);
|
||||
tree_leaves_placement(
|
||||
vmanip,
|
||||
v3f(position.X + x - 1, position.Y + y,
|
||||
position.Z + z),
|
||||
ps.next(),
|
||||
tree_definition
|
||||
);
|
||||
tree_leaves_placement(
|
||||
vmanip,v3f(position.X + x, position.Y + y,
|
||||
position.Z + z + 1),
|
||||
ps.next(),
|
||||
tree_definition
|
||||
);
|
||||
tree_leaves_placement(
|
||||
vmanip,v3f(position.X + x, position.Y + y,
|
||||
position.Z + z - 1),
|
||||
ps.next(),
|
||||
tree_definition
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
dir = v3f(1, 0, 0);
|
||||
dir = transposeMatrix(rotation, dir);
|
||||
position += dir;
|
||||
break;
|
||||
case 'f':
|
||||
tree_single_leaves_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y, position.Z),
|
||||
ps.next(),
|
||||
tree_definition
|
||||
);
|
||||
dir = v3f(1, 0, 0);
|
||||
dir = transposeMatrix(rotation, dir);
|
||||
position += dir;
|
||||
break;
|
||||
case 'R':
|
||||
tree_fruit_placement(
|
||||
vmanip,
|
||||
v3f(position.X, position.Y, position.Z),
|
||||
tree_definition
|
||||
);
|
||||
dir = v3f(1, 0, 0);
|
||||
dir = transposeMatrix(rotation, dir);
|
||||
position += dir;
|
||||
break;
|
||||
|
||||
// turtle orientation commands
|
||||
case '[':
|
||||
stack_orientation.push(rotation);
|
||||
stack_position.push(position);
|
||||
break;
|
||||
case ']':
|
||||
if (stack_orientation.empty())
|
||||
return UNBALANCED_BRACKETS;
|
||||
rotation = stack_orientation.top();
|
||||
stack_orientation.pop();
|
||||
position = stack_position.top();
|
||||
stack_position.pop();
|
||||
break;
|
||||
case '+':
|
||||
temp_rotation.makeIdentity();
|
||||
temp_rotation = setRotationAxisRadians(temp_rotation,
|
||||
angle_in_radians + angleOffset_in_radians, v3f(0, 0, 1));
|
||||
rotation *= temp_rotation;
|
||||
break;
|
||||
case '-':
|
||||
temp_rotation.makeIdentity();
|
||||
temp_rotation = setRotationAxisRadians(temp_rotation,
|
||||
angle_in_radians + angleOffset_in_radians, v3f(0, 0, -1));
|
||||
rotation *= temp_rotation;
|
||||
break;
|
||||
case '&':
|
||||
temp_rotation.makeIdentity();
|
||||
temp_rotation = setRotationAxisRadians(temp_rotation,
|
||||
angle_in_radians + angleOffset_in_radians, v3f(0, 1, 0));
|
||||
rotation *= temp_rotation;
|
||||
break;
|
||||
case '^':
|
||||
temp_rotation.makeIdentity();
|
||||
temp_rotation = setRotationAxisRadians(temp_rotation,
|
||||
angle_in_radians + angleOffset_in_radians, v3f(0, -1, 0));
|
||||
rotation *= temp_rotation;
|
||||
break;
|
||||
case '*':
|
||||
temp_rotation.makeIdentity();
|
||||
temp_rotation = setRotationAxisRadians(temp_rotation,
|
||||
angle_in_radians, v3f(1, 0, 0));
|
||||
rotation *= temp_rotation;
|
||||
break;
|
||||
case '/':
|
||||
temp_rotation.makeIdentity();
|
||||
temp_rotation = setRotationAxisRadians(temp_rotation,
|
||||
angle_in_radians, v3f(-1, 0, 0));
|
||||
rotation *= temp_rotation;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void tree_node_placement(MMVManip &vmanip, v3f p0, MapNode node)
|
||||
{
|
||||
v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
|
||||
if (!vmanip.m_area.contains(p1))
|
||||
return;
|
||||
u32 vi = vmanip.m_area.index(p1);
|
||||
if (vmanip.m_data[vi].getContent() != CONTENT_AIR
|
||||
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
|
||||
return;
|
||||
vmanip.m_data[vmanip.m_area.index(p1)] = node;
|
||||
}
|
||||
|
||||
|
||||
void tree_trunk_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
|
||||
{
|
||||
v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
|
||||
if (!vmanip.m_area.contains(p1))
|
||||
return;
|
||||
u32 vi = vmanip.m_area.index(p1);
|
||||
content_t current_node = vmanip.m_data[vi].getContent();
|
||||
if (current_node != CONTENT_AIR && current_node != CONTENT_IGNORE
|
||||
&& current_node != tree_definition.leavesnode.getContent()
|
||||
&& current_node != tree_definition.leaves2node.getContent()
|
||||
&& current_node != tree_definition.fruitnode.getContent())
|
||||
return;
|
||||
vmanip.m_data[vi] = tree_definition.trunknode;
|
||||
}
|
||||
|
||||
|
||||
void tree_leaves_placement(MMVManip &vmanip, v3f p0,
|
||||
PseudoRandom ps, TreeDef &tree_definition)
|
||||
{
|
||||
MapNode leavesnode = tree_definition.leavesnode;
|
||||
if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
|
||||
leavesnode = tree_definition.leaves2node;
|
||||
v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
|
||||
if (!vmanip.m_area.contains(p1))
|
||||
return;
|
||||
u32 vi = vmanip.m_area.index(p1);
|
||||
if (vmanip.m_data[vi].getContent() != CONTENT_AIR
|
||||
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
|
||||
return;
|
||||
if (tree_definition.fruit_chance > 0) {
|
||||
if (ps.range(1, 100) > 100 - tree_definition.fruit_chance)
|
||||
vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
|
||||
else
|
||||
vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
|
||||
} else if (ps.range(1, 100) > 20) {
|
||||
vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void tree_single_leaves_placement(MMVManip &vmanip, v3f p0,
|
||||
PseudoRandom ps, TreeDef &tree_definition)
|
||||
{
|
||||
MapNode leavesnode = tree_definition.leavesnode;
|
||||
if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
|
||||
leavesnode = tree_definition.leaves2node;
|
||||
v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
|
||||
if (!vmanip.m_area.contains(p1))
|
||||
return;
|
||||
u32 vi = vmanip.m_area.index(p1);
|
||||
if (vmanip.m_data[vi].getContent() != CONTENT_AIR
|
||||
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
|
||||
return;
|
||||
vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
|
||||
}
|
||||
|
||||
|
||||
void tree_fruit_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
|
||||
{
|
||||
v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
|
||||
if (!vmanip.m_area.contains(p1))
|
||||
return;
|
||||
u32 vi = vmanip.m_area.index(p1);
|
||||
if (vmanip.m_data[vi].getContent() != CONTENT_AIR
|
||||
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
|
||||
return;
|
||||
vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
|
||||
}
|
||||
|
||||
|
||||
irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
|
||||
{
|
||||
double c = cos(angle);
|
||||
double s = sin(angle);
|
||||
double t = 1.0 - c;
|
||||
|
||||
double tx = t * axis.X;
|
||||
double ty = t * axis.Y;
|
||||
double tz = t * axis.Z;
|
||||
double sx = s * axis.X;
|
||||
double sy = s * axis.Y;
|
||||
double sz = s * axis.Z;
|
||||
|
||||
M[0] = tx * axis.X + c;
|
||||
M[1] = tx * axis.Y + sz;
|
||||
M[2] = tx * axis.Z - sy;
|
||||
|
||||
M[4] = ty * axis.X - sz;
|
||||
M[5] = ty * axis.Y + c;
|
||||
M[6] = ty * axis.Z + sx;
|
||||
|
||||
M[8] = tz * axis.X + sy;
|
||||
M[9] = tz * axis.Y - sx;
|
||||
M[10] = tz * axis.Z + c;
|
||||
return M;
|
||||
}
|
||||
|
||||
|
||||
v3f transposeMatrix(irr::core::matrix4 M, v3f v)
|
||||
{
|
||||
v3f translated;
|
||||
double x = M[0] * v.X + M[4] * v.Y + M[8] * v.Z +M[12];
|
||||
double y = M[1] * v.X + M[5] * v.Y + M[9] * v.Z +M[13];
|
||||
double z = M[2] * v.X + M[6] * v.Y + M[10] * v.Z +M[14];
|
||||
translated.X = x;
|
||||
translated.Y = y;
|
||||
translated.Z = z;
|
||||
return translated;
|
||||
}
|
||||
|
||||
|
||||
void make_jungletree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed)
|
||||
{
|
||||
/*
|
||||
NOTE: Tree-placing code is currently duplicated in the engine
|
||||
and in games that have saplings; both are deprecated but not
|
||||
replaced yet
|
||||
*/
|
||||
content_t c_tree = ndef->getId("mapgen_jungletree");
|
||||
content_t c_leaves = ndef->getId("mapgen_jungleleaves");
|
||||
if (c_tree == CONTENT_IGNORE)
|
||||
c_tree = ndef->getId("mapgen_tree");
|
||||
if (c_leaves == CONTENT_IGNORE)
|
||||
c_leaves = ndef->getId("mapgen_leaves");
|
||||
|
||||
MapNode treenode(c_tree);
|
||||
MapNode leavesnode(c_leaves);
|
||||
|
||||
PseudoRandom pr(seed);
|
||||
for (s16 x= -1; x <= 1; x++)
|
||||
for (s16 z= -1; z <= 1; z++) {
|
||||
if (pr.range(0, 2) == 0)
|
||||
continue;
|
||||
v3s16 p1 = p0 + v3s16(x, 0, z);
|
||||
v3s16 p2 = p0 + v3s16(x, -1, z);
|
||||
u32 vi1 = vmanip.m_area.index(p1);
|
||||
u32 vi2 = vmanip.m_area.index(p2);
|
||||
|
||||
if (vmanip.m_area.contains(p2) &&
|
||||
vmanip.m_data[vi2].getContent() == CONTENT_AIR)
|
||||
vmanip.m_data[vi2] = treenode;
|
||||
else if (vmanip.m_area.contains(p1) &&
|
||||
vmanip.m_data[vi1].getContent() == CONTENT_AIR)
|
||||
vmanip.m_data[vi1] = treenode;
|
||||
}
|
||||
vmanip.m_data[vmanip.m_area.index(p0)] = treenode;
|
||||
|
||||
s16 trunk_h = pr.range(8, 12);
|
||||
v3s16 p1 = p0;
|
||||
for (s16 ii = 0; ii < trunk_h; ii++) {
|
||||
if (vmanip.m_area.contains(p1)) {
|
||||
u32 vi = vmanip.m_area.index(p1);
|
||||
vmanip.m_data[vi] = treenode;
|
||||
}
|
||||
p1.Y++;
|
||||
}
|
||||
|
||||
// p1 is now the last piece of the trunk
|
||||
p1.Y -= 1;
|
||||
|
||||
VoxelArea leaves_a(v3s16(-3, -2, -3), v3s16(3, 2, 3));
|
||||
//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
|
||||
Buffer<u8> leaves_d(leaves_a.getVolume());
|
||||
for (s32 i = 0; i < leaves_a.getVolume(); i++)
|
||||
leaves_d[i] = 0;
|
||||
|
||||
// Force leaves at near the end of the trunk
|
||||
s16 d = 1;
|
||||
for (s16 z = -d; z <= d; z++)
|
||||
for (s16 y = -d; y <= d; y++)
|
||||
for (s16 x = -d; x <= d; x++) {
|
||||
leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
|
||||
}
|
||||
|
||||
// Add leaves randomly
|
||||
for (u32 iii = 0; iii < 30; iii++) {
|
||||
v3s16 p(
|
||||
pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
|
||||
pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
|
||||
pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z - d)
|
||||
);
|
||||
|
||||
for (s16 z = 0; z <= d; z++)
|
||||
for (s16 y = 0; y <= d; y++)
|
||||
for (s16 x = 0; x <= d; x++) {
|
||||
leaves_d[leaves_a.index(p + v3s16(x, y, z))] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Blit leaves to vmanip
|
||||
for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
|
||||
for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
|
||||
v3s16 pmin(leaves_a.MinEdge.X, y, z);
|
||||
u32 i = leaves_a.index(pmin);
|
||||
u32 vi = vmanip.m_area.index(pmin + p1);
|
||||
for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
|
||||
v3s16 p(x, y, z);
|
||||
if (vmanip.m_area.contains(p + p1) &&
|
||||
(vmanip.m_data[vi].getContent() == CONTENT_AIR ||
|
||||
vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
|
||||
if (leaves_d[i] == 1)
|
||||
vmanip.m_data[vi] = leavesnode;
|
||||
}
|
||||
vi++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void make_pine_tree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed)
|
||||
{
|
||||
/*
|
||||
NOTE: Tree-placing code is currently duplicated in the engine
|
||||
and in games that have saplings; both are deprecated but not
|
||||
replaced yet
|
||||
*/
|
||||
content_t c_tree = ndef->getId("mapgen_pine_tree");
|
||||
content_t c_leaves = ndef->getId("mapgen_pine_needles");
|
||||
content_t c_snow = ndef->getId("mapgen_snow");
|
||||
if (c_tree == CONTENT_IGNORE)
|
||||
c_tree = ndef->getId("mapgen_tree");
|
||||
if (c_leaves == CONTENT_IGNORE)
|
||||
c_leaves = ndef->getId("mapgen_leaves");
|
||||
if (c_snow == CONTENT_IGNORE)
|
||||
c_snow = CONTENT_AIR;
|
||||
|
||||
MapNode treenode(c_tree);
|
||||
MapNode leavesnode(c_leaves);
|
||||
MapNode snownode(c_snow);
|
||||
|
||||
PseudoRandom pr(seed);
|
||||
u16 trunk_h = pr.range(9, 13);
|
||||
v3s16 p1 = p0;
|
||||
for (u16 ii = 0; ii < trunk_h; ii++) {
|
||||
if (vmanip.m_area.contains(p1)) {
|
||||
u32 vi = vmanip.m_area.index(p1);
|
||||
vmanip.m_data[vi] = treenode;
|
||||
}
|
||||
p1.Y++;
|
||||
}
|
||||
|
||||
// Make p1 the top node of the trunk
|
||||
p1.Y -= 1;
|
||||
|
||||
VoxelArea leaves_a(v3s16(-3, -6, -3), v3s16(3, 3, 3));
|
||||
Buffer<u8> leaves_d(leaves_a.getVolume());
|
||||
for (s32 i = 0; i < leaves_a.getVolume(); i++)
|
||||
leaves_d[i] = 0;
|
||||
|
||||
// Upper branches
|
||||
u16 dev = 3;
|
||||
for (s16 yy = -1; yy <= 1; yy++) {
|
||||
for (s16 zz = -dev; zz <= dev; zz++) {
|
||||
u32 i = leaves_a.index(v3s16(-dev, yy, zz));
|
||||
u32 ia = leaves_a.index(v3s16(-dev, yy+1, zz));
|
||||
for (s16 xx = -dev; xx <= dev; xx++) {
|
||||
if (pr.range(0, 20) <= 19 - dev) {
|
||||
leaves_d[i] = 1;
|
||||
leaves_d[ia] = 2;
|
||||
}
|
||||
i++;
|
||||
ia++;
|
||||
}
|
||||
}
|
||||
dev--;
|
||||
}
|
||||
|
||||
// Centre top nodes
|
||||
leaves_d[leaves_a.index(v3s16(0, 1, 0))] = 1;
|
||||
leaves_d[leaves_a.index(v3s16(0, 2, 0))] = 1;
|
||||
leaves_d[leaves_a.index(v3s16(0, 3, 0))] = 2;
|
||||
|
||||
// Lower branches
|
||||
s16 my = -6;
|
||||
for (u32 iii = 0; iii < 20; iii++) {
|
||||
s16 xi = pr.range(-3, 2);
|
||||
s16 yy = pr.range(-6, -5);
|
||||
s16 zi = pr.range(-3, 2);
|
||||
if (yy > my)
|
||||
my = yy;
|
||||
for (s16 zz = zi; zz <= zi + 1; zz++) {
|
||||
u32 i = leaves_a.index(v3s16(xi, yy, zz));
|
||||
u32 ia = leaves_a.index(v3s16(xi, yy + 1, zz));
|
||||
for (s32 xx = xi; xx <= xi + 1; xx++) {
|
||||
leaves_d[i] = 1;
|
||||
if (leaves_d[ia] == 0)
|
||||
leaves_d[ia] = 2;
|
||||
i++;
|
||||
ia++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev = 2;
|
||||
for (s16 yy = my + 1; yy <= my + 2; yy++) {
|
||||
for (s16 zz = -dev; zz <= dev; zz++) {
|
||||
u32 i = leaves_a.index(v3s16(-dev, yy, zz));
|
||||
u32 ia = leaves_a.index(v3s16(-dev, yy + 1, zz));
|
||||
for (s16 xx = -dev; xx <= dev; xx++) {
|
||||
if (pr.range(0, 20) <= 19 - dev) {
|
||||
leaves_d[i] = 1;
|
||||
leaves_d[ia] = 2;
|
||||
}
|
||||
i++;
|
||||
ia++;
|
||||
}
|
||||
}
|
||||
dev--;
|
||||
}
|
||||
|
||||
// Blit leaves to vmanip
|
||||
for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
|
||||
for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
|
||||
v3s16 pmin(leaves_a.MinEdge.X, y, z);
|
||||
u32 i = leaves_a.index(pmin);
|
||||
u32 vi = vmanip.m_area.index(pmin + p1);
|
||||
for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
|
||||
v3s16 p(x, y, z);
|
||||
if (vmanip.m_area.contains(p + p1) &&
|
||||
(vmanip.m_data[vi].getContent() == CONTENT_AIR ||
|
||||
vmanip.m_data[vi].getContent() == CONTENT_IGNORE ||
|
||||
vmanip.m_data[vi] == snownode)) {
|
||||
if (leaves_d[i] == 1)
|
||||
vmanip.m_data[vi] = leavesnode;
|
||||
else if (leaves_d[i] == 2)
|
||||
vmanip.m_data[vi] = snownode;
|
||||
}
|
||||
vi++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace treegen
|
92
src/mapgen/treegen.h
Normal file
92
src/mapgen/treegen.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>,
|
||||
2012-2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <matrix4.h>
|
||||
#include "noise.h"
|
||||
|
||||
class MMVManip;
|
||||
class INodeDefManager;
|
||||
class ServerEnvironment;
|
||||
|
||||
|
||||
namespace treegen {
|
||||
|
||||
enum error {
|
||||
SUCCESS,
|
||||
UNBALANCED_BRACKETS
|
||||
};
|
||||
|
||||
struct TreeDef {
|
||||
std::string initial_axiom;
|
||||
std::string rules_a;
|
||||
std::string rules_b;
|
||||
std::string rules_c;
|
||||
std::string rules_d;
|
||||
|
||||
MapNode trunknode;
|
||||
MapNode leavesnode;
|
||||
MapNode leaves2node;
|
||||
|
||||
int leaves2_chance;
|
||||
int angle;
|
||||
int iterations;
|
||||
int iterations_random_level;
|
||||
std::string trunk_type;
|
||||
bool thin_branches;
|
||||
MapNode fruitnode;
|
||||
int fruit_chance;
|
||||
s32 seed;
|
||||
bool explicit_seed;
|
||||
};
|
||||
|
||||
// Add default tree
|
||||
void make_tree(MMVManip &vmanip, v3s16 p0,
|
||||
bool is_apple_tree, INodeDefManager *ndef, s32 seed);
|
||||
// Add jungle tree
|
||||
void make_jungletree(MMVManip &vmanip, v3s16 p0,
|
||||
INodeDefManager *ndef, s32 seed);
|
||||
// Add pine tree
|
||||
void make_pine_tree(MMVManip &vmanip, v3s16 p0,
|
||||
INodeDefManager *ndef, s32 seed);
|
||||
|
||||
// Add L-Systems tree (used by engine)
|
||||
treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef,
|
||||
TreeDef tree_definition);
|
||||
// Spawn L-systems tree from LUA
|
||||
treegen::error spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef,
|
||||
const TreeDef &tree_definition);
|
||||
|
||||
// L-System tree gen helper functions
|
||||
void tree_node_placement(MMVManip &vmanip, v3f p0,
|
||||
MapNode node);
|
||||
void tree_trunk_placement(MMVManip &vmanip, v3f p0,
|
||||
TreeDef &tree_definition);
|
||||
void tree_leaves_placement(MMVManip &vmanip, v3f p0,
|
||||
PseudoRandom ps, TreeDef &tree_definition);
|
||||
void tree_single_leaves_placement(MMVManip &vmanip, v3f p0,
|
||||
PseudoRandom ps, TreeDef &tree_definition);
|
||||
void tree_fruit_placement(MMVManip &vmanip, v3f p0,
|
||||
TreeDef &tree_definition);
|
||||
irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis);
|
||||
|
||||
v3f transposeMatrix(irr::core::matrix4 M ,v3f v);
|
||||
|
||||
}; // namespace treegen
|
Loading…
Add table
Add a link
Reference in a new issue