3 Copyright (C) 2015-2020 paramat
4 Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
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.
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.
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.
26 #include "util/numeric.h"
31 FlagDesc flagdesc_ore
[] = {
32 {"absheight", OREFLAG_ABSHEIGHT
}, // Non-functional
33 {"puff_cliffs", OREFLAG_PUFF_CLIFFS
},
34 {"puff_additive_composition", OREFLAG_PUFF_ADDITIVE
},
39 ///////////////////////////////////////////////////////////////////////////////
42 OreManager::OreManager(IGameDef
*gamedef
) :
43 ObjDefManager(gamedef
, OBJDEF_ORE
)
48 size_t OreManager::placeAllOres(Mapgen
*mg
, u32 blockseed
, v3s16 nmin
, v3s16 nmax
)
52 for (size_t i
= 0; i
!= m_objects
.size(); i
++) {
53 Ore
*ore
= (Ore
*)m_objects
[i
];
57 nplaced
+= ore
->placeOre(mg
, blockseed
, nmin
, nmax
);
65 void OreManager::clear()
67 for (ObjDef
*object
: m_objects
) {
68 Ore
*ore
= (Ore
*) object
;
75 OreManager
*OreManager::clone() const
77 auto mgr
= new OreManager();
78 ObjDefManager::cloneTo(mgr
);
83 ///////////////////////////////////////////////////////////////////////////////
92 void Ore::resolveNodeNames()
94 getIdFromNrBacklog(&c_ore
, "", CONTENT_AIR
);
95 getIdsFromNrBacklog(&c_wherein
);
99 size_t Ore::placeOre(Mapgen
*mg
, u32 blockseed
, v3s16 nmin
, v3s16 nmax
)
101 if (nmin
.Y
> y_max
|| nmax
.Y
< y_min
)
104 int actual_ymin
= MYMAX(nmin
.Y
, y_min
);
105 int actual_ymax
= MYMIN(nmax
.Y
, y_max
);
106 if (clust_size
>= actual_ymax
- actual_ymin
+ 1)
109 nmin
.Y
= actual_ymin
;
110 nmax
.Y
= actual_ymax
;
111 generate(mg
->vm
, mg
->seed
, blockseed
, nmin
, nmax
, mg
->biomemap
);
117 void Ore::cloneTo(Ore
*def
) const
119 ObjDef::cloneTo(def
);
120 NodeResolver::cloneTo(def
);
122 def
->c_wherein
= c_wherein
;
123 def
->clust_scarcity
= clust_scarcity
;
124 def
->clust_num_ores
= clust_num_ores
;
125 def
->clust_size
= clust_size
;
128 def
->ore_param2
= ore_param2
;
130 def
->nthresh
= nthresh
;
132 def
->noise
= nullptr; // cannot be shared! so created on demand
133 def
->biomes
= biomes
;
137 ///////////////////////////////////////////////////////////////////////////////
140 ObjDef
*OreScatter::clone() const
142 auto def
= new OreScatter();
148 void OreScatter::generate(MMVManip
*vm
, int mapseed
, u32 blockseed
,
149 v3s16 nmin
, v3s16 nmax
, biome_t
*biomemap
)
151 PcgRandom
pr(blockseed
);
152 MapNode
n_ore(c_ore
, 0, ore_param2
);
154 u32 sizex
= (nmax
.X
- nmin
.X
+ 1);
155 u32 volume
= (nmax
.X
- nmin
.X
+ 1) *
156 (nmax
.Y
- nmin
.Y
+ 1) *
157 (nmax
.Z
- nmin
.Z
+ 1);
158 u32 csize
= clust_size
;
159 u32 cvolume
= csize
* csize
* csize
;
160 u32 nclusters
= volume
/ clust_scarcity
;
162 for (u32 i
= 0; i
!= nclusters
; i
++) {
163 int x0
= pr
.range(nmin
.X
, nmax
.X
- csize
+ 1);
164 int y0
= pr
.range(nmin
.Y
, nmax
.Y
- csize
+ 1);
165 int z0
= pr
.range(nmin
.Z
, nmax
.Z
- csize
+ 1);
167 if ((flags
& OREFLAG_USE_NOISE
) &&
168 (NoisePerlin3D(&np
, x0
, y0
, z0
, mapseed
) < nthresh
))
171 if (biomemap
&& !biomes
.empty()) {
172 u32 index
= sizex
* (z0
- nmin
.Z
) + (x0
- nmin
.X
);
173 auto it
= biomes
.find(biomemap
[index
]);
174 if (it
== biomes
.end())
178 for (u32 z1
= 0; z1
!= csize
; z1
++)
179 for (u32 y1
= 0; y1
!= csize
; y1
++)
180 for (u32 x1
= 0; x1
!= csize
; x1
++) {
181 if (pr
.range(1, cvolume
) > clust_num_ores
)
184 u32 i
= vm
->m_area
.index(x0
+ x1
, y0
+ y1
, z0
+ z1
);
185 if (!CONTAINS(c_wherein
, vm
->m_data
[i
].getContent()))
188 vm
->m_data
[i
] = n_ore
;
194 ///////////////////////////////////////////////////////////////////////////////
197 ObjDef
*OreSheet::clone() const
199 auto def
= new OreSheet();
202 def
->column_height_max
= column_height_max
;
203 def
->column_height_min
= column_height_min
;
204 def
->column_midpoint_factor
= column_midpoint_factor
;
210 void OreSheet::generate(MMVManip
*vm
, int mapseed
, u32 blockseed
,
211 v3s16 nmin
, v3s16 nmax
, biome_t
*biomemap
)
213 PcgRandom
pr(blockseed
+ 4234);
214 MapNode
n_ore(c_ore
, 0, ore_param2
);
216 u16 max_height
= column_height_max
;
217 int y_start_min
= nmin
.Y
+ max_height
;
218 int y_start_max
= nmax
.Y
- max_height
;
220 int y_start
= y_start_min
< y_start_max
?
221 pr
.range(y_start_min
, y_start_max
) :
222 (y_start_min
+ y_start_max
) / 2;
225 int sx
= nmax
.X
- nmin
.X
+ 1;
226 int sz
= nmax
.Z
- nmin
.Z
+ 1;
227 noise
= new Noise(&np
, 0, sx
, sz
);
229 noise
->seed
= mapseed
+ y_start
;
230 noise
->perlinMap2D(nmin
.X
, nmin
.Z
);
233 for (int z
= nmin
.Z
; z
<= nmax
.Z
; z
++)
234 for (int x
= nmin
.X
; x
<= nmax
.X
; x
++, index
++) {
235 float noiseval
= noise
->result
[index
];
236 if (noiseval
< nthresh
)
239 if (biomemap
&& !biomes
.empty()) {
240 auto it
= biomes
.find(biomemap
[index
]);
241 if (it
== biomes
.end())
245 u16 height
= pr
.range(column_height_min
, column_height_max
);
246 int ymidpoint
= y_start
+ noiseval
;
247 int y0
= MYMAX(nmin
.Y
, ymidpoint
- height
* (1 - column_midpoint_factor
));
248 int y1
= MYMIN(nmax
.Y
, y0
+ height
- 1);
250 for (int y
= y0
; y
<= y1
; y
++) {
251 u32 i
= vm
->m_area
.index(x
, y
, z
);
252 if (!vm
->m_area
.contains(i
))
254 if (!CONTAINS(c_wherein
, vm
->m_data
[i
].getContent()))
257 vm
->m_data
[i
] = n_ore
;
263 ///////////////////////////////////////////////////////////////////////////////
268 delete noise_puff_top
;
269 delete noise_puff_bottom
;
273 ObjDef
*OrePuff::clone() const
275 auto def
= new OrePuff();
278 def
->np_puff_top
= np_puff_top
;
279 def
->np_puff_bottom
= np_puff_bottom
;
280 def
->noise_puff_top
= nullptr; // cannot be shared, on-demand
281 def
->noise_puff_bottom
= nullptr;
287 void OrePuff::generate(MMVManip
*vm
, int mapseed
, u32 blockseed
,
288 v3s16 nmin
, v3s16 nmax
, biome_t
*biomemap
)
290 PcgRandom
pr(blockseed
+ 4234);
291 MapNode
n_ore(c_ore
, 0, ore_param2
);
293 int y_start
= pr
.range(nmin
.Y
, nmax
.Y
);
296 int sx
= nmax
.X
- nmin
.X
+ 1;
297 int sz
= nmax
.Z
- nmin
.Z
+ 1;
298 noise
= new Noise(&np
, 0, sx
, sz
);
299 noise_puff_top
= new Noise(&np_puff_top
, 0, sx
, sz
);
300 noise_puff_bottom
= new Noise(&np_puff_bottom
, 0, sx
, sz
);
303 noise
->seed
= mapseed
+ y_start
;
304 noise
->perlinMap2D(nmin
.X
, nmin
.Z
);
305 bool noise_generated
= false;
308 for (int z
= nmin
.Z
; z
<= nmax
.Z
; z
++)
309 for (int x
= nmin
.X
; x
<= nmax
.X
; x
++, index
++) {
310 float noiseval
= noise
->result
[index
];
311 if (noiseval
< nthresh
)
314 if (biomemap
&& !biomes
.empty()) {
315 auto it
= biomes
.find(biomemap
[index
]);
316 if (it
== biomes
.end())
320 if (!noise_generated
) {
321 noise_generated
= true;
322 noise_puff_top
->perlinMap2D(nmin
.X
, nmin
.Z
);
323 noise_puff_bottom
->perlinMap2D(nmin
.X
, nmin
.Z
);
326 float ntop
= noise_puff_top
->result
[index
];
327 float nbottom
= noise_puff_bottom
->result
[index
];
329 if (!(flags
& OREFLAG_PUFF_CLIFFS
)) {
330 float ndiff
= noiseval
- nthresh
;
338 int y0
= ymid
- nbottom
;
339 int y1
= ymid
+ ntop
;
341 if ((flags
& OREFLAG_PUFF_ADDITIVE
) && (y0
> y1
))
344 for (int y
= y0
; y
<= y1
; y
++) {
345 u32 i
= vm
->m_area
.index(x
, y
, z
);
346 if (!vm
->m_area
.contains(i
))
348 if (!CONTAINS(c_wherein
, vm
->m_data
[i
].getContent()))
351 vm
->m_data
[i
] = n_ore
;
357 ///////////////////////////////////////////////////////////////////////////////
360 ObjDef
*OreBlob::clone() const
362 auto def
= new OreBlob();
368 void OreBlob::generate(MMVManip
*vm
, int mapseed
, u32 blockseed
,
369 v3s16 nmin
, v3s16 nmax
, biome_t
*biomemap
)
371 PcgRandom
pr(blockseed
+ 2404);
372 MapNode
n_ore(c_ore
, 0, ore_param2
);
374 u32 sizex
= (nmax
.X
- nmin
.X
+ 1);
375 u32 volume
= (nmax
.X
- nmin
.X
+ 1) *
376 (nmax
.Y
- nmin
.Y
+ 1) *
377 (nmax
.Z
- nmin
.Z
+ 1);
378 u32 csize
= clust_size
;
379 u32 nblobs
= volume
/ clust_scarcity
;
382 noise
= new Noise(&np
, mapseed
, csize
, csize
, csize
);
384 for (u32 i
= 0; i
!= nblobs
; i
++) {
385 int x0
= pr
.range(nmin
.X
, nmax
.X
- csize
+ 1);
386 int y0
= pr
.range(nmin
.Y
, nmax
.Y
- csize
+ 1);
387 int z0
= pr
.range(nmin
.Z
, nmax
.Z
- csize
+ 1);
389 if (biomemap
&& !biomes
.empty()) {
390 u32 bmapidx
= sizex
* (z0
- nmin
.Z
) + (x0
- nmin
.X
);
391 auto it
= biomes
.find(biomemap
[bmapidx
]);
392 if (it
== biomes
.end())
396 bool noise_generated
= false;
397 noise
->seed
= blockseed
+ i
;
400 for (u32 z1
= 0; z1
!= csize
; z1
++)
401 for (u32 y1
= 0; y1
!= csize
; y1
++)
402 for (u32 x1
= 0; x1
!= csize
; x1
++, index
++) {
403 u32 i
= vm
->m_area
.index(x0
+ x1
, y0
+ y1
, z0
+ z1
);
404 if (!CONTAINS(c_wherein
, vm
->m_data
[i
].getContent()))
407 // Lazily generate noise only if there's a chance of ore being placed
408 // This simple optimization makes calls 6x faster on average
409 if (!noise_generated
) {
410 noise_generated
= true;
411 noise
->perlinMap3D(x0
, y0
, z0
);
414 float noiseval
= noise
->result
[index
];
416 float xdist
= (s32
)x1
- (s32
)csize
/ 2;
417 float ydist
= (s32
)y1
- (s32
)csize
/ 2;
418 float zdist
= (s32
)z1
- (s32
)csize
/ 2;
420 noiseval
-= std::sqrt(xdist
* xdist
+ ydist
* ydist
+ zdist
* zdist
) / csize
;
422 if (noiseval
< nthresh
)
425 vm
->m_data
[i
] = n_ore
;
431 ///////////////////////////////////////////////////////////////////////////////
440 ObjDef
*OreVein::clone() const
442 auto def
= new OreVein();
445 def
->random_factor
= random_factor
;
446 def
->noise2
= nullptr; // cannot be shared, on-demand
447 def
->sizey_prev
= sizey_prev
;
453 void OreVein::generate(MMVManip
*vm
, int mapseed
, u32 blockseed
,
454 v3s16 nmin
, v3s16 nmax
, biome_t
*biomemap
)
456 PcgRandom
pr(blockseed
+ 520);
457 MapNode
n_ore(c_ore
, 0, ore_param2
);
459 int sizex
= nmax
.X
- nmin
.X
+ 1;
460 int sizey
= nmax
.Y
- nmin
.Y
+ 1;
461 // Because this ore uses 3D noise the perlinmap Y size can be different in
462 // different mapchunks due to ore Y limits. So recreate the noise objects
463 // if Y size has changed.
464 // Because these noise objects are created multiple times for this ore type
465 // it is necessary to 'delete' them here.
466 if (!noise
|| sizey
!= sizey_prev
) {
469 int sizez
= nmax
.Z
- nmin
.Z
+ 1;
470 noise
= new Noise(&np
, mapseed
, sizex
, sizey
, sizez
);
471 noise2
= new Noise(&np
, mapseed
+ 436, sizex
, sizey
, sizez
);
475 bool noise_generated
= false;
477 for (int z
= nmin
.Z
; z
<= nmax
.Z
; z
++)
478 for (int y
= nmin
.Y
; y
<= nmax
.Y
; y
++)
479 for (int x
= nmin
.X
; x
<= nmax
.X
; x
++, index
++) {
480 u32 i
= vm
->m_area
.index(x
, y
, z
);
481 if (!vm
->m_area
.contains(i
))
483 if (!CONTAINS(c_wherein
, vm
->m_data
[i
].getContent()))
486 if (biomemap
&& !biomes
.empty()) {
487 u32 bmapidx
= sizex
* (z
- nmin
.Z
) + (x
- nmin
.X
);
488 auto it
= biomes
.find(biomemap
[bmapidx
]);
489 if (it
== biomes
.end())
493 // Same lazy generation optimization as in OreBlob
494 if (!noise_generated
) {
495 noise_generated
= true;
496 noise
->perlinMap3D(nmin
.X
, nmin
.Y
, nmin
.Z
);
497 noise2
->perlinMap3D(nmin
.X
, nmin
.Y
, nmin
.Z
);
500 // randval ranges from -1..1
502 Note: can generate values slightly larger than 1
503 but this can't be changed as mapgen must be deterministic accross versions.
505 float randval
= (float)pr
.next() / float(pr
.RANDOM_RANGE
/ 2) - 1.f
;
506 float noiseval
= contour(noise
->result
[index
]);
507 float noiseval2
= contour(noise2
->result
[index
]);
508 if (noiseval
* noiseval2
+ randval
* random_factor
< nthresh
)
511 vm
->m_data
[i
] = n_ore
;
516 ///////////////////////////////////////////////////////////////////////////////
519 OreStratum::~OreStratum()
521 delete noise_stratum_thickness
;
525 ObjDef
*OreStratum::clone() const
527 auto def
= new OreStratum();
530 def
->np_stratum_thickness
= np_stratum_thickness
;
531 def
->noise_stratum_thickness
= nullptr; // cannot be shared, on-demand
532 def
->stratum_thickness
= stratum_thickness
;
538 void OreStratum::generate(MMVManip
*vm
, int mapseed
, u32 blockseed
,
539 v3s16 nmin
, v3s16 nmax
, biome_t
*biomemap
)
541 PcgRandom
pr(blockseed
+ 4234);
542 MapNode
n_ore(c_ore
, 0, ore_param2
);
544 if (flags
& OREFLAG_USE_NOISE
) {
546 int sx
= nmax
.X
- nmin
.X
+ 1;
547 int sz
= nmax
.Z
- nmin
.Z
+ 1;
548 noise
= new Noise(&np
, 0, sx
, sz
);
550 noise
->perlinMap2D(nmin
.X
, nmin
.Z
);
553 if (flags
& OREFLAG_USE_NOISE2
) {
554 if (!noise_stratum_thickness
) {
555 int sx
= nmax
.X
- nmin
.X
+ 1;
556 int sz
= nmax
.Z
- nmin
.Z
+ 1;
557 noise_stratum_thickness
= new Noise(&np_stratum_thickness
, 0, sx
, sz
);
559 noise_stratum_thickness
->perlinMap2D(nmin
.X
, nmin
.Z
);
564 for (int z
= nmin
.Z
; z
<= nmax
.Z
; z
++)
565 for (int x
= nmin
.X
; x
<= nmax
.X
; x
++, index
++) {
566 if (biomemap
&& !biomes
.empty()) {
567 auto it
= biomes
.find(biomemap
[index
]);
568 if (it
== biomes
.end())
575 if (flags
& OREFLAG_USE_NOISE
) {
576 float nhalfthick
= ((flags
& OREFLAG_USE_NOISE2
) ?
577 noise_stratum_thickness
->result
[index
] : (float)stratum_thickness
) /
579 float nmid
= noise
->result
[index
];
580 y0
= MYMAX(nmin
.Y
, std::ceil(nmid
- nhalfthick
));
581 y1
= MYMIN(nmax
.Y
, nmid
+ nhalfthick
);
582 } else { // Simple horizontal stratum
587 for (int y
= y0
; y
<= y1
; y
++) {
588 if (pr
.range(1, clust_scarcity
) != 1)
591 u32 i
= vm
->m_area
.index(x
, y
, z
);
592 if (!vm
->m_area
.contains(i
))
594 if (!CONTAINS(c_wherein
, vm
->m_data
[i
].getContent()))
597 vm
->m_data
[i
] = n_ore
;