3 Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 FlagDesc flagdesc_ore[] = {
29 {"absheight", OREFLAG_ABSHEIGHT},
30 {"puff_cliffs", OREFLAG_PUFF_CLIFFS},
31 {"puff_additive_composition", OREFLAG_PUFF_ADDITIVE},
36 ///////////////////////////////////////////////////////////////////////////////
39 OreManager::OreManager(IGameDef *gamedef) :
40 ObjDefManager(gamedef, OBJDEF_ORE)
45 size_t OreManager::placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
49 for (size_t i = 0; i != m_objects.size(); i++) {
50 Ore *ore = (Ore *)m_objects[i];
54 nplaced += ore->placeOre(mg, blockseed, nmin, nmax);
62 void OreManager::clear()
64 for (size_t i = 0; i < m_objects.size(); i++) {
65 Ore *ore = (Ore *)m_objects[i];
72 ///////////////////////////////////////////////////////////////////////////////
88 void Ore::resolveNodeNames()
90 getIdFromNrBacklog(&c_ore, "", CONTENT_AIR);
91 getIdsFromNrBacklog(&c_wherein);
95 size_t Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
99 in_range |= (nmin.Y <= y_max && nmax.Y >= y_min);
100 if (flags & OREFLAG_ABSHEIGHT)
101 in_range |= (nmin.Y >= -y_max && nmax.Y <= -y_min) << 1;
105 int actual_ymin, actual_ymax;
106 if (in_range & ORE_RANGE_MIRROR) {
107 actual_ymin = MYMAX(nmin.Y, -y_max);
108 actual_ymax = MYMIN(nmax.Y, -y_min);
110 actual_ymin = MYMAX(nmin.Y, y_min);
111 actual_ymax = MYMIN(nmax.Y, y_max);
113 if (clust_size >= actual_ymax - actual_ymin + 1)
116 nmin.Y = actual_ymin;
117 nmax.Y = actual_ymax;
118 generate(mg->vm, mg->seed, blockseed, nmin, nmax, mg->biomemap);
124 ///////////////////////////////////////////////////////////////////////////////
127 void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
128 v3s16 nmin, v3s16 nmax, u8 *biomemap)
130 PcgRandom pr(blockseed);
131 MapNode n_ore(c_ore, 0, ore_param2);
133 u32 sizex = (nmax.X - nmin.X + 1);
134 u32 volume = (nmax.X - nmin.X + 1) *
135 (nmax.Y - nmin.Y + 1) *
136 (nmax.Z - nmin.Z + 1);
137 u32 csize = clust_size;
138 u32 cvolume = csize * csize * csize;
139 u32 nclusters = volume / clust_scarcity;
141 for (u32 i = 0; i != nclusters; i++) {
142 int x0 = pr.range(nmin.X, nmax.X - csize + 1);
143 int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
144 int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
146 if ((flags & OREFLAG_USE_NOISE) &&
147 (NoisePerlin3D(&np, x0, y0, z0, mapseed) < nthresh))
150 if (biomemap && !biomes.empty()) {
151 u32 index = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
152 UNORDERED_SET<u8>::iterator it = biomes.find(biomemap[index]);
153 if (it == biomes.end())
157 for (u32 z1 = 0; z1 != csize; z1++)
158 for (u32 y1 = 0; y1 != csize; y1++)
159 for (u32 x1 = 0; x1 != csize; x1++) {
160 if (pr.range(1, cvolume) > clust_num_ores)
163 u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
164 if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
167 vm->m_data[i] = n_ore;
173 ///////////////////////////////////////////////////////////////////////////////
176 void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
177 v3s16 nmin, v3s16 nmax, u8 *biomemap)
179 PcgRandom pr(blockseed + 4234);
180 MapNode n_ore(c_ore, 0, ore_param2);
182 u16 max_height = column_height_max;
183 int y_start_min = nmin.Y + max_height;
184 int y_start_max = nmax.Y - max_height;
186 int y_start = y_start_min < y_start_max ?
187 pr.range(y_start_min, y_start_max) :
188 (y_start_min + y_start_max) / 2;
191 int sx = nmax.X - nmin.X + 1;
192 int sz = nmax.Z - nmin.Z + 1;
193 noise = new Noise(&np, 0, sx, sz);
195 noise->seed = mapseed + y_start;
196 noise->perlinMap2D(nmin.X, nmin.Z);
199 for (int z = nmin.Z; z <= nmax.Z; z++)
200 for (int x = nmin.X; x <= nmax.X; x++, index++) {
201 float noiseval = noise->result[index];
202 if (noiseval < nthresh)
205 if (biomemap && !biomes.empty()) {
206 UNORDERED_SET<u8>::iterator it = biomes.find(biomemap[index]);
207 if (it == biomes.end())
211 u16 height = pr.range(column_height_min, column_height_max);
212 int ymidpoint = y_start + noiseval;
213 int y0 = MYMAX(nmin.Y, ymidpoint - height * (1 - column_midpoint_factor));
214 int y1 = MYMIN(nmax.Y, y0 + height - 1);
216 for (int y = y0; y <= y1; y++) {
217 u32 i = vm->m_area.index(x, y, z);
218 if (!vm->m_area.contains(i))
220 if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
223 vm->m_data[i] = n_ore;
229 ///////////////////////////////////////////////////////////////////////////////
234 noise_puff_top = NULL;
235 noise_puff_bottom = NULL;
241 delete noise_puff_top;
242 delete noise_puff_bottom;
246 void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed,
247 v3s16 nmin, v3s16 nmax, u8 *biomemap)
249 PcgRandom pr(blockseed + 4234);
250 MapNode n_ore(c_ore, 0, ore_param2);
252 int y_start = pr.range(nmin.Y, nmax.Y);
255 int sx = nmax.X - nmin.X + 1;
256 int sz = nmax.Z - nmin.Z + 1;
257 noise = new Noise(&np, 0, sx, sz);
258 noise_puff_top = new Noise(&np_puff_top, 0, sx, sz);
259 noise_puff_bottom = new Noise(&np_puff_bottom, 0, sx, sz);
262 noise->seed = mapseed + y_start;
263 noise->perlinMap2D(nmin.X, nmin.Z);
264 bool noise_generated = false;
267 for (int z = nmin.Z; z <= nmax.Z; z++)
268 for (int x = nmin.X; x <= nmax.X; x++, index++) {
269 float noiseval = noise->result[index];
270 if (noiseval < nthresh)
273 if (biomemap && !biomes.empty()) {
274 UNORDERED_SET<u8>::iterator it = biomes.find(biomemap[index]);
275 if (it == biomes.end())
279 if (!noise_generated) {
280 noise_generated = true;
281 noise_puff_top->perlinMap2D(nmin.X, nmin.Z);
282 noise_puff_bottom->perlinMap2D(nmin.X, nmin.Z);
285 float ntop = noise_puff_top->result[index];
286 float nbottom = noise_puff_bottom->result[index];
288 if (!(flags & OREFLAG_PUFF_CLIFFS)) {
289 float ndiff = noiseval - nthresh;
297 int y0 = ymid - nbottom;
298 int y1 = ymid + ntop;
300 if ((flags & OREFLAG_PUFF_ADDITIVE) && (y0 > y1))
303 for (int y = y0; y <= y1; y++) {
304 u32 i = vm->m_area.index(x, y, z);
305 if (!vm->m_area.contains(i))
307 if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
310 vm->m_data[i] = n_ore;
316 ///////////////////////////////////////////////////////////////////////////////
319 void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
320 v3s16 nmin, v3s16 nmax, u8 *biomemap)
322 PcgRandom pr(blockseed + 2404);
323 MapNode n_ore(c_ore, 0, ore_param2);
325 u32 sizex = (nmax.X - nmin.X + 1);
326 u32 volume = (nmax.X - nmin.X + 1) *
327 (nmax.Y - nmin.Y + 1) *
328 (nmax.Z - nmin.Z + 1);
329 u32 csize = clust_size;
330 u32 nblobs = volume / clust_scarcity;
333 noise = new Noise(&np, mapseed, csize, csize, csize);
335 for (u32 i = 0; i != nblobs; i++) {
336 int x0 = pr.range(nmin.X, nmax.X - csize + 1);
337 int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
338 int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
340 if (biomemap && !biomes.empty()) {
341 u32 bmapidx = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
342 UNORDERED_SET<u8>::iterator it = biomes.find(biomemap[bmapidx]);
343 if (it == biomes.end())
347 bool noise_generated = false;
348 noise->seed = blockseed + i;
351 for (u32 z1 = 0; z1 != csize; z1++)
352 for (u32 y1 = 0; y1 != csize; y1++)
353 for (u32 x1 = 0; x1 != csize; x1++, index++) {
354 u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
355 if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
358 // Lazily generate noise only if there's a chance of ore being placed
359 // This simple optimization makes calls 6x faster on average
360 if (!noise_generated) {
361 noise_generated = true;
362 noise->perlinMap3D(x0, y0, z0);
365 float noiseval = noise->result[index];
367 float xdist = (s32)x1 - (s32)csize / 2;
368 float ydist = (s32)y1 - (s32)csize / 2;
369 float zdist = (s32)z1 - (s32)csize / 2;
371 noiseval -= (sqrt(xdist * xdist + ydist * ydist + zdist * zdist) / csize);
373 if (noiseval < nthresh)
376 vm->m_data[i] = n_ore;
382 ///////////////////////////////////////////////////////////////////////////////
397 void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
398 v3s16 nmin, v3s16 nmax, u8 *biomemap)
400 PcgRandom pr(blockseed + 520);
401 MapNode n_ore(c_ore, 0, ore_param2);
403 u32 sizex = (nmax.X - nmin.X + 1);
406 int sx = nmax.X - nmin.X + 1;
407 int sy = nmax.Y - nmin.Y + 1;
408 int sz = nmax.Z - nmin.Z + 1;
409 noise = new Noise(&np, mapseed, sx, sy, sz);
410 noise2 = new Noise(&np, mapseed + 436, sx, sy, sz);
412 bool noise_generated = false;
415 for (int z = nmin.Z; z <= nmax.Z; z++)
416 for (int y = nmin.Y; y <= nmax.Y; y++)
417 for (int x = nmin.X; x <= nmax.X; x++, index++) {
418 u32 i = vm->m_area.index(x, y, z);
419 if (!vm->m_area.contains(i))
421 if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
424 if (biomemap && !biomes.empty()) {
425 u32 bmapidx = sizex * (z - nmin.Z) + (x - nmin.X);
426 UNORDERED_SET<u8>::iterator it = biomes.find(biomemap[bmapidx]);
427 if (it == biomes.end())
431 // Same lazy generation optimization as in OreBlob
432 if (!noise_generated) {
433 noise_generated = true;
434 noise->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
435 noise2->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
438 // randval ranges from -1..1
439 float randval = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f;
440 float noiseval = contour(noise->result[index]);
441 float noiseval2 = contour(noise2->result[index]);
442 if (noiseval * noiseval2 + randval * random_factor < nthresh)
445 vm->m_data[i] = n_ore;