aboutsummaryrefslogtreecommitdiff
path: root/src/content_cao.cpp
Commit message (Expand)AuthorAge
* Move object nametags to cameraRealBadAngel2016-02-18
* v2d & aabbox3d<f32> & sky cleanupsnerzhul2016-02-11
* Don't pass non-const references to collision methodsest312016-01-29
* Revert "Add support for using arbitrary meshes as items"Sapier2015-12-29
* Add support for using arbitrary meshes as itemsSapier2015-12-29
* Add support for limiting rotation of automatic face movement dir entitysSapier2015-12-19
* Add option to give every object a nametagBlockMen2015-12-15
* Add option to disable backface culling for modelsBlockMen2015-10-25
* Fix enforcing of nametag hidingest312015-10-15
* Rename macros with two leading underscoresShadowNinja2015-10-14
* Use warningstream for log messages with WARNINGShadowNinja2015-10-14
* Change i++ to ++iDavid Jones2015-08-25
* Fix issues with light of attached CAOsRealBadAngel2015-07-23
* Use UTF-8 instead of narrowest312015-07-08
* Fix some issues with animations, and allow non-looped animations to be definedMirceaKitsune2015-06-22
* Small CAO improvementsest312015-06-20
* Generic CAO cleanups and renames for clarificationest312015-06-20
* Make attached objects visible in 3rd person viewest312015-06-20
* Don't ignore nametag attribute change message for newer versionsest312015-05-21
* Enforce hiding nametagest312015-05-18
* Make nametags white againest312015-05-18
* Add -Wsign-compare for Clang builds and fix some signed/unsigned compiler war...kwolekr2015-05-16
* Generalize core.get/set_nametag_color into core.get/set_nametag_attributesTeTpaAka2015-05-15
* Add get and set functions for the nametag colorTeTpaAka2015-05-15
* Move globals from main.cpp to more sane locationsCraig Robbins2015-04-01
* Move texture_min_size even further down the pipe. Now, textures are JIT-upsca...Aaron Suen2015-03-31
* For usages of assert() that are meant to persist in Release builds (when NDEB...Craig Robbins2015-03-07
* Replace std::list to std::vector into tile.cpp (m_texture_trash) and move til...Loic Blot2015-03-05
* Use skin font for usernames (fixes #2363)BlockMen2015-02-21
* SAO work: ActiveObject types & SAO cleanup * Replace u8 types with ActiveObje...Loic Blot2015-02-17
* Fix entitiy WieldMeshSceneNode not freed due to additional grabgregorycu2015-01-17
* Use std::string::empty() instead of size() where applicableAnton2014-12-12
* Implement WieldMeshSceneNode which improves wield mesh renderingKahrl2014-11-08
* Split settings into seperate source and header filesShadowNinja2014-09-21
* Fix client crash on deletion of an object the clients localplayer is attached tosapier2014-07-06
* Fix memory leaks in GenericCAO, ShaderSource and Player classessapier2014-07-02
* Make sure none of the irrlicht member objects of GenericCAO are deleted prior...sapier2014-06-29
* Fix GenericCAO fails to grabing member animated mesh node to prevent it do be...sapier2014-06-27
* Speedup attachement handling by replacing vector search by direct array acces...sapier2014-06-23
* Add support for interlaced polarized 3d screenssapier2014-05-18
* Split declaration of GenericCAO from implementationsapier2014-05-17
* Fix player:set_animation() in third person viewBlockMen2014-05-03
* Fix all warnings reported by clangSfan52014-04-15
* Use integers instead of float valuesBlockMen2014-04-12
* Add player:set_eye_offset() by @MirceaKitsune and clean upBlockMen2014-04-12
* Add third person viewBlockMen2014-04-12
* Fix rendering glitches when far from the center of the mapNovatux2014-03-04
* Create new instance of mesh every time it's required (Solves #703)Perttu Ahola2014-01-06
* Fix a bug in GenericCAO GENERIC_CMD_UPDATE_POSITION parsing.Ilya Zhuravlev2013-12-13
* Add sneak and sneak_glitch to set_physics_override()PilzAdam2013-12-03
id='n465' href='#n465'>465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
/*
Minetest
Copyright (C) 2014-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "mg_ore.h"
#include "mapgen.h"
#include "noise.h"
#include "map.h"
#include "log.h"
#include "util/numeric.h"
#include <cmath>
#include <algorithm>


FlagDesc flagdesc_ore[] = {
	{"absheight",                 OREFLAG_ABSHEIGHT}, // Non-functional
	{"puff_cliffs",               OREFLAG_PUFF_CLIFFS},
	{"puff_additive_composition", OREFLAG_PUFF_ADDITIVE},
	{NULL,                        0}
};


///////////////////////////////////////////////////////////////////////////////


OreManager::OreManager(IGameDef *gamedef) :
	ObjDefManager(gamedef, OBJDEF_ORE)
{
}


size_t OreManager::placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
{
	size_t nplaced = 0;

	for (size_t i = 0; i != m_objects.size(); i++) {
		Ore *ore = (Ore *)m_objects[i];
		if (!ore)
			continue;

		nplaced += ore->placeOre(mg, blockseed, nmin, nmax);
		blockseed++;
	}

	return nplaced;
}


void OreManager::clear()
{
	for (ObjDef *object : m_objects) {
		Ore *ore = (Ore *) object;
		delete ore;
	}
	m_objects.clear();
}


///////////////////////////////////////////////////////////////////////////////


Ore::~Ore()
{
	delete noise;
}


void Ore::resolveNodeNames()
{
	getIdFromNrBacklog(&c_ore, "", CONTENT_AIR);
	getIdsFromNrBacklog(&c_wherein);
}


size_t Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
{
	if (nmin.Y > y_max || nmax.Y < y_min)
		return 0;

	int actual_ymin = MYMAX(nmin.Y, y_min);
	int actual_ymax = MYMIN(nmax.Y, y_max);
	if (clust_size >= actual_ymax - actual_ymin + 1)
		return 0;

	nmin.Y = actual_ymin;
	nmax.Y = actual_ymax;
	generate(mg->vm, mg->seed, blockseed, nmin, nmax, mg->biomemap);

	return 1;
}


///////////////////////////////////////////////////////////////////////////////


void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
	v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
	PcgRandom pr(blockseed);
	MapNode n_ore(c_ore, 0, ore_param2);

	u32 sizex  = (nmax.X - nmin.X + 1);
	u32 volume = (nmax.X - nmin.X + 1) *
				 (nmax.Y - nmin.Y + 1) *
				 (nmax.Z - nmin.Z + 1);
	u32 csize     = clust_size;
	u32 cvolume    = csize * csize * csize;
	u32 nclusters = volume / clust_scarcity;

	for (u32 i = 0; i != nclusters; i++) {
		int x0 = pr.range(nmin.X, nmax.X - csize + 1);
		int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
		int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);

		if ((flags & OREFLAG_USE_NOISE) &&
			(NoisePerlin3D(&np, x0, y0, z0, mapseed) < nthresh))
			continue;

		if (biomemap && !biomes.empty()) {
			u32 index = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
			std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
			if (it == biomes.end())
				continue;
		}

		for (u32 z1 = 0; z1 != csize; z1++)
		for (u32 y1 = 0; y1 != csize; y1++)
		for (u32 x1 = 0; x1 != csize; x1++) {
			if (pr.range(1, cvolume) > clust_num_ores)
				continue;

			u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
			if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
				continue;

			vm->m_data[i] = n_ore;
		}
	}
}


///////////////////////////////////////////////////////////////////////////////


void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
	v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
	PcgRandom pr(blockseed + 4234);
	MapNode n_ore(c_ore, 0, ore_param2);

	u16 max_height = column_height_max;
	int y_start_min = nmin.Y + max_height;
	int y_start_max = nmax.Y - max_height;

	int y_start = y_start_min < y_start_max ?
		pr.range(y_start_min, y_start_max) :
		(y_start_min + y_start_max) / 2;

	if (!noise) {
		int sx = nmax.X - nmin.X + 1;
		int sz = nmax.Z - nmin.Z + 1;
		noise = new Noise(&np, 0, sx, sz);
	}
	noise->seed = mapseed + y_start;
	noise->perlinMap2D(nmin.X, nmin.Z);

	size_t index = 0;
	for (int z = nmin.Z; z <= nmax.Z; z++)
	for (int x = nmin.X; x <= nmax.X; x++, index++) {
		float noiseval = noise->result[index];
		if (noiseval < nthresh)
			continue;

		if (biomemap && !biomes.empty()) {
			std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
			if (it == biomes.end())
				continue;
		}

		u16 height = pr.range(column_height_min, column_height_max);
		int ymidpoint = y_start + noiseval;
		int y0 = MYMAX(nmin.Y, ymidpoint - height * (1 - column_midpoint_factor));
		int y1 = MYMIN(nmax.Y, y0 + height - 1);

		for (int y = y0; y <= y1; y++) {
			u32 i = vm->m_area.index(x, y, z);
			if (!vm->m_area.contains(i))
				continue;
			if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
				continue;

			vm->m_data[i] = n_ore;
		}
	}
}


///////////////////////////////////////////////////////////////////////////////


OrePuff::~OrePuff()
{
	delete noise_puff_top;
	delete noise_puff_bottom;
}


void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed,
	v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
	PcgRandom pr(blockseed + 4234);
	MapNode n_ore(c_ore, 0, ore_param2);

	int y_start = pr.range(nmin.Y, nmax.Y);

	if (!noise) {
		int sx = nmax.X - nmin.X + 1;
		int sz = nmax.Z - nmin.Z + 1;
		noise = new Noise(&np, 0, sx, sz);
		noise_puff_top = new Noise(&np_puff_top, 0, sx, sz);
		noise_puff_bottom = new Noise(&np_puff_bottom, 0, sx, sz);
	}

	noise->seed = mapseed + y_start;
	noise->perlinMap2D(nmin.X, nmin.Z);
	bool noise_generated = false;

	size_t index = 0;
	for (int z = nmin.Z; z <= nmax.Z; z++)
	for (int x = nmin.X; x <= nmax.X; x++, index++) {
		float noiseval = noise->result[index];
		if (noiseval < nthresh)
			continue;

		if (biomemap && !biomes.empty()) {
			std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
			if (it == biomes.end())
				continue;
		}

		if (!noise_generated) {
			noise_generated = true;
			noise_puff_top->perlinMap2D(nmin.X, nmin.Z);
			noise_puff_bottom->perlinMap2D(nmin.X, nmin.Z);
		}

		float ntop    = noise_puff_top->result[index];
		float nbottom = noise_puff_bottom->result[index];

		if (!(flags & OREFLAG_PUFF_CLIFFS)) {
			float ndiff = noiseval - nthresh;
			if (ndiff < 1.0f) {
				ntop *= ndiff;
				nbottom *= ndiff;
			}
		}

		int ymid = y_start;
		int y0 = ymid - nbottom;
		int y1 = ymid + ntop;

		if ((flags & OREFLAG_PUFF_ADDITIVE) && (y0 > y1))
			SWAP(int, y0, y1);

		for (int y = y0; y <= y1; y++) {
			u32 i = vm->m_area.index(x, y, z);
			if (!vm->m_area.contains(i))
				continue;
			if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
				continue;

			vm->m_data[i] = n_ore;
		}
	}
}


///////////////////////////////////////////////////////////////////////////////


void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
	v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
	PcgRandom pr(blockseed + 2404);
	MapNode n_ore(c_ore, 0, ore_param2);

	u32 sizex  = (nmax.X - nmin.X + 1);
	u32 volume = (nmax.X - nmin.X + 1) *
				 (nmax.Y - nmin.Y + 1) *
				 (nmax.Z - nmin.Z + 1);
	u32 csize  = clust_size;
	u32 nblobs = volume / clust_scarcity;

	if (!noise)
		noise = new Noise(&np, mapseed, csize, csize, csize);

	for (u32 i = 0; i != nblobs; i++) {
		int x0 = pr.range(nmin.X, nmax.X - csize + 1);
		int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
		int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);

		if (biomemap && !biomes.empty()) {
			u32 bmapidx = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
			std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[bmapidx]);
			if (it == biomes.end())
				continue;
		}

		bool noise_generated = false;
		noise->seed = blockseed + i;

		size_t index = 0;
		for (u32 z1 = 0; z1 != csize; z1++)
		for (u32 y1 = 0; y1 != csize; y1++)
		for (u32 x1 = 0; x1 != csize; x1++, index++) {
			u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
			if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
				continue;

			// Lazily generate noise only if there's a chance of ore being placed
			// This simple optimization makes calls 6x faster on average
			if (!noise_generated) {
				noise_generated = true;
				noise->perlinMap3D(x0, y0, z0);
			}

			float noiseval = noise->result[index];

			float xdist = (s32)x1 - (s32)csize / 2;
			float ydist = (s32)y1 - (s32)csize / 2;
			float zdist = (s32)z1 - (s32)csize / 2;

			noiseval -= std::sqrt(xdist * xdist + ydist * ydist + zdist * zdist) / csize;

			if (noiseval < nthresh)
				continue;

			vm->m_data[i] = n_ore;
		}
	}
}


///////////////////////////////////////////////////////////////////////////////


OreVein::~OreVein()
{
	delete noise2;
}


void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
	v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
	PcgRandom pr(blockseed + 520);
	MapNode n_ore(c_ore, 0, ore_param2);

	int sizex = nmax.X - nmin.X + 1;
	int sizey = nmax.Y - nmin.Y + 1;
	// Because this ore uses 3D noise the perlinmap Y size can be different in
	// different mapchunks due to ore Y limits. So recreate the noise objects
	// if Y size has changed.
	// Because these noise objects are created multiple times for this ore type
	// it is necessary to 'delete' them here.
	if (!noise || sizey != sizey_prev) {
		delete noise;
		delete noise2;
		int sizez = nmax.Z - nmin.Z + 1;
		noise  = new Noise(&np, mapseed, sizex, sizey, sizez);
		noise2 = new Noise(&np, mapseed + 436, sizex, sizey, sizez);
		sizey_prev = sizey;
	}

	bool noise_generated = false;
	size_t index = 0;
	for (int z = nmin.Z; z <= nmax.Z; z++)
	for (int y = nmin.Y; y <= nmax.Y; y++)
	for (int x = nmin.X; x <= nmax.X; x++, index++) {
		u32 i = vm->m_area.index(x, y, z);
		if (!vm->m_area.contains(i))
			continue;
		if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
			continue;

		if (biomemap && !biomes.empty()) {
			u32 bmapidx = sizex * (z - nmin.Z) + (x - nmin.X);
			std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[bmapidx]);
			if (it == biomes.end())
				continue;
		}

		// Same lazy generation optimization as in OreBlob
		if (!noise_generated) {
			noise_generated = true;
			noise->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
			noise2->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
		}

		// randval ranges from -1..1
		float randval   = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f;
		float noiseval  = contour(noise->result[index]);
		float noiseval2 = contour(noise2->result[index]);
		if (noiseval * noiseval2 + randval * random_factor < nthresh)
			continue;

		vm->m_data[i] = n_ore;
	}
}


///////////////////////////////////////////////////////////////////////////////


OreStratum::~OreStratum()
{
	delete noise_stratum_thickness;
}


void OreStratum::generate(MMVManip *vm, int mapseed, u32 blockseed,
	v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
	PcgRandom pr(blockseed + 4234);
	MapNode n_ore(c_ore, 0, ore_param2);

	if (flags & OREFLAG_USE_NOISE) {
		if (!noise) {
			int sx = nmax.X - nmin.X + 1;
			int sz = nmax.Z - nmin.Z + 1;
			noise = new Noise(&np, 0, sx, sz);
		}
		noise->perlinMap2D(nmin.X, nmin.Z);
	}

	if (flags & OREFLAG_USE_NOISE2) {
		if (!noise_stratum_thickness) {
			int sx = nmax.X - nmin.X + 1;
			int sz = nmax.Z - nmin.Z + 1;
			noise_stratum_thickness = new Noise(&np_stratum_thickness, 0, sx, sz);
		}
		noise_stratum_thickness->perlinMap2D(nmin.X, nmin.Z);
	}

	size_t index = 0;

	for (int z = nmin.Z; z <= nmax.Z; z++)
	for (int x = nmin.X; x <= nmax.X; x++, index++) {
		if (biomemap && !biomes.empty()) {
			std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
			if (it == biomes.end())
				continue;
		}

		int y0;
		int y1;

		if (flags & OREFLAG_USE_NOISE) {
			float nhalfthick = ((flags & OREFLAG_USE_NOISE2) ?
				noise_stratum_thickness->result[index] : (float)stratum_thickness) /
				2.0f;
			float nmid = noise->result[index];
			y0 = MYMAX(nmin.Y, std::ceil(nmid - nhalfthick));
			y1 = MYMIN(nmax.Y, nmid + nhalfthick);
		} else { // Simple horizontal stratum
			y0 = nmin.Y;
			y1 = nmax.Y;
		}

		for (int y = y0; y <= y1; y++) {
			if (pr.range(1, clust_scarcity) != 1)
				continue;

			u32 i = vm->m_area.index(x, y, z);
			if (!vm->m_area.contains(i))
				continue;
			if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
				continue;

			vm->m_data[i] = n_ore;
		}
	}
}