]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapgen/mg_ore.cpp
Initialise 'seabed_height' to avoid compilation warning (#8715)
[dragonfireclient.git] / src / mapgen / mg_ore.cpp
1 /*
2 Minetest
3 Copyright (C) 2014-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
4 Copyright (C) 2015-2018 paramat
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "mg_ore.h"
22 #include "mapgen.h"
23 #include "noise.h"
24 #include "map.h"
25 #include "log.h"
26 #include "util/numeric.h"
27 #include <cmath>
28 #include <algorithm>
29
30
31 FlagDesc flagdesc_ore[] = {
32         {"absheight",                 OREFLAG_ABSHEIGHT}, // Non-functional
33         {"puff_cliffs",               OREFLAG_PUFF_CLIFFS},
34         {"puff_additive_composition", OREFLAG_PUFF_ADDITIVE},
35         {NULL,                        0}
36 };
37
38
39 ///////////////////////////////////////////////////////////////////////////////
40
41
42 OreManager::OreManager(IGameDef *gamedef) :
43         ObjDefManager(gamedef, OBJDEF_ORE)
44 {
45 }
46
47
48 size_t OreManager::placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
49 {
50         size_t nplaced = 0;
51
52         for (size_t i = 0; i != m_objects.size(); i++) {
53                 Ore *ore = (Ore *)m_objects[i];
54                 if (!ore)
55                         continue;
56
57                 nplaced += ore->placeOre(mg, blockseed, nmin, nmax);
58                 blockseed++;
59         }
60
61         return nplaced;
62 }
63
64
65 void OreManager::clear()
66 {
67         for (ObjDef *object : m_objects) {
68                 Ore *ore = (Ore *) object;
69                 delete ore;
70         }
71         m_objects.clear();
72 }
73
74
75 ///////////////////////////////////////////////////////////////////////////////
76
77
78 Ore::~Ore()
79 {
80         delete noise;
81 }
82
83
84 void Ore::resolveNodeNames()
85 {
86         getIdFromNrBacklog(&c_ore, "", CONTENT_AIR);
87         getIdsFromNrBacklog(&c_wherein);
88 }
89
90
91 size_t Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
92 {
93         if (nmin.Y > y_max || nmax.Y < y_min)
94                 return 0;
95
96         int actual_ymin = MYMAX(nmin.Y, y_min);
97         int actual_ymax = MYMIN(nmax.Y, y_max);
98         if (clust_size >= actual_ymax - actual_ymin + 1)
99                 return 0;
100
101         nmin.Y = actual_ymin;
102         nmax.Y = actual_ymax;
103         generate(mg->vm, mg->seed, blockseed, nmin, nmax, mg->biomemap);
104
105         return 1;
106 }
107
108
109 ///////////////////////////////////////////////////////////////////////////////
110
111
112 void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
113         v3s16 nmin, v3s16 nmax, u8 *biomemap)
114 {
115         PcgRandom pr(blockseed);
116         MapNode n_ore(c_ore, 0, ore_param2);
117
118         u32 sizex  = (nmax.X - nmin.X + 1);
119         u32 volume = (nmax.X - nmin.X + 1) *
120                                  (nmax.Y - nmin.Y + 1) *
121                                  (nmax.Z - nmin.Z + 1);
122         u32 csize     = clust_size;
123         u32 cvolume    = csize * csize * csize;
124         u32 nclusters = volume / clust_scarcity;
125
126         for (u32 i = 0; i != nclusters; i++) {
127                 int x0 = pr.range(nmin.X, nmax.X - csize + 1);
128                 int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
129                 int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
130
131                 if ((flags & OREFLAG_USE_NOISE) &&
132                         (NoisePerlin3D(&np, x0, y0, z0, mapseed) < nthresh))
133                         continue;
134
135                 if (biomemap && !biomes.empty()) {
136                         u32 index = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
137                         std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
138                         if (it == biomes.end())
139                                 continue;
140                 }
141
142                 for (u32 z1 = 0; z1 != csize; z1++)
143                 for (u32 y1 = 0; y1 != csize; y1++)
144                 for (u32 x1 = 0; x1 != csize; x1++) {
145                         if (pr.range(1, cvolume) > clust_num_ores)
146                                 continue;
147
148                         u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
149                         if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
150                                 continue;
151
152                         vm->m_data[i] = n_ore;
153                 }
154         }
155 }
156
157
158 ///////////////////////////////////////////////////////////////////////////////
159
160
161 void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
162         v3s16 nmin, v3s16 nmax, u8 *biomemap)
163 {
164         PcgRandom pr(blockseed + 4234);
165         MapNode n_ore(c_ore, 0, ore_param2);
166
167         u16 max_height = column_height_max;
168         int y_start_min = nmin.Y + max_height;
169         int y_start_max = nmax.Y - max_height;
170
171         int y_start = y_start_min < y_start_max ?
172                 pr.range(y_start_min, y_start_max) :
173                 (y_start_min + y_start_max) / 2;
174
175         if (!noise) {
176                 int sx = nmax.X - nmin.X + 1;
177                 int sz = nmax.Z - nmin.Z + 1;
178                 noise = new Noise(&np, 0, sx, sz);
179         }
180         noise->seed = mapseed + y_start;
181         noise->perlinMap2D(nmin.X, nmin.Z);
182
183         size_t index = 0;
184         for (int z = nmin.Z; z <= nmax.Z; z++)
185         for (int x = nmin.X; x <= nmax.X; x++, index++) {
186                 float noiseval = noise->result[index];
187                 if (noiseval < nthresh)
188                         continue;
189
190                 if (biomemap && !biomes.empty()) {
191                         std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
192                         if (it == biomes.end())
193                                 continue;
194                 }
195
196                 u16 height = pr.range(column_height_min, column_height_max);
197                 int ymidpoint = y_start + noiseval;
198                 int y0 = MYMAX(nmin.Y, ymidpoint - height * (1 - column_midpoint_factor));
199                 int y1 = MYMIN(nmax.Y, y0 + height - 1);
200
201                 for (int y = y0; y <= y1; y++) {
202                         u32 i = vm->m_area.index(x, y, z);
203                         if (!vm->m_area.contains(i))
204                                 continue;
205                         if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
206                                 continue;
207
208                         vm->m_data[i] = n_ore;
209                 }
210         }
211 }
212
213
214 ///////////////////////////////////////////////////////////////////////////////
215
216
217 OrePuff::~OrePuff()
218 {
219         delete noise_puff_top;
220         delete noise_puff_bottom;
221 }
222
223
224 void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed,
225         v3s16 nmin, v3s16 nmax, u8 *biomemap)
226 {
227         PcgRandom pr(blockseed + 4234);
228         MapNode n_ore(c_ore, 0, ore_param2);
229
230         int y_start = pr.range(nmin.Y, nmax.Y);
231
232         if (!noise) {
233                 int sx = nmax.X - nmin.X + 1;
234                 int sz = nmax.Z - nmin.Z + 1;
235                 noise = new Noise(&np, 0, sx, sz);
236                 noise_puff_top = new Noise(&np_puff_top, 0, sx, sz);
237                 noise_puff_bottom = new Noise(&np_puff_bottom, 0, sx, sz);
238         }
239
240         noise->seed = mapseed + y_start;
241         noise->perlinMap2D(nmin.X, nmin.Z);
242         bool noise_generated = false;
243
244         size_t index = 0;
245         for (int z = nmin.Z; z <= nmax.Z; z++)
246         for (int x = nmin.X; x <= nmax.X; x++, index++) {
247                 float noiseval = noise->result[index];
248                 if (noiseval < nthresh)
249                         continue;
250
251                 if (biomemap && !biomes.empty()) {
252                         std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
253                         if (it == biomes.end())
254                                 continue;
255                 }
256
257                 if (!noise_generated) {
258                         noise_generated = true;
259                         noise_puff_top->perlinMap2D(nmin.X, nmin.Z);
260                         noise_puff_bottom->perlinMap2D(nmin.X, nmin.Z);
261                 }
262
263                 float ntop    = noise_puff_top->result[index];
264                 float nbottom = noise_puff_bottom->result[index];
265
266                 if (!(flags & OREFLAG_PUFF_CLIFFS)) {
267                         float ndiff = noiseval - nthresh;
268                         if (ndiff < 1.0f) {
269                                 ntop *= ndiff;
270                                 nbottom *= ndiff;
271                         }
272                 }
273
274                 int ymid = y_start;
275                 int y0 = ymid - nbottom;
276                 int y1 = ymid + ntop;
277
278                 if ((flags & OREFLAG_PUFF_ADDITIVE) && (y0 > y1))
279                         SWAP(int, y0, y1);
280
281                 for (int y = y0; y <= y1; y++) {
282                         u32 i = vm->m_area.index(x, y, z);
283                         if (!vm->m_area.contains(i))
284                                 continue;
285                         if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
286                                 continue;
287
288                         vm->m_data[i] = n_ore;
289                 }
290         }
291 }
292
293
294 ///////////////////////////////////////////////////////////////////////////////
295
296
297 void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
298         v3s16 nmin, v3s16 nmax, u8 *biomemap)
299 {
300         PcgRandom pr(blockseed + 2404);
301         MapNode n_ore(c_ore, 0, ore_param2);
302
303         u32 sizex  = (nmax.X - nmin.X + 1);
304         u32 volume = (nmax.X - nmin.X + 1) *
305                                  (nmax.Y - nmin.Y + 1) *
306                                  (nmax.Z - nmin.Z + 1);
307         u32 csize  = clust_size;
308         u32 nblobs = volume / clust_scarcity;
309
310         if (!noise)
311                 noise = new Noise(&np, mapseed, csize, csize, csize);
312
313         for (u32 i = 0; i != nblobs; i++) {
314                 int x0 = pr.range(nmin.X, nmax.X - csize + 1);
315                 int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
316                 int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
317
318                 if (biomemap && !biomes.empty()) {
319                         u32 bmapidx = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
320                         std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[bmapidx]);
321                         if (it == biomes.end())
322                                 continue;
323                 }
324
325                 bool noise_generated = false;
326                 noise->seed = blockseed + i;
327
328                 size_t index = 0;
329                 for (u32 z1 = 0; z1 != csize; z1++)
330                 for (u32 y1 = 0; y1 != csize; y1++)
331                 for (u32 x1 = 0; x1 != csize; x1++, index++) {
332                         u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
333                         if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
334                                 continue;
335
336                         // Lazily generate noise only if there's a chance of ore being placed
337                         // This simple optimization makes calls 6x faster on average
338                         if (!noise_generated) {
339                                 noise_generated = true;
340                                 noise->perlinMap3D(x0, y0, z0);
341                         }
342
343                         float noiseval = noise->result[index];
344
345                         float xdist = (s32)x1 - (s32)csize / 2;
346                         float ydist = (s32)y1 - (s32)csize / 2;
347                         float zdist = (s32)z1 - (s32)csize / 2;
348
349                         noiseval -= std::sqrt(xdist * xdist + ydist * ydist + zdist * zdist) / csize;
350
351                         if (noiseval < nthresh)
352                                 continue;
353
354                         vm->m_data[i] = n_ore;
355                 }
356         }
357 }
358
359
360 ///////////////////////////////////////////////////////////////////////////////
361
362
363 OreVein::~OreVein()
364 {
365         delete noise2;
366 }
367
368
369 void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
370         v3s16 nmin, v3s16 nmax, u8 *biomemap)
371 {
372         PcgRandom pr(blockseed + 520);
373         MapNode n_ore(c_ore, 0, ore_param2);
374
375         int sizex = nmax.X - nmin.X + 1;
376         int sizey = nmax.Y - nmin.Y + 1;
377         // Because this ore uses 3D noise the perlinmap Y size can be different in
378         // different mapchunks due to ore Y limits. So recreate the noise objects
379         // if Y size has changed.
380         // Because these noise objects are created multiple times for this ore type
381         // it is necessary to 'delete' them here.
382         if (!noise || sizey != sizey_prev) {
383                 delete noise;
384                 delete noise2;
385                 int sizez = nmax.Z - nmin.Z + 1;
386                 noise  = new Noise(&np, mapseed, sizex, sizey, sizez);
387                 noise2 = new Noise(&np, mapseed + 436, sizex, sizey, sizez);
388                 sizey_prev = sizey;
389         }
390
391         bool noise_generated = false;
392         size_t index = 0;
393         for (int z = nmin.Z; z <= nmax.Z; z++)
394         for (int y = nmin.Y; y <= nmax.Y; y++)
395         for (int x = nmin.X; x <= nmax.X; x++, index++) {
396                 u32 i = vm->m_area.index(x, y, z);
397                 if (!vm->m_area.contains(i))
398                         continue;
399                 if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
400                         continue;
401
402                 if (biomemap && !biomes.empty()) {
403                         u32 bmapidx = sizex * (z - nmin.Z) + (x - nmin.X);
404                         std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[bmapidx]);
405                         if (it == biomes.end())
406                                 continue;
407                 }
408
409                 // Same lazy generation optimization as in OreBlob
410                 if (!noise_generated) {
411                         noise_generated = true;
412                         noise->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
413                         noise2->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
414                 }
415
416                 // randval ranges from -1..1
417                 float randval   = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f;
418                 float noiseval  = contour(noise->result[index]);
419                 float noiseval2 = contour(noise2->result[index]);
420                 if (noiseval * noiseval2 + randval * random_factor < nthresh)
421                         continue;
422
423                 vm->m_data[i] = n_ore;
424         }
425 }
426
427
428 ///////////////////////////////////////////////////////////////////////////////
429
430
431 OreStratum::~OreStratum()
432 {
433         delete noise_stratum_thickness;
434 }
435
436
437 void OreStratum::generate(MMVManip *vm, int mapseed, u32 blockseed,
438         v3s16 nmin, v3s16 nmax, u8 *biomemap)
439 {
440         PcgRandom pr(blockseed + 4234);
441         MapNode n_ore(c_ore, 0, ore_param2);
442
443         if (flags & OREFLAG_USE_NOISE) {
444                 if (!noise) {
445                         int sx = nmax.X - nmin.X + 1;
446                         int sz = nmax.Z - nmin.Z + 1;
447                         noise = new Noise(&np, 0, sx, sz);
448                 }
449                 noise->perlinMap2D(nmin.X, nmin.Z);
450         }
451
452         if (flags & OREFLAG_USE_NOISE2) {
453                 if (!noise_stratum_thickness) {
454                         int sx = nmax.X - nmin.X + 1;
455                         int sz = nmax.Z - nmin.Z + 1;
456                         noise_stratum_thickness = new Noise(&np_stratum_thickness, 0, sx, sz);
457                 }
458                 noise_stratum_thickness->perlinMap2D(nmin.X, nmin.Z);
459         }
460
461         size_t index = 0;
462
463         for (int z = nmin.Z; z <= nmax.Z; z++)
464         for (int x = nmin.X; x <= nmax.X; x++, index++) {
465                 if (biomemap && !biomes.empty()) {
466                         std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
467                         if (it == biomes.end())
468                                 continue;
469                 }
470
471                 int y0;
472                 int y1;
473
474                 if (flags & OREFLAG_USE_NOISE) {
475                         float nhalfthick = ((flags & OREFLAG_USE_NOISE2) ?
476                                 noise_stratum_thickness->result[index] : (float)stratum_thickness) /
477                                 2.0f;
478                         float nmid = noise->result[index];
479                         y0 = MYMAX(nmin.Y, std::ceil(nmid - nhalfthick));
480                         y1 = MYMIN(nmax.Y, nmid + nhalfthick);
481                 } else { // Simple horizontal stratum
482                         y0 = nmin.Y;
483                         y1 = nmax.Y;
484                 }
485
486                 for (int y = y0; y <= y1; y++) {
487                         if (pr.range(1, clust_scarcity) != 1)
488                                 continue;
489
490                         u32 i = vm->m_area.index(x, y, z);
491                         if (!vm->m_area.contains(i))
492                                 continue;
493                         if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
494                                 continue;
495
496                         vm->m_data[i] = n_ore;
497                 }
498         }
499 }