Skip to content

Commit

Permalink
Mgv5/v7: Fix generateBiomes biome recalculation logic Biomegen down t…
Browse files Browse the repository at this point in the history
…o y = -192 for mgv5 deep oceans. Improve code
  • Loading branch information
paramat committed May 23, 2015
1 parent 39869aa commit 3dba6d1
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 103 deletions.
110 changes: 58 additions & 52 deletions src/mapgen_v5.cpp
Expand Up @@ -327,7 +327,7 @@ void MapgenV5::calculateNoise()
noise_cave2->perlinMap3D(x, y, z);
}

if (node_max.Y >= water_level) {
if (node_max.Y >= BIOMEGEN_BASE_V5) {
noise_filler_depth->perlinMap2D(x, z);
noise_heat->perlinMap2D(x, z);
noise_humidity->perlinMap2D(x, z);
Expand Down Expand Up @@ -396,7 +396,7 @@ int MapgenV5::generateBaseTerrain()

MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map)
{
if (node_max.Y < water_level)
if (node_max.Y < BIOMEGEN_BASE_V5)
return STONE;

v3s16 em = vm->m_area.getExtent();
Expand All @@ -406,74 +406,80 @@ MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map)
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 = NULL;
s16 dfiller = 0;
s16 y0_top = 0;
s16 y0_filler = 0;
s16 depth_water_top = 0;
u16 depth_top = 0;
u16 base_filler = 0;
u16 depth_water_top = 0;
u32 vi = vm->m_area.index(x, node_max.Y, z);

s16 nplaced = 0;
u32 i = vm->m_area.index(x, node_max.Y, z);
// Check node at base of mapchunk above, either a node of a previously
// generated mapchunk or if not, a node of overgenerated base terrain.
content_t c_above = vm->m_data[vi + em.X].getContent();
bool air_above = c_above == CONTENT_AIR;

content_t c_above = vm->m_data[i + em.X].getContent();
bool have_air = c_above == CONTENT_AIR;
// If there is air above enable top/filler placement, otherwise force nplaced to
// stone level by setting a number that will exceed any possible filler depth.
u16 nplaced = (air_above) ? 0 : (u16)-1;

This comment has been minimized.

Copy link
@Zeno-

Zeno- May 28, 2015

Contributor

This is used below somewhere as well. Perhaps it could be an inline function or function-like macro.


for (s16 y = node_max.Y; y >= node_min.Y; y--) {
content_t c = vm->m_data[i].getContent();

if (c != CONTENT_IGNORE && c != CONTENT_AIR &&
(y == node_max.Y || have_air)) {
content_t c = vm->m_data[vi].getContent();

// Biome is only (re)calculated for each stone/water upper surface found
// below air while working downwards. The chosen biome then remains in
// effect for all nodes below until the next biome recalculation.
// Biome is (re)calculated when a stone/water node is either: detected
// below an air node, or, is at column top and might be underground
// or underwater and therefore might not be below air.

This comment has been minimized.

Copy link
@kilbith

kilbith May 23, 2015

Contributor

Hmm... seems like you're repeating yourself, right ? (same for the mgv7 file, lines 621-626)

This comment has been minimized.

Copy link
@paramat

paramat May 28, 2015

Author Contributor

Yes a little, i was aware of some repetition while editing this and tried to minimize it, but to be clear and informative i found it was necessary.

if (c != CONTENT_AIR && (y == node_max.Y || air_above)) {
biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
dfiller = biome->depth_filler + noise_filler_depth->result[index];
y0_top = biome->depth_top;
y0_filler = biome->depth_top + dfiller;
depth_top = biome->depth_top;
base_filler = MYMAX(depth_top + biome->depth_filler
+ noise_filler_depth->result[index], 0);
depth_water_top = biome->depth_water_top;

// Detect stone type for dungeons during every biome calculation.
// This is more efficient than detecting per-node and will not
// miss any desert stone or sandstone biomes.
if (biome->c_stone == c_desert_stone)
stone_type = DESERT_STONE;
else if (biome->c_stone == c_sandstone)
stone_type = SANDSTONE;
}

if (c == c_stone && have_air) {
content_t c_below = vm->m_data[i - em.X].getContent();

if (c_below != CONTENT_AIR && c_below != c_water_source) {
if (nplaced < y0_top) {
vm->m_data[i] = MapNode(biome->c_top);
nplaced++;
} else if (nplaced < y0_filler && nplaced >= y0_top) {
vm->m_data[i] = MapNode(biome->c_filler);
nplaced++;
} else if (c == c_stone) {
have_air = false;
nplaced = 0;
vm->m_data[i] = MapNode(biome->c_stone);
} else {
have_air = false;
nplaced = 0;
}
} else if (c == c_stone) {
have_air = false;
nplaced = 0;
vm->m_data[i] = MapNode(biome->c_stone);
if (c == c_stone) {
content_t c_below = vm->m_data[vi - em.X].getContent();

// If the node below isn't solid, make this node stone, so that
// any top/filler nodes above are structurally supported.
// This is done by aborting the cycle of top/filler placement
// immediately by forcing nplaced to stone level.
if (c_below == CONTENT_AIR || c_below == c_water_source)
nplaced = (u16)-1;

This comment has been minimized.

Copy link
@Zeno-

Zeno- May 28, 2015

Contributor

Why is this explicit cast necessary?

Edit: It has just occurred to be that perhaps some (or most?) compilers might warn about the implicit version from signed to unsigned, so if this is the case then the cast is necessary to make the compiler quiet. This i s an additional reason for the magic number (u16)-1 to be a macro... a) it hides the ugly cast; b) the name of the macro (or const... whatever you choose) would explain what -1 "is" even if it's something like #define UNDEFINED_NPLACED_VALUE ((u16)-1) or MAX_NPLACED_VALUE it's better than a magic number that is meaningless to those reading the code (although I don't see the association between either of these suggestions and "stone level")


if (nplaced < depth_top) {
vm->m_data[vi] = MapNode(biome->c_top);
nplaced++;
} else if (nplaced < base_filler) {
vm->m_data[vi] = MapNode(biome->c_filler);
nplaced++;
} else {
vm->m_data[vi] = MapNode(biome->c_stone);
}
} else if (c == c_stone) {
have_air = false;
nplaced = 0;
vm->m_data[i] = MapNode(biome->c_stone);

air_above = false;
} else if (c == c_water_source) {
have_air = true;
nplaced = 0;
if (y > water_level - depth_water_top)
vm->m_data[i] = MapNode(biome->c_water_top);
else
vm->m_data[i] = MapNode(biome->c_water);
vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ?
biome->c_water_top : biome->c_water);
nplaced = 0; // Enable top/filler placement for next surface
air_above = false; // Biome is not recalculated underwater
} else if (c == CONTENT_AIR) {
have_air = true;
nplaced = 0;
nplaced = 0; // Enable top/filler placement for next surface
air_above = true; // Biome will be recalculated at next surface
} else { // Possible various nodes overgenerated from neighbouring mapchunks
nplaced = (u16)-1; // Disable top/filler placement
air_above = false;
}

vm->m_area.add_y(em, i, -1);
vm->m_area.add_y(em, vi, -1);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/mapgen_v5.h
Expand Up @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapgen.h"

#define LARGE_CAVE_DEPTH -256
#define BIOMEGEN_BASE_V5 -192

/////////////////// Mapgen V5 flags
//#define MGV5_ 0x01
Expand Down
108 changes: 57 additions & 51 deletions src/mapgen_v7.cpp
Expand Up @@ -362,7 +362,7 @@ void MapgenV7::calculateNoise()
noise_mount_height->perlinMap2D(x, z);
}

if (node_max.Y >= water_level) {
if (node_max.Y >= BIOMEGEN_BASE_V7) {
noise_filler_depth->perlinMap2D(x, z);
noise_heat->perlinMap2D(x, z);
noise_humidity->perlinMap2D(x, z);
Expand Down Expand Up @@ -591,7 +591,7 @@ void MapgenV7::generateRidgeTerrain()

MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map)
{
if (node_max.Y < water_level)
if (node_max.Y < BIOMEGEN_BASE_V7)
return STONE;

v3s16 em = vm->m_area.getExtent();
Expand All @@ -601,74 +601,80 @@ MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map)
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 = NULL;
s16 dfiller = 0;
s16 y0_top = 0;
s16 y0_filler = 0;
s16 depth_water_top = 0;
u16 depth_top = 0;
u16 base_filler = 0;
u16 depth_water_top = 0;
u32 vi = vm->m_area.index(x, node_max.Y, z);

s16 nplaced = 0;
u32 i = vm->m_area.index(x, node_max.Y, z);
// Check node at base of mapchunk above, either a node of a previously
// generated mapchunk or if not, a node of overgenerated base terrain.
content_t c_above = vm->m_data[vi + em.X].getContent();
bool air_above = c_above == CONTENT_AIR;

content_t c_above = vm->m_data[i + em.X].getContent();
bool have_air = c_above == CONTENT_AIR;
// If there is air above enable top/filler placement, otherwise force nplaced to
// stone level by setting a number that will exceed any possible filler depth.
u16 nplaced = (air_above) ? 0 : (u16)-1;

This comment has been minimized.

Copy link
@Zeno-

Zeno- May 28, 2015

Contributor

Please don't cast where a cast is not necessary

Edit: also, please don't use parenthesis where they are not necessary.

E.g. the line should simply be u16 nplaced = air_above ? 0 : -1;

Why is npaced set to -1 if air_above is false? Is -1 stone level? If so it should be a constant-like macro not a magic number.


for (s16 y = node_max.Y; y >= node_min.Y; y--) {
content_t c = vm->m_data[i].getContent();
content_t c = vm->m_data[vi].getContent();

if (c != CONTENT_IGNORE && c != CONTENT_AIR &&
(y == node_max.Y || have_air)) {
// Biome is only (re)calculated for each stone/water upper surface found
// below air while working downwards. The chosen biome then remains in
// effect for all nodes below until the next biome recalculation.
// Biome is (re)calculated when a stone/water node is either: detected
// below an air node, or, is at column top and might be underground
// or underwater and therefore might not be below air.
if (c != CONTENT_AIR && (y == node_max.Y || air_above)) {
biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
dfiller = biome->depth_filler + noise_filler_depth->result[index];
y0_top = biome->depth_top;
y0_filler = biome->depth_top + dfiller;
depth_top = biome->depth_top;
base_filler = MYMAX(depth_top + biome->depth_filler
+ noise_filler_depth->result[index], 0);
depth_water_top = biome->depth_water_top;

// Detect stone type for dungeons during every biome calculation.
// This is more efficient than detecting per-node and will not
// miss any desert stone or sandstone biomes.
if (biome->c_stone == c_desert_stone)
stone_type = DESERT_STONE;
else if (biome->c_stone == c_sandstone)
stone_type = SANDSTONE;
}

if (c == c_stone && have_air) {
content_t c_below = vm->m_data[i - em.X].getContent();

if (c_below != CONTENT_AIR && c_below != c_water_source) {
if (nplaced < y0_top) {
vm->m_data[i] = MapNode(biome->c_top);
nplaced++;
} else if (nplaced < y0_filler && nplaced >= y0_top) {
vm->m_data[i] = MapNode(biome->c_filler);
nplaced++;
} else if (c == c_stone) {
have_air = false;
nplaced = 0;
vm->m_data[i] = MapNode(biome->c_stone);
} else {
have_air = false;
nplaced = 0;
}
} else if (c == c_stone) {
have_air = false;
nplaced = 0;
vm->m_data[i] = MapNode(biome->c_stone);
if (c == c_stone) {
content_t c_below = vm->m_data[vi - em.X].getContent();

// If the node below isn't solid, make this node stone, so that
// any top/filler nodes above are structurally supported.
// This is done by aborting the cycle of top/filler placement
// immediately by forcing nplaced to stone level.
if (c_below == CONTENT_AIR || c_below == c_water_source)
nplaced = (u16)-1;

if (nplaced < depth_top) {
vm->m_data[vi] = MapNode(biome->c_top);
nplaced++;
} else if (nplaced < base_filler) {
vm->m_data[vi] = MapNode(biome->c_filler);
nplaced++;
} else {
vm->m_data[vi] = MapNode(biome->c_stone);
}
} else if (c == c_stone) {
have_air = false;
nplaced = 0;
vm->m_data[i] = MapNode(biome->c_stone);

air_above = false;
} else if (c == c_water_source) {
have_air = true;
nplaced = 0;
if (y > water_level - depth_water_top)
vm->m_data[i] = MapNode(biome->c_water_top);
else
vm->m_data[i] = MapNode(biome->c_water);
vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ?
biome->c_water_top : biome->c_water);
nplaced = 0; // Enable top/filler placement for next surface
air_above = false; // Biome is not recalculated underwater
} else if (c == CONTENT_AIR) {
have_air = true;
nplaced = 0;
nplaced = 0; // Enable top/filler placement for next surface
air_above = true; // Biome will be recalculated at next surface
} else { // Possible various nodes overgenerated from neighbouring mapchunks
nplaced = (u16)-1; // Disable top/filler placement
air_above = false;
}

vm->m_area.add_y(em, i, -1);
vm->m_area.add_y(em, vi, -1);
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/mapgen_v7.h
Expand Up @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,

#include "mapgen.h"

#define BIOMEGEN_BASE_V7 -192

/////////////////// Mapgen V7 flags
#define MGV7_MOUNTAINS 0x01
#define MGV7_RIDGES 0x02
Expand Down

0 comments on commit 3dba6d1

Please sign in to comment.