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