#include "server/mapgen.h"
#include "server/server_map.h"
#include "util.h"
-#define NUM_BIOMES 3
-static s32 height_mountain(v3s32 pos, f64 factor, s32 height)
+Biome get_biome(v2s32 pos, f64 *factor)
{
- return pow((height + 96) * pow(((smooth2d(U32(pos.x) / 48.0, U32(pos.z) / 48.0, 0, seed + SO_MOUNTAIN_HEIGHT) + 1.0) * 256.0 + 128.0), factor), 1.0 / (factor + 1.0)) - 96;
+ for (Biome i = 0; i < BIOME_COUNT; i++) {
+ BiomeDef *def = &biomes[i];
+ f64 f = def->probability == 1.0 ? 1.0 : (smooth2d(U32(pos.x) / def->threshold, U32(pos.y) / def->threshold, 0, seed + def->offset) * 0.5 - 0.5 + def->probability) / def->probability;
+
+ if (f > 0.0) {
+ if (factor)
+ *factor = f;
+ return i;
+ }
+ }
+
+ return BIOME_COUNT;
+}
+
+// mountain biome
+
+static s32 height_mountain(v2s32 pos, f64 factor, s32 height, unused void *row_data, unused void *block_data)
+{
+ return pow((height + 96) * pow(((smooth2d(U32(pos.x) / 48.0, U32(pos.y) / 48.0, 0, seed + SO_MOUNTAIN_HEIGHT) + 1.0) * 256.0 + 128.0), factor), 1.0 / (factor + 1.0)) - 96;
}
-static Node generate_mountain(unused v3s32 pos, s32 diff, unused f64 wetness, unused f64 temperature, unused f64 factor, unused MapBlock *block, unused List *changed_blocks)
+static Node generate_mountain(unused v3s32 pos, s32 diff, unused f64 wetness, unused f64 temperature, unused f64 factor, unused MapBlock *block, unused List *changed_blocks, unused void *row_data, unused void *block_data)
{
return diff <= 0 ? NODE_STONE : NODE_AIR;
}
+// ocean biome
+
typedef enum
{
OL_BEACH_EDGE,
static f64 ocean_level_start[OL_COUNT] = {
0.0,
+ 0.1,
0.2,
- 0.3,
- 0.8,
+ 0.5,
};
+typedef struct
+{
+ bool has_vulcano;
+ v2s32 vulcano_pos;
+} OceanBlockData;
+
+typedef struct
+{
+ bool vulcano;
+ bool vulcano_crater;
+ s32 vulcano_height;
+ s32 vulcano_crater_top;
+ Node vulcano_stone;
+} OceanRowData;
+
+static const f64 vulcano_radius = 256.0;
+static const f64 vulcano_block_offset = vulcano_radius * 2.0 / MAPBLOCK_SIZE;
+
static OceanLevel get_ocean_level(f64 factor)
{
if (factor >= ocean_level_start[OL_DEEP_OCEAN])
return OL_BEACH_EDGE;
}
-static s32 mix(s32 a, s32 b, f64 f)
-{
- return (a * (1.0 - f) + b * f);
-}
-
static f64 get_ocean_level_factor(f64 factor, OceanLevel level)
{
f64 start, end;
return (factor - start) / (end - start);
}
-static s32 height_ocean(unused v3s32 pos, f64 factor, s32 height)
+static bool is_vulcano(v2s32 pos)
+{
+ f64 factor;
+ return smooth2d(U32(pos.x), U32(pos.y), 0, seed + SO_VULCANO) > 0.0 && get_biome((v2s32) {pos.x * MAPBLOCK_SIZE, pos.y * MAPBLOCK_SIZE}, &factor) == BIOME_OCEAN && get_ocean_level(factor) == OL_DEEP_OCEAN;
+}
+
+static bool find_near_vulcano(v2s32 pos, v2s32 *result)
+{
+ f64 x = pos.x / vulcano_block_offset;
+ f64 z = pos.y / vulcano_block_offset;
+
+ s32 lx, lz;
+ lx = floor(x);
+ lz = floor(z);
+
+ s32 hx, hz;
+ hx = ceil(x);
+ hz = ceil(z);
+
+ for (s32 ix = lx; ix <= hx; ix++) {
+ for (s32 iz = lz; iz <= hz; iz++) {
+ v2s32 vulcano_pos = {ix * 32, iz * 32};
+
+ if (is_vulcano(vulcano_pos)) {
+ *result = vulcano_pos;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static f64 mix(f64 a, f64 b, f64 f)
+{
+ return (a * (1.0 - f) + b * f);
+}
+
+static inline f64 min(f64 a, f64 b)
+{
+ return a < b ? a : b;
+}
+
+static inline f64 max(f64 a, f64 b)
+{
+ return a > b ? a : b;
+}
+
+static f64 distance(v2s32 a, v2s32 b)
+{
+ return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
+}
+
+static s32 calculate_ocean_floor(f64 factor, s32 height)
{
switch (get_ocean_level(factor)) {
case OL_BEACH_EDGE:
- return mix(height + 1, 0, get_ocean_level_factor(factor, OL_BEACH_EDGE));
+ return mix(height + 1, 0, pow(get_ocean_level_factor(factor, OL_BEACH_EDGE), 0.8));
case OL_BEACH:
return 0;
case OL_OCEAN:
- return mix(0, -10, get_ocean_level_factor(factor, OL_OCEAN));
+ return mix(0, -10, pow(get_ocean_level_factor(factor, OL_OCEAN), 0.5));
case OL_DEEP_OCEAN:
- return mix(-10, -50, get_ocean_level_factor(factor, OL_DEEP_OCEAN));
+ return mix(-10, -50, pow(get_ocean_level_factor(factor, OL_DEEP_OCEAN), 0.5));
default:
break;
return height;
}
-static Node generate_ocean(unused v3s32 pos, s32 diff, unused f64 wetness, unused f64 temperature, unused f64 factor, unused MapBlock *block, unused List *changed_blocks)
+static void preprocess_block_ocean(MapBlock *block, unused List *changed_blocks, void *block_data)
{
- if (diff <= -5)
- return NODE_STONE;
- else if (diff <= 0)
- return NODE_SAND;
- else if (pos.y <= 0)
- return NODE_WATER;
+ OceanBlockData *data = block_data;
+
+ v2s32 vulcano_pos;
+ if ((data->has_vulcano = find_near_vulcano((v2s32) {block->pos.x, block->pos.z}, &vulcano_pos)))
+ data->vulcano_pos = (v2s32) {vulcano_pos.x * MAPBLOCK_SIZE, vulcano_pos.y * MAPBLOCK_SIZE};
+}
+
+static void preprocess_row_ocean(v2s32 pos, unused s32 height, unused f64 factor, void *row_data, void *block_data)
+{
+ OceanRowData *rdata = row_data;
+ OceanBlockData *bdata = block_data;
+ rdata->vulcano = false;
+
+ if (bdata->has_vulcano) {
+ f64 dist = distance(pos, bdata->vulcano_pos);
+
+ if (dist < vulcano_radius) {
+ f64 crater_factor = pow(asin(1.0 - dist / vulcano_radius), 2.0);
+ f64 vulcano_height = (pnoise2d(U32(pos.x) / 100.0, U32(pos.y) / 100.0, 0.2, 2, seed + SO_VULCANO_HEIGHT) * 0.5 + 0.5) * 128.0 * crater_factor + 1.0 - 30.0;
+ bool is_crater = vulcano_height > 0;
+
+ if (! is_crater)
+ vulcano_height = min(vulcano_height + 5.0, 0.0);
+
+ if (vulcano_height < 0)
+ vulcano_height *= 2.0;
+
+ rdata->vulcano = true;
+ rdata->vulcano_crater = is_crater;
+ rdata->vulcano_height = floor(vulcano_height + 0.5);
+ rdata->vulcano_crater_top = 50 + floor((pnoise2d(U32(pos.x) / 3.0, U32(pos.y) / 3.0, 0.0, 1, seed + SO_VULCANO_CRATER_TOP) * 0.5 + 0.5) * 3.0 + 0.5);
+ rdata->vulcano_stone = is_crater ? ((pnoise2d(U32(pos.x) / 16.0, U32(pos.y) / 16.0, 0.85, 3, seed + SO_VULCANO_STONE) * 0.5 + 0.5) * crater_factor > 0.4 ? NODE_VULCANO_STONE : NODE_STONE) : NODE_SAND;
+ }
+ }
+}
+
+static s32 height_ocean(unused v2s32 pos, f64 factor, s32 height, void *row_data, unused void *block_data)
+{
+ OceanRowData *rdata = row_data;
+ s32 ocean_floor = calculate_ocean_floor(factor, height);
+
+ return rdata->vulcano ? max(ocean_floor, rdata->vulcano_height) : ocean_floor;
+}
+
+static Node generate_ocean(v3s32 pos, s32 diff, unused f64 wetness, unused f64 temperature, unused f64 factor, unused MapBlock *block, unused List *changed_blocks, void *row_data, unused void *block_data)
+{
+ OceanRowData *rdata = row_data;
+
+ if (rdata->vulcano && rdata->vulcano_crater) {
+ if (diff <= -5)
+ return pos.y <= 45 ? NODE_LAVA : NODE_AIR;
+ else if (diff <= 0)
+ return pos.y <= rdata->vulcano_crater_top ? rdata->vulcano_stone : NODE_AIR;
+ else
+ return NODE_AIR;
+ } else {
+ if (diff <= -5)
+ return NODE_STONE;
+ else if (diff <= 0)
+ return NODE_SAND;
+ else if (pos.y <= 0)
+ return NODE_WATER;
+ }
return NODE_AIR;
}
-static s32 height_hills(unused v3s32 pos, unused f64 factor, s32 height)
+// hills biome
+
+static s32 height_hills(unused v2s32 pos, unused f64 factor, s32 height, unused void *row_data, unused void *block_data)
{
return height;
}
-static Node generate_hills(v3s32 pos, s32 diff, unused f64 wetness, unused f64 temperature, unused f64 factor, unused MapBlock *block, List *changed_blocks)
+static Node generate_hills(v3s32 pos, s32 diff, unused f64 wetness, unused f64 temperature, unused f64 factor, unused MapBlock *block, List *changed_blocks, unused void *row_data, unused void *block_data)
{
if (diff == 2 && smooth2d(U32(pos.x), U32(pos.z), 0, seed + SO_BOULDER_CENTER) > 0.999) {
for (s8 bx = -1; bx <= 1; bx++) {
return NODE_AIR;
}
-static BiomeDef biomes[NUM_BIOMES] = {
+BiomeDef biomes[BIOME_COUNT] = {
{
.probability = 0.2,
.offset = SO_MOUNTAIN,
+ .threshold = 1024.0,
+ .snow = true,
.height = &height_mountain,
.generate = &generate_mountain,
+ .block_data_size = 0,
+ .preprocess_block = NULL,
+ .row_data_size = 0,
+ .preprocess_row = NULL,
},
{
.probability = 0.2,
.offset = SO_OCEAN,
+ .threshold = 2048.0,
+ .snow = false,
.height = &height_ocean,
.generate = &generate_ocean,
+ .block_data_size = sizeof(OceanBlockData),
+ .preprocess_block = &preprocess_block_ocean,
+ .row_data_size = sizeof(OceanRowData),
+ .preprocess_row = &preprocess_row_ocean,
+
},
{
.probability = 1.0,
.offset = SO_NONE,
+ .threshold = 0.0,
+ .snow = true,
.height = &height_hills,
.generate = &generate_hills,
+ .block_data_size = 0,
+ .preprocess_block = NULL,
+ .row_data_size = 0,
+ .preprocess_row = NULL,
},
};
-
-BiomeDef *get_biome(v3s32 pos, f64 *factor)
-{
- for (int i = 0; i < NUM_BIOMES; i++) {
- BiomeDef *def = &biomes[i];
- f64 f = (smooth2d(U32(pos.x) / 1024.0, U32(pos.z) / 1024.0, 0, seed + def->offset) * 0.5 - 0.5 + def->probability) / def->probability;
-
- if (f > 0.0) {
- if (factor)
- *factor = f;
- return def;
- }
- }
-
- return NULL;
-}
#include <stdio.h>
#include <math.h>
+#include <stdlib.h>
#include "environment.h"
#include "perlin.h"
#include "server/biomes.h"
MapBlockExtraData *extra = block->extra;
v3s32 block_node_pos = {block->pos.x * MAPBLOCK_SIZE, block->pos.y * MAPBLOCK_SIZE, block->pos.z * MAPBLOCK_SIZE};
- v3s32 pos;
+
+ char *block_data[BIOME_COUNT] = {NULL};
+ bool preprocessed_block[BIOME_COUNT];
for (u8 x = 0; x < MAPBLOCK_SIZE; x++) {
- pos.x = block_node_pos.x + x;
+ s32 pos_x = block_node_pos.x + x;
for (u8 z = 0; z < MAPBLOCK_SIZE; z++) {
- pos.z = block_node_pos.z + z;
+ v2s32 pos_horizontal = {pos_x, block_node_pos.z + z};
+
+ s32 height = pnoise2d(U32(pos_horizontal.x) / 32.0, U32(pos_horizontal.y) / 32.0, 0.45, 5, seed + SO_HEIGHT) * 16.0 + 32;
+
+ f64 factor;
+ Biome biome = get_biome(pos_horizontal, &factor);
+ BiomeDef *biome_def = &biomes[biome];
+
+ if (biome_def->block_data_size > 0 && ! block_data[biome])
+ block_data[biome] = malloc(biome_def->block_data_size);
- s32 height = pnoise2d(U32(pos.x) / 32.0, U32(pos.z) / 32.0, 0.45, 5, seed + SO_HEIGHT) * 16.0 + 32;
+ if (biome_def->preprocess_block && ! preprocessed_block[biome]) {
+ biome_def->preprocess_block(block, changed_blocks, block_data[biome]);
+ preprocessed_block[biome] = true;
+ }
+
+ char row_data[biome_def->row_data_size];
- f64 biome_factor;
- BiomeDef *biome_def = get_biome(pos, &biome_factor);
+ if (biome_def->preprocess_row)
+ biome_def->preprocess_row(pos_horizontal, height, factor, row_data, block_data[biome]);
- height = biome_def->height(pos, biome_factor, height);
+ height = biome_def->height(pos_horizontal, factor, height, row_data, block_data[biome]);
for (u8 y = 0; y < MAPBLOCK_SIZE; y++) {
- pos.y = block_node_pos.y + y;
+ v3s32 pos = {pos_horizontal.x, block_node_pos.y + y, pos_horizontal.y};
f64 wetness = get_wetness(pos);
f64 temperature = get_temperature(pos);
s32 diff = pos.y - height;
- Node node = biome_def->generate(pos, diff, wetness, temperature, biome_factor, block, changed_blocks);
+ Node node = biome_def->generate(pos, diff, wetness, temperature, factor, block, changed_blocks, row_data, block_data[biome]);
- if (diff <= 1 && temperature < 0.0 && node == NODE_AIR)
+ if (biome_def->snow && diff <= 1 && temperature < 0.0 && node == NODE_AIR)
node = NODE_SNOW;
pthread_mutex_lock(&block->mtx);
}
}
}
+
+ for (Biome i = 0; i < BIOME_COUNT; i++) {
+ if (block_data[i])
+ free(block_data[i]);
+ }
}