]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/biomes.c
Trees break if they have no connection to ground
[dragonblocks_alpha.git] / src / server / biomes.c
1 #include <math.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "server/biomes.h"
5 #include "server/server_terrain.h"
6 #include "server/terrain_gen.h"
7 #include "server/voxel_depth_search.h"
8
9 Biome get_biome(v2s32 pos, f64 *factor)
10 {
11         for (Biome i = 0; i < COUNT_BIOME; i++) {
12                 BiomeDef *def = &biomes[i];
13                 f64 f = def->probability == 1.0 ? 1.0
14                         : (smooth2d(U32(pos.x) / def->threshold, U32(pos.y) / def->threshold, 0, seed + def->offset) * 0.5 - 0.5 + def->probability) / def->probability;
15
16                 if (f > 0.0) {
17                         if (factor)
18                                 *factor = f;
19                         return i;
20                 }
21         }
22
23         return COUNT_BIOME;
24 }
25
26 // mountain biome
27
28 static s32 height_mountain(BiomeArgsHeight *args)
29 {
30         return pow((args->height + 96) * pow(((smooth2d(U32(args->pos.x) / 48.0, U32(args->pos.y) / 48.0, 0, seed + OFFSET_MOUNTAIN_HEIGHT) + 1.0) * 256.0 + 128.0), args->factor), 1.0 / (args->factor + 1.0)) - 96;
31 }
32
33 static NodeType generate_mountain(BiomeArgsGenerate *args)
34 {
35         return args->diff <= 0 ? NODE_STONE : NODE_AIR;
36 }
37
38 // ocean biome
39
40 typedef enum {
41         OCEAN_EDGE,
42         OCEAN_BEACH,
43         OCEAN_MAIN,
44         OCEAN_DEEP,
45         COUNT_OCEAN
46 } OceanLevel;
47
48 static f64 ocean_level_start[COUNT_OCEAN] = {
49         0.0,
50         0.1,
51         0.2,
52         0.5,
53 };
54
55 typedef struct {
56         bool has_vulcano;
57         v2s32 vulcano_pos;
58 } OceanChunkData;
59
60 typedef struct {
61         bool vulcano;
62         bool vulcano_crater;
63         s32 vulcano_height;
64         s32 vulcano_crater_top;
65         NodeType vulcano_stone;
66 } OceanRowData;
67
68 static const f64 vulcano_radius = 256.0;
69 static const f64 vulcano_diameter = vulcano_radius * 2.0;
70
71 static OceanLevel get_ocean_level(f64 factor)
72 {
73         if (factor >= ocean_level_start[OCEAN_DEEP])
74                 return OCEAN_DEEP;
75         else if (factor >= ocean_level_start[OCEAN_MAIN])
76                 return OCEAN_MAIN;
77         else if (factor >= ocean_level_start[OCEAN_BEACH])
78                 return OCEAN_BEACH;
79
80         return OCEAN_EDGE;
81 }
82
83 static f64 get_ocean_level_factor(f64 factor, OceanLevel level)
84 {
85         f64 start, end;
86         start = ocean_level_start[level];
87         end = ++level == COUNT_OCEAN ? 1.0 : ocean_level_start[level];
88
89         return (factor - start) / (end - start);
90 }
91
92 static f64 distance(v2s32 a, v2s32 b)
93 {
94         return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
95 }
96
97 static s32 calculate_ocean_floor(f64 factor, s32 height)
98 {
99         switch (get_ocean_level(factor)) {
100                 case OCEAN_EDGE:
101                         return f64_mix(height + 1, 0, pow(get_ocean_level_factor(factor, OCEAN_EDGE), 0.8));
102
103                 case OCEAN_BEACH:
104                         return 0;
105
106                 case OCEAN_MAIN:
107                         return f64_mix(0, -10, pow(get_ocean_level_factor(factor, OCEAN_MAIN), 0.5));
108
109                 case OCEAN_DEEP:
110                         return f64_mix(-10, -50, pow(get_ocean_level_factor(factor, OCEAN_DEEP), 0.5));
111
112                 default:
113                         break;
114         }
115
116         return height;
117 }
118
119 static void before_chunk_ocean(BiomeArgsChunk *args)
120 {
121         OceanChunkData *chunk_data = args->chunk_data;
122
123         chunk_data->vulcano_pos = (v2s32) {
124                 floor((f64) args->chunk->pos.x * CHUNK_SIZE / vulcano_diameter + 0.5) * vulcano_diameter,
125                 floor((f64) args->chunk->pos.z * CHUNK_SIZE / vulcano_diameter + 0.5) * vulcano_diameter
126         };
127
128         f64 factor;
129         chunk_data->has_vulcano = noise2d(chunk_data->vulcano_pos.x, chunk_data->vulcano_pos.y, 0, seed + OFFSET_VULCANO) > 0.0
130                 && get_biome((v2s32) {chunk_data->vulcano_pos.x, chunk_data->vulcano_pos.y}, &factor) == BIOME_OCEAN
131                 && get_ocean_level(factor) == OCEAN_DEEP;
132 }
133
134 static void before_row_ocean(BiomeArgsRow *args)
135 {
136         OceanChunkData *chunk_data = args->chunk_data;
137         OceanRowData *row_data = args->row_data;
138
139         row_data->vulcano = false;
140
141         if (chunk_data->has_vulcano) {
142                 f64 dist = distance(args->pos, chunk_data->vulcano_pos);
143
144                 if (dist < vulcano_radius) {
145                         f64 crater_factor = pow(asin(1.0 - dist / vulcano_radius), 2.0);
146                         f64 vulcano_height = (pnoise2d(U32(args->pos.x) / 100.0, U32(args->pos.y) / 100.0, 0.2, 2, seed + OFFSET_VULCANO_HEIGHT) * 0.5 + 0.5) * 128.0 * crater_factor + 1.0 - 30.0;
147                         bool is_crater = vulcano_height > 0;
148
149                         if (!is_crater)
150                                 vulcano_height = f64_min(vulcano_height + 5.0, 0.0);
151
152                         if (vulcano_height < 0)
153                                 vulcano_height *= 2.0;
154
155                         row_data->vulcano = true;
156                         row_data->vulcano_crater = is_crater;
157                         row_data->vulcano_height = floor(vulcano_height + 0.5);
158                         row_data->vulcano_crater_top = 50 + floor((pnoise2d(U32(args->pos.x) / 3.0, U32(args->pos.y) / 3.0, 0.0, 1, seed + OFFSET_VULCANO_CRATER_TOP) * 0.5 + 0.5) * 3.0 + 0.5);
159                         row_data->vulcano_stone = is_crater
160                                 ? ((pnoise2d(U32(args->pos.x) / 16.0, U32(args->pos.y) / 16.0, 0.85, 3, seed + OFFSET_VULCANO_STONE) * 0.5 + 0.5) * crater_factor > 0.4
161                                         ? NODE_VULCANO_STONE
162                                         : NODE_STONE)
163                                 : NODE_SAND;
164                 }
165         }
166 }
167
168 static s32 height_ocean(BiomeArgsHeight *args)
169 {
170         OceanRowData *row_data = args->row_data;
171
172         s32 ocean_floor = calculate_ocean_floor(args->factor, args->height);
173         return row_data->vulcano ? f64_max(ocean_floor, row_data->vulcano_height) : ocean_floor;
174 }
175
176 NodeType ocean_get_node_at(v3s32 pos, s32 diff, void *_row_data)
177 {
178         OceanRowData *row_data = _row_data;
179
180         if (row_data->vulcano && row_data->vulcano_crater) {
181                 if (diff <= -5)
182                         return pos.y <= 45 ? NODE_LAVA : NODE_AIR;
183                 else if (diff <= 0)
184                         return pos.y <= row_data->vulcano_crater_top ? row_data->vulcano_stone : NODE_AIR;
185                 else
186                         return NODE_AIR;
187         } else {
188                 if (diff <= -5)
189                         return NODE_STONE;
190                 else if (diff <= 0)
191                         return NODE_SAND;
192                 else if (pos.y <= 0)
193                         return NODE_WATER;
194         }
195
196         return NODE_AIR;
197 }
198
199 static NodeType generate_ocean(BiomeArgsGenerate *args)
200 {
201         return ocean_get_node_at(args->pos, args->diff, args->row_data);
202 }
203
204 // hills biome
205
206 typedef struct {
207         Tree boulder_visit;
208         bool boulder_success[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE];
209 } HillsChunkData;
210
211 static void before_chunk_hills(BiomeArgsChunk *args)
212 {
213         HillsChunkData *chunk_data = args->chunk_data;
214         tree_ini(&chunk_data->boulder_visit);
215         memset(chunk_data->boulder_success, 0, sizeof chunk_data->boulder_success);
216 }
217
218 static void after_chunk_hills(BiomeArgsChunk *args)
219 {
220         HillsChunkData *chunk_data = args->chunk_data;
221         tree_clr(&chunk_data->boulder_visit, &free, NULL, NULL, 0);
222 }
223
224 static s32 height_hills(BiomeArgsHeight *args)
225 {
226         return args->height;
227 }
228
229 static bool is_boulder(s32 diff, v3s32 pos)
230 {
231         return diff < 16 &&
232                 smooth3d(U32(pos.x) / 16.0, U32(pos.y) / 12.0, U32(pos.z) / 16.0, 0, seed + OFFSET_BOULDER) > 0.8;
233 }
234
235 static void boulder_search_callback(DepthSearchNode *node)
236 {
237         s32 diff = node->pos.y - terrain_gen_get_base_height((v2s32) {node->pos.x, node->pos.z});
238
239         if (diff <= 0)
240                 node->type = DEPTH_SEARCH_TARGET;
241         else if (is_boulder(diff, node->pos))
242                 node->type = DEPTH_SEARCH_PATH;
243         else
244                 node->type = DEPTH_SEARCH_BLOCK;
245 }
246
247 static NodeType generate_hills(BiomeArgsGenerate *args)
248 {
249         HillsChunkData *chunk_data = args->chunk_data;
250
251         if (is_boulder(args->diff, args->pos) && (args->diff <= 0 || voxel_depth_search(args->pos,
252                         (void *) &boulder_search_callback, NULL,
253                         &chunk_data->boulder_success[args->offset.x][args->offset.y][args->offset.z],
254                         &chunk_data->boulder_visit)))
255                 return NODE_STONE;
256
257         if (args->diff <= -5)
258                 return NODE_STONE;
259         else if (args->diff <= -1)
260                 return NODE_DIRT;
261         else if (args->diff <= 0)
262                 return NODE_GRASS;
263
264         return NODE_AIR;
265 }
266
267 BiomeDef biomes[COUNT_BIOME] = {
268         {
269                 .probability = 0.2,
270                 .offset = OFFSET_MOUNTAIN,
271                 .threshold = 1024.0,
272                 .snow = true,
273                 .height = &height_mountain,
274                 .generate = &generate_mountain,
275                 .chunk_data_size = 0,
276                 .before_chunk = NULL,
277                 .after_chunk = NULL,
278                 .row_data_size = 0,
279                 .before_row = NULL,
280                 .after_row = NULL,
281         },
282         {
283                 .probability = 0.2,
284                 .offset = OFFSET_OCEAN,
285                 .threshold = 2048.0,
286                 .snow = false,
287                 .height = &height_ocean,
288                 .generate = &generate_ocean,
289                 .chunk_data_size = sizeof(OceanChunkData),
290                 .before_chunk = &before_chunk_ocean,
291                 .after_chunk = NULL,
292                 .row_data_size = sizeof(OceanRowData),
293                 .before_row = &before_row_ocean,
294                 .after_row = NULL,
295         },
296         {
297                 .probability = 1.0,
298                 .offset = OFFSET_NONE,
299                 .threshold = 0.0,
300                 .snow = true,
301                 .height = &height_hills,
302                 .generate = &generate_hills,
303                 .chunk_data_size = sizeof(HillsChunkData),
304                 .before_chunk = &before_chunk_hills,
305                 .after_chunk = &after_chunk_hills,
306                 .row_data_size = 0,
307                 .before_row = NULL,
308                 .after_row = NULL,
309         },
310 };