1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-01 17:38:41 +00:00

Move files to subdirectories (#6599)

* Move files around
This commit is contained in:
Vitaliy 2017-11-09 01:56:20 +03:00 committed by Loïc Blot
parent fc9747eb4b
commit 20a85d76d9
118 changed files with 236 additions and 221 deletions

19
src/mapgen/CMakeLists.txt Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

302
src/mapgen/mapgen.h Normal file
View 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;
};

View 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(&params->np_base, seed, csize.X, csize.Z);
noise_filler_depth = new Noise(&params->np_filler_depth, seed, csize.X, csize.Z);
noise_height1 = new Noise(&params->np_height1, seed, csize.X, csize.Z);
noise_height2 = new Noise(&params->np_height2, seed, csize.X, csize.Z);
noise_height3 = new Noise(&params->np_height3, seed, csize.X, csize.Z);
noise_height4 = new Noise(&params->np_height4, seed, csize.X, csize.Z);
noise_hills_terrain = new Noise(&params->np_hills_terrain, seed, csize.X, csize.Z);
noise_ridge_terrain = new Noise(&params->np_ridge_terrain, seed, csize.X, csize.Z);
noise_step_terrain = new Noise(&params->np_step_terrain, seed, csize.X, csize.Z);
noise_hills = new Noise(&params->np_hills, seed, csize.X, csize.Z);
noise_ridge_mnt = new Noise(&params->np_ridge_mnt, seed, csize.X, csize.Z);
noise_step_mnt = new Noise(&params->np_step_mnt, seed, csize.X, csize.Z);
//// 3D terrain noise
// 1 up 1 down overgeneration
noise_mnt_var = new Noise(&params->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;
}

View 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
View 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(&params->np_filler_depth, seed, csize.X, csize.Z);
if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
noise_terrain = new Noise(&params->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
View 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;
};

View 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(&params->np_seabed, seed, csize.X, csize.Z);
noise_filler_depth = new Noise(&params->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;
}

View 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;
};

View 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;
}

View 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
View 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(&params->np_filler_depth, seed, csize.X, csize.Z);
noise_factor = new Noise(&params->np_factor, seed, csize.X, csize.Z);
noise_height = new Noise(&params->np_height, seed, csize.X, csize.Z);
// 3D terrain noise
// 1-up 1-down overgeneration
noise_ground = new Noise(&params->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
View 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

File diff suppressed because it is too large Load diff

169
src/mapgen/mapgen_v6.h Normal file
View 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
View 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(&params->np_terrain_base, seed, csize.X, csize.Z);
noise_terrain_alt = new Noise(&params->np_terrain_alt, seed, csize.X, csize.Z);
noise_terrain_persist = new Noise(&params->np_terrain_persist, seed, csize.X, csize.Z);
noise_height_select = new Noise(&params->np_height_select, seed, csize.X, csize.Z);
noise_filler_depth = new Noise(&params->np_filler_depth, seed, csize.X, csize.Z);
if (spflags & MGV7_MOUNTAINS)
noise_mount_height = new Noise(&params->np_mount_height, seed, csize.X, csize.Z);
if (spflags & MGV7_FLOATLANDS) {
noise_floatland_base = new Noise(&params->np_floatland_base, seed, csize.X, csize.Z);
noise_float_base_height = new Noise(&params->np_float_base_height, seed, csize.X, csize.Z);
}
if (spflags & MGV7_RIDGES) {
noise_ridge_uwater = new Noise(&params->np_ridge_uwater, seed, csize.X, csize.Z);
// 3D noise, 1-up 1-down overgeneration
noise_ridge = new Noise(&params->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(&params->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
View 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;
};

View 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(&params->np_filler_depth, seed, csize.X, csize.Z);
noise_inter_valley_slope = new Noise(&params->np_inter_valley_slope, seed, csize.X, csize.Z);
noise_rivers = new Noise(&params->np_rivers, seed, csize.X, csize.Z);
noise_terrain_height = new Noise(&params->np_terrain_height, seed, csize.X, csize.Z);
noise_valley_depth = new Noise(&params->np_valley_depth, seed, csize.X, csize.Z);
noise_valley_profile = new Noise(&params->np_valley_profile, seed, csize.X, csize.Z);
//// 3D Terrain noise
// 1-up 1-down overgeneration
noise_inter_valley_fill = new Noise(&params->np_inter_valley_fill, seed, csize.X, csize.Y + 2, csize.Z);
// 1-down overgeneraion
noise_cave1 = new Noise(&params->np_cave1, seed, csize.X, csize.Y + 1, csize.Z);
noise_cave2 = new Noise(&params->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
noise_massive_caves = new Noise(&params->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
View 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
View 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(&params->np_heat,
params->seed, m_csize.X, m_csize.Z);
noise_humidity = new Noise(&params->np_humidity,
params->seed, m_csize.X, m_csize.Z);
noise_heat_blend = new Noise(&params->np_heat_blend,
params->seed, m_csize.X, m_csize.Z);
noise_humidity_blend = new Noise(&params->np_humidity_blend,
params->seed, m_csize.X, m_csize.Z);
heatmap = noise_heat->result;
humidmap = noise_humidity->result;
biomemap = new 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
View 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;
};

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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